@promptbook/browser 0.112.0-64 → 0.112.0-66

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 (55) hide show
  1. package/esm/index.es.js +746 -250
  2. package/esm/index.es.js.map +1 -1
  3. package/esm/src/_packages/components.index.d.ts +0 -2
  4. package/esm/src/_packages/core.index.d.ts +2 -2
  5. package/esm/src/_packages/types.index.d.ts +0 -2
  6. package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
  7. package/esm/src/avatars/visuals/minecraft2AvatarVisual.d.ts +7 -0
  8. package/esm/src/avatars/visuals/minecraftAvatarVisualShared.d.ts +48 -0
  9. package/esm/src/book-2.0/book-language-documentation/BookLanguageDocumentationExample.d.ts +27 -0
  10. package/esm/src/book-2.0/book-language-documentation/bookLanguageCommonPitfalls.d.ts +25 -0
  11. package/esm/src/book-2.0/book-language-documentation/bookLanguageDocumentationExamples.d.ts +10 -0
  12. package/esm/src/book-2.0/book-language-documentation/createStandaloneBookLanguageMarkdown.d.ts +14 -0
  13. package/esm/src/book-2.0/book-language-documentation/renderGroupedCommitmentDocumentationMarkdown.d.ts +43 -0
  14. package/esm/src/book-components/Chat/types/ChatMessage.d.ts +0 -34
  15. package/esm/src/cli/cli-commands/coder/ensureCoderGitignoreFile.d.ts +1 -1
  16. package/esm/src/commitments/KNOWLEDGE/KNOWLEDGE.d.ts +0 -11
  17. package/esm/src/commitments/_common/toolRuntimeContext.d.ts +0 -6
  18. package/esm/src/config.d.ts +3 -3
  19. package/esm/src/formfactors/index.d.ts +2 -2
  20. package/esm/src/formfactors/matcher/MatcherFormfactorDefinition.d.ts +1 -1
  21. package/esm/src/formfactors/translator/TranslatorFormfactorDefinition.d.ts +1 -1
  22. package/esm/src/llm-providers/openai/OpenAiAgentKitExecutionTools.d.ts +0 -4
  23. package/esm/src/llm-providers/openai/OpenAiAgentKitExecutionToolsOptions.d.ts +0 -9
  24. package/esm/src/utils/files/getPromptbookTempPath.d.ts +24 -0
  25. package/esm/src/version.d.ts +1 -1
  26. package/package.json +2 -2
  27. package/umd/index.umd.js +749 -253
  28. package/umd/index.umd.js.map +1 -1
  29. package/umd/src/_packages/components.index.d.ts +0 -2
  30. package/umd/src/_packages/core.index.d.ts +2 -2
  31. package/umd/src/_packages/types.index.d.ts +0 -2
  32. package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
  33. package/umd/src/avatars/visuals/minecraft2AvatarVisual.d.ts +7 -0
  34. package/umd/src/avatars/visuals/minecraftAvatarVisualShared.d.ts +48 -0
  35. package/umd/src/book-2.0/book-language-documentation/BookLanguageDocumentationExample.d.ts +27 -0
  36. package/umd/src/book-2.0/book-language-documentation/bookLanguageCommonPitfalls.d.ts +25 -0
  37. package/umd/src/book-2.0/book-language-documentation/bookLanguageDocumentationExamples.d.ts +10 -0
  38. package/umd/src/book-2.0/book-language-documentation/createStandaloneBookLanguageMarkdown.d.ts +14 -0
  39. package/umd/src/book-2.0/book-language-documentation/renderGroupedCommitmentDocumentationMarkdown.d.ts +43 -0
  40. package/umd/src/book-2.0/book-language-documentation/renderGroupedCommitmentDocumentationMarkdown.test.d.ts +1 -0
  41. package/umd/src/book-components/Chat/types/ChatMessage.d.ts +0 -34
  42. package/umd/src/cli/cli-commands/coder/ensureCoderGitignoreFile.d.ts +1 -1
  43. package/umd/src/commitments/KNOWLEDGE/KNOWLEDGE.d.ts +0 -11
  44. package/umd/src/commitments/_common/toolRuntimeContext.d.ts +0 -6
  45. package/umd/src/config.d.ts +3 -3
  46. package/umd/src/formfactors/index.d.ts +2 -2
  47. package/umd/src/formfactors/matcher/MatcherFormfactorDefinition.d.ts +1 -1
  48. package/umd/src/formfactors/translator/TranslatorFormfactorDefinition.d.ts +1 -1
  49. package/umd/src/llm-providers/openai/OpenAiAgentKitExecutionTools.d.ts +0 -4
  50. package/umd/src/llm-providers/openai/OpenAiAgentKitExecutionToolsOptions.d.ts +0 -9
  51. package/umd/src/utils/files/getPromptbookTempPath.d.ts +24 -0
  52. package/umd/src/utils/files/getPromptbookTempPath.test.d.ts +1 -0
  53. package/umd/src/version.d.ts +1 -1
  54. /package/esm/src/{commitments/KNOWLEDGE/KNOWLEDGE.test.d.ts → book-2.0/book-language-documentation/renderGroupedCommitmentDocumentationMarkdown.test.d.ts} +0 -0
  55. /package/{umd/src/commitments/KNOWLEDGE/KNOWLEDGE.test.d.ts → esm/src/utils/files/getPromptbookTempPath.test.d.ts} +0 -0
package/esm/index.es.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { spaceTrim as spaceTrim$1 } from 'spacetrim';
2
2
  import { Registration } from 'destroyable';
3
3
  import { BehaviorSubject, Subject } from 'rxjs';
4
+ import { posix, basename, join, dirname, isAbsolute } from 'path';
4
5
  import { randomBytes } from 'crypto';
5
6
  import { SHA256 } from 'crypto-js';
6
7
  import hexEncoder from 'crypto-js/enc-hex';
7
- import { basename, join, dirname, isAbsolute } from 'path';
8
8
  import moment from 'moment';
9
9
  import { lookup, extension } from 'mime-types';
10
10
  import { forTime } from 'waitasecond';
@@ -29,7 +29,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
29
29
  * @generated
30
30
  * @see https://github.com/webgptorg/promptbook
31
31
  */
32
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-64';
32
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-66';
33
33
  /**
34
34
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
35
35
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -948,6 +948,29 @@ function saturate(amount) {
948
948
  }
949
949
  // TODO: Maybe implement by mix+hsl
950
950
 
951
+ /**
952
+ * Relative directory name without the `./` prefix for Git ignore rules and glob patterns.
953
+ *
954
+ * @private internal utility for Promptbook-owned temp files
955
+ */
956
+ const PROMPTBOOK_TEMP_DIRECTORY_NAME = '.promptbook';
957
+ /**
958
+ * Builds one project-relative path inside the shared Promptbook working directory.
959
+ *
960
+ * @private internal utility for Promptbook-owned temp files
961
+ */
962
+ function getPromptbookTempPath(...pathSegments) {
963
+ return `./${getPromptbookTempPosixPath(...pathSegments)}`;
964
+ }
965
+ /**
966
+ * Builds one POSIX path fragment inside the shared Promptbook working directory for globs and generated text files.
967
+ *
968
+ * @private internal utility for Promptbook-owned temp files
969
+ */
970
+ function getPromptbookTempPosixPath(...pathSegments) {
971
+ return posix.join(PROMPTBOOK_TEMP_DIRECTORY_NAME, ...pathSegments);
972
+ }
973
+
951
974
  /**
952
975
  * Returns the same value that is passed as argument.
953
976
  * No side effects.
@@ -1130,7 +1153,6 @@ const DEFAULT_MAX_PARALLEL_COUNT = 5; // <- TODO: [🤹‍♂️]
1130
1153
  */
1131
1154
  const DEFAULT_MAX_EXECUTION_ATTEMPTS = 7; // <- TODO: [🤹‍♂️]
1132
1155
  // <- TODO: [🕝] Make also `AGENTS_DIRNAME_ALTERNATIVES`
1133
- // TODO: Just `.promptbook` in config, hardcode subfolders like `download-cache` or `execution-cache`
1134
1156
  /**
1135
1157
  * Where to store the temporary downloads
1136
1158
  *
@@ -1138,7 +1160,15 @@ const DEFAULT_MAX_EXECUTION_ATTEMPTS = 7; // <- TODO: [🤹‍♂️]
1138
1160
  *
1139
1161
  * @public exported from `@promptbook/core`
1140
1162
  */
1141
- const DEFAULT_DOWNLOAD_CACHE_DIRNAME = './.promptbook/download-cache';
1163
+ const DEFAULT_DOWNLOAD_CACHE_DIRNAME = getPromptbookTempPath('download-cache');
1164
+ /**
1165
+ * Where to store the cache of executions for promptbook CLI
1166
+ *
1167
+ * Note: When the folder does not exist, it is created recursively
1168
+ *
1169
+ * @public exported from `@promptbook/core`
1170
+ */
1171
+ getPromptbookTempPath('execution-cache');
1142
1172
  /**
1143
1173
  * Where to store the scrape cache
1144
1174
  *
@@ -1146,7 +1176,7 @@ const DEFAULT_DOWNLOAD_CACHE_DIRNAME = './.promptbook/download-cache';
1146
1176
  *
1147
1177
  * @public exported from `@promptbook/core`
1148
1178
  */
1149
- const DEFAULT_SCRAPE_CACHE_DIRNAME = './.promptbook/scrape-cache';
1179
+ const DEFAULT_SCRAPE_CACHE_DIRNAME = getPromptbookTempPath('scrape-cache');
1150
1180
  // <- TODO: [🧜‍♂️]
1151
1181
  /**
1152
1182
  * Default settings for parsing and generating CSV files in Promptbook.
@@ -6977,18 +7007,6 @@ function parseDataUrlKnowledgeSource(source) {
6977
7007
  }
6978
7008
  // Note: [💞] Ignore a discrepancy between file name and entity name
6979
7009
 
6980
- /**
6981
- * Name of the tool used by agents to search configured `KNOWLEDGE` sources.
6982
- *
6983
- * @public exported from `@promptbook/core`
6984
- */
6985
- const KNOWLEDGE_SEARCH_TOOL_NAME = 'knowledge_search';
6986
- /**
6987
- * Title of the system-message section generated for `KNOWLEDGE` commitments.
6988
- *
6989
- * @private constant of `KnowledgeCommitmentDefinition`
6990
- */
6991
- const KNOWLEDGE_SEARCH_SYSTEM_SECTION_TITLE = 'Knowledge Search';
6992
7010
  /**
6993
7011
  * KNOWLEDGE commitment definition
6994
7012
  *
@@ -7110,17 +7128,9 @@ class KnowledgeCommitmentDefinition extends BaseCommitmentDefinition {
7110
7128
  knowledgeInfoEntries.push(`Knowledge Source Inline: ${inlineSource.filename} (derived from inline content and processed for retrieval during chat)`);
7111
7129
  }
7112
7130
  if (knowledgeInfoEntries.length === 0) {
7113
- return addKnowledgeSearchToolAndSystemSection(nextRequirements);
7131
+ return nextRequirements;
7114
7132
  }
7115
- return addKnowledgeSearchToolAndSystemSection(nextRequirements);
7116
- }
7117
- /**
7118
- * Gets human-readable titles for tool functions provided by this commitment.
7119
- */
7120
- getToolTitles() {
7121
- return {
7122
- [KNOWLEDGE_SEARCH_TOOL_NAME]: 'Knowledge search',
7123
- };
7133
+ return this.appendToSystemMessage(nextRequirements, knowledgeInfoEntries.join('\n'), '\n\n');
7124
7134
  }
7125
7135
  }
7126
7136
  /**
@@ -7134,128 +7144,6 @@ function hasMeaningfulNonUrlText(content, urls) {
7134
7144
  const significantText = contentWithoutUrls.replace(/[\s.,!?;:'"`()[\]{}<>/-]+/g, '');
7135
7145
  return significantText.length > 0;
7136
7146
  }
7137
- /**
7138
- * Adds the shared `knowledge_search` tool definition and the consolidated system-message section.
7139
- *
7140
- * @param requirements - Requirements after one `KNOWLEDGE` commitment was applied.
7141
- * @returns Requirements with the knowledge search instructions and tool definition.
7142
- *
7143
- * @private internal utility of `KnowledgeCommitmentDefinition`
7144
- */
7145
- function addKnowledgeSearchToolAndSystemSection(requirements) {
7146
- const nextRequirements = addKnowledgeSearchTool(requirements);
7147
- const section = createKnowledgeSearchSystemSection(nextRequirements);
7148
- const sectionHeader = `## ${KNOWLEDGE_SEARCH_SYSTEM_SECTION_TITLE}`;
7149
- if (nextRequirements.systemMessage.includes(sectionHeader)) {
7150
- return {
7151
- ...nextRequirements,
7152
- systemMessage: nextRequirements.systemMessage.replace(new RegExp(`## ${KNOWLEDGE_SEARCH_SYSTEM_SECTION_TITLE.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?(?=\\n\\n##|$)`), section),
7153
- };
7154
- }
7155
- return {
7156
- ...nextRequirements,
7157
- systemMessage: nextRequirements.systemMessage.trim()
7158
- ? `${nextRequirements.systemMessage}\n\n${section}`
7159
- : section,
7160
- };
7161
- }
7162
- /**
7163
- * Adds the `knowledge_search` model tool when it is not already present.
7164
- *
7165
- * @param requirements - Current model requirements.
7166
- * @returns Requirements with the tool definition available to the model.
7167
- *
7168
- * @private internal utility of `KnowledgeCommitmentDefinition`
7169
- */
7170
- function addKnowledgeSearchTool(requirements) {
7171
- const existingTools = requirements.tools || [];
7172
- if (existingTools.some((tool) => tool.name === KNOWLEDGE_SEARCH_TOOL_NAME)) {
7173
- return requirements;
7174
- }
7175
- return {
7176
- ...requirements,
7177
- tools: [
7178
- ...existingTools,
7179
- {
7180
- name: KNOWLEDGE_SEARCH_TOOL_NAME,
7181
- description: spaceTrim$1(`
7182
- Search the agent's configured knowledge sources and return relevant excerpts with citation ids.
7183
- Use this before answering questions that may depend on the agent's KNOWLEDGE commitments.
7184
- `),
7185
- parameters: {
7186
- type: 'object',
7187
- properties: {
7188
- query: {
7189
- type: 'string',
7190
- description: 'The natural-language search query for the knowledge base.',
7191
- },
7192
- limit: {
7193
- type: 'integer',
7194
- description: 'Maximum number of matching source excerpts to return.',
7195
- },
7196
- },
7197
- required: ['query'],
7198
- },
7199
- },
7200
- ],
7201
- };
7202
- }
7203
- /**
7204
- * Creates the model-facing system-message section for knowledge search.
7205
- *
7206
- * @param requirements - Current model requirements.
7207
- * @returns Markdown system-message section.
7208
- *
7209
- * @private internal utility of `KnowledgeCommitmentDefinition`
7210
- */
7211
- function createKnowledgeSearchSystemSection(requirements) {
7212
- const sourceEntries = createKnowledgeSourceSystemEntries(requirements);
7213
- const sourceList = sourceEntries.length > 0 ? sourceEntries.map((entry) => `- ${entry}`).join('\n') : '- None';
7214
- return spaceTrim$1(`
7215
- ## ${KNOWLEDGE_SEARCH_SYSTEM_SECTION_TITLE}
7216
-
7217
- - Use \`${KNOWLEDGE_SEARCH_TOOL_NAME}\` to search the configured knowledge sources before answering questions that depend on this agent's knowledge base.
7218
- - Base source-backed factual answers on the returned excerpts.
7219
- - When you use a returned excerpt, include its citation marker in the answer body, for example \`[0:0]\`.
7220
- - If the search returns no relevant information, say that the knowledge base did not contain the answer instead of inventing it.
7221
-
7222
- Configured knowledge sources:
7223
- ${sourceList}
7224
- `);
7225
- }
7226
- /**
7227
- * Builds a stable list of configured knowledge sources for system-message diagnostics.
7228
- *
7229
- * @param requirements - Current model requirements.
7230
- * @returns Human-readable source entries.
7231
- *
7232
- * @private internal utility of `KnowledgeCommitmentDefinition`
7233
- */
7234
- function createKnowledgeSourceSystemEntries(requirements) {
7235
- var _a;
7236
- const entries = [];
7237
- const seenEntries = new Set();
7238
- for (const source of requirements.knowledgeSources || []) {
7239
- const entry = `Source URL: ${source} (processed for retrieval during chat)`;
7240
- if (seenEntries.has(entry)) {
7241
- continue;
7242
- }
7243
- seenEntries.add(entry);
7244
- entries.push(entry);
7245
- }
7246
- const inlineSources = (((_a = requirements._metadata) === null || _a === void 0 ? void 0 : _a.inlineKnowledgeSources) || [])
7247
- .map((source) => source.filename)
7248
- .filter(Boolean);
7249
- for (const filename of inlineSources) {
7250
- const entry = `Knowledge Source Inline: ${filename} (Inline source: processed for retrieval during chat)`;
7251
- if (seenEntries.has(entry)) {
7252
- continue;
7253
- }
7254
- seenEntries.add(entry);
7255
- entries.push(entry);
7256
- }
7257
- return entries;
7258
- }
7259
7147
 
7260
7148
  /**
7261
7149
  * LANGUAGE commitment definition
@@ -9907,6 +9795,177 @@ function drawFractalCore(context, size, palette, timeMs, corePhase) {
9907
9795
  context.restore();
9908
9796
  }
9909
9797
 
9798
+ /* eslint-disable no-magic-numbers */
9799
+ /**
9800
+ * Builds the seeded six-face texture pack used by the Minecraft-style head cuboid.
9801
+ *
9802
+ * @param random Seeded random generator.
9803
+ * @param palette Derived avatar palette.
9804
+ * @param hasHeadband Whether the generated avatar should include a colored headband.
9805
+ * @returns Head cuboid textures.
9806
+ *
9807
+ * @private helper of the Minecraft avatar visuals
9808
+ */
9809
+ function createMinecraftHeadTextures(random, palette, hasHeadband) {
9810
+ const faceTexture = createMinecraftFaceTexture(random, palette, hasHeadband);
9811
+ const hairColor = random() < 0.5 ? palette.primary : palette.secondary;
9812
+ const skinColor = palette.highlight;
9813
+ const headbandColor = hasHeadband ? palette.accent : hairColor;
9814
+ const sideTexture = createFilledTexture(skinColor);
9815
+ const backTexture = createFilledTexture(skinColor);
9816
+ const topTexture = createFilledTexture(hairColor);
9817
+ const bottomTexture = createFilledTexture(`${palette.shadow}cc`);
9818
+ fillTextureRect(sideTexture, 0, 0, 8, 3, hairColor);
9819
+ fillTextureRect(backTexture, 0, 0, 8, 5, hairColor);
9820
+ fillTextureRect(backTexture, 1, 5, 6, 1, hairColor);
9821
+ if (hasHeadband) {
9822
+ fillTextureRect(sideTexture, 0, 2, 8, 1, headbandColor);
9823
+ fillTextureRect(backTexture, 0, 2, 8, 1, headbandColor);
9824
+ fillTextureRect(topTexture, 0, 4, 8, 1, headbandColor);
9825
+ }
9826
+ sideTexture[4][4] = `${palette.shadow}99`;
9827
+ sideTexture[5][4] = `${palette.shadow}cc`;
9828
+ backTexture[6][2] = `${palette.shadow}99`;
9829
+ backTexture[6][5] = `${palette.shadow}99`;
9830
+ return {
9831
+ front: faceTexture,
9832
+ back: backTexture,
9833
+ left: sideTexture,
9834
+ right: mirrorMinecraftTexture(sideTexture),
9835
+ top: topTexture,
9836
+ bottom: bottomTexture,
9837
+ };
9838
+ }
9839
+ /**
9840
+ * Builds the seeded six-face texture pack used by the Minecraft-style torso cuboid.
9841
+ *
9842
+ * @param random Seeded random generator.
9843
+ * @param palette Derived avatar palette.
9844
+ * @returns Torso cuboid textures.
9845
+ *
9846
+ * @private helper of the Minecraft avatar visuals
9847
+ */
9848
+ function createMinecraftTorsoTextures(random, palette) {
9849
+ const frontTexture = createMinecraftShirtTexture(random, palette);
9850
+ const sideTexture = createFilledTexture(palette.primary);
9851
+ const backTexture = createFilledTexture(palette.primary);
9852
+ const topTexture = createFilledTexture(`${palette.highlight}dd`);
9853
+ const bottomTexture = createFilledTexture(`${palette.shadow}dd`);
9854
+ const stripeColor = random() < 0.5 ? palette.secondary : palette.highlight;
9855
+ fillTextureRect(sideTexture, 0, 0, 8, 2, palette.shadow);
9856
+ fillTextureRect(backTexture, 0, 0, 8, 2, palette.shadow);
9857
+ fillTextureRect(backTexture, 3, 2, 2, 6, stripeColor);
9858
+ fillTextureRect(sideTexture, 4, 2, 1, 6, stripeColor);
9859
+ fillTextureRect(topTexture, 0, 0, 8, 2, palette.shadow);
9860
+ fillTextureRect(topTexture, 2, 2, 4, 4, stripeColor);
9861
+ return {
9862
+ front: frontTexture,
9863
+ back: backTexture,
9864
+ left: sideTexture,
9865
+ right: mirrorMinecraftTexture(sideTexture),
9866
+ top: topTexture,
9867
+ bottom: bottomTexture,
9868
+ };
9869
+ }
9870
+ /**
9871
+ * Mirrors one Minecraft texture horizontally.
9872
+ *
9873
+ * @param texture Source texture.
9874
+ * @returns Mirrored texture copy.
9875
+ *
9876
+ * @private helper of the Minecraft avatar visuals
9877
+ */
9878
+ function mirrorMinecraftTexture(texture) {
9879
+ return texture.map((row) => [...row].reverse());
9880
+ }
9881
+ /**
9882
+ * Creates the front-face pixel texture for the cube head.
9883
+ *
9884
+ * @param random Seeded random generator.
9885
+ * @param palette Derived avatar palette.
9886
+ * @param hasHeadband Whether the avatar should render a headband row.
9887
+ * @returns 8x8 pixel texture.
9888
+ *
9889
+ * @private helper of the Minecraft avatar visuals
9890
+ */
9891
+ function createMinecraftFaceTexture(random, palette, hasHeadband) {
9892
+ const texture = createFilledTexture(palette.highlight);
9893
+ const hairlineColor = random() < 0.5 ? palette.primary : palette.secondary;
9894
+ const cheekColor = random() < 0.5 ? `${palette.accent}bb` : `${palette.secondary}bb`;
9895
+ fillTextureRect(texture, 0, 0, 8, 2, hairlineColor);
9896
+ texture[2][0] = hairlineColor;
9897
+ texture[2][7] = hairlineColor;
9898
+ texture[3][0] = hairlineColor;
9899
+ texture[3][7] = hairlineColor;
9900
+ if (hasHeadband) {
9901
+ fillTextureRect(texture, 0, 2, 8, 1, palette.accent);
9902
+ }
9903
+ texture[3][2] = palette.ink;
9904
+ texture[3][5] = palette.ink;
9905
+ texture[4][2] = '#ffffff';
9906
+ texture[4][5] = '#ffffff';
9907
+ texture[5][1] = cheekColor;
9908
+ texture[5][6] = cheekColor;
9909
+ texture[5][3] = palette.shadow;
9910
+ texture[5][4] = palette.shadow;
9911
+ texture[6][3] = palette.shadow;
9912
+ texture[6][4] = palette.shadow;
9913
+ return texture;
9914
+ }
9915
+ /**
9916
+ * Creates the front-face pixel texture for the torso.
9917
+ *
9918
+ * @param random Seeded random generator.
9919
+ * @param palette Derived avatar palette.
9920
+ * @returns 8x8 torso texture.
9921
+ *
9922
+ * @private helper of the Minecraft avatar visuals
9923
+ */
9924
+ function createMinecraftShirtTexture(random, palette) {
9925
+ const texture = createFilledTexture(palette.primary);
9926
+ const stripeColor = random() < 0.5 ? palette.secondary : palette.highlight;
9927
+ fillTextureRect(texture, 0, 0, 8, 2, palette.shadow);
9928
+ for (let rowIndex = 2; rowIndex < 8; rowIndex++) {
9929
+ texture[rowIndex][3] = stripeColor;
9930
+ texture[rowIndex][4] = stripeColor;
9931
+ }
9932
+ texture[4][1] = palette.accent;
9933
+ texture[4][6] = palette.accent;
9934
+ texture[5][2] = palette.highlight;
9935
+ texture[5][5] = palette.highlight;
9936
+ return texture;
9937
+ }
9938
+ /**
9939
+ * Creates one solid-color 8x8 Minecraft texture.
9940
+ *
9941
+ * @param color Fill color.
9942
+ * @returns Filled 8x8 texture.
9943
+ *
9944
+ * @private helper of the Minecraft avatar visuals
9945
+ */
9946
+ function createFilledTexture(color) {
9947
+ return Array.from({ length: 8 }, () => Array.from({ length: 8 }, () => color));
9948
+ }
9949
+ /**
9950
+ * Fills one rectangular area inside a mutable Minecraft texture.
9951
+ *
9952
+ * @param texture Mutable target texture.
9953
+ * @param x Left texture coordinate.
9954
+ * @param y Top texture coordinate.
9955
+ * @param width Rectangle width.
9956
+ * @param height Rectangle height.
9957
+ * @param color Fill color.
9958
+ *
9959
+ * @private helper of the Minecraft avatar visuals
9960
+ */
9961
+ function fillTextureRect(texture, x, y, width, height, color) {
9962
+ for (let rowIndex = y; rowIndex < y + height; rowIndex++) {
9963
+ for (let columnIndex = x; columnIndex < x + width; columnIndex++) {
9964
+ texture[rowIndex][columnIndex] = color;
9965
+ }
9966
+ }
9967
+ }
9968
+
9910
9969
  /* eslint-disable no-magic-numbers */
9911
9970
  /**
9912
9971
  * Minecraft-style 3D avatar visual.
@@ -9931,8 +9990,8 @@ const minecraftAvatarVisual = {
9931
9990
  const bodyX = size * 0.33;
9932
9991
  const bodyY = headY + headSize * 0.96;
9933
9992
  const hasHeadband = random() < 0.5;
9934
- const faceTexture = createMinecraftFaceTexture(createRandom('minecraft-face'), palette, hasHeadband);
9935
- const shirtTexture = createMinecraftShirtTexture(createRandom('minecraft-shirt'), palette);
9993
+ const headTextures = createMinecraftHeadTextures(createRandom('minecraft-face'), palette, hasHeadband);
9994
+ const torsoTextures = createMinecraftTorsoTextures(createRandom('minecraft-shirt'), palette);
9936
9995
  drawAvatarFrame(context, size, palette);
9937
9996
  const spotlight = context.createRadialGradient(size * 0.5, size * 0.18, size * 0.05, size * 0.5, size * 0.18, size * 0.5);
9938
9997
  spotlight.addColorStop(0, `${palette.highlight}66`);
@@ -9952,7 +10011,7 @@ const minecraftAvatarVisual = {
9952
10011
  width: bodyWidth,
9953
10012
  height: bodyHeight,
9954
10013
  depth: bodyDepth,
9955
- frontTexture: shirtTexture,
10014
+ frontTexture: torsoTextures.front,
9956
10015
  topColor: `${palette.highlight}cc`,
9957
10016
  sideColor: `${palette.secondary}dd`,
9958
10017
  outlineColor: `${palette.shadow}aa`,
@@ -9963,7 +10022,7 @@ const minecraftAvatarVisual = {
9963
10022
  width: headSize,
9964
10023
  height: headSize,
9965
10024
  depth,
9966
- frontTexture: faceTexture,
10025
+ frontTexture: headTextures.front,
9967
10026
  topColor: `${palette.highlight}ee`,
9968
10027
  sideColor: `${palette.secondary}ee`,
9969
10028
  outlineColor: `${palette.shadow}cc`,
@@ -10028,72 +10087,505 @@ function drawVoxelCuboid(context, cuboid) {
10028
10087
  context.closePath();
10029
10088
  context.stroke();
10030
10089
  }
10090
+
10091
+ /* eslint-disable no-magic-numbers */
10031
10092
  /**
10032
- * Creates the front-face pixel texture for the cube head.
10093
+ * Fixed scene camera distance used for the proper-3D projection.
10033
10094
  *
10034
- * @param random Seeded random generator.
10095
+ * @private helper of `minecraft2AvatarVisual`
10096
+ */
10097
+ const CAMERA_DISTANCE_RATIO = 1.4;
10098
+ /**
10099
+ * Shared light direction used to shade projected cuboid faces.
10100
+ *
10101
+ * @private helper of `minecraft2AvatarVisual`
10102
+ */
10103
+ const LIGHT_DIRECTION = normalizeVector3({
10104
+ x: 0.4,
10105
+ y: -0.65,
10106
+ z: 0.92,
10107
+ });
10108
+ /**
10109
+ * Minecraft 3D 2 avatar visual.
10110
+ *
10111
+ * @private built-in avatar visual
10112
+ */
10113
+ const minecraft2AvatarVisual = {
10114
+ id: 'minecraft2',
10115
+ title: 'Minecraft 3D 2',
10116
+ description: 'Proper 3D Minecraft-style portrait with textured cuboids and pointer-driven head turns.',
10117
+ isAnimated: true,
10118
+ supportsPointerTracking: true,
10119
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
10120
+ const spotlightY = size * 0.22;
10121
+ const headRandom = createRandom('minecraft2-head');
10122
+ const hasHeadband = headRandom() < 0.5;
10123
+ const headTextures = createMinecraftHeadTextures(createRandom('minecraft2-head-textures'), palette, hasHeadband);
10124
+ const torsoTextures = createMinecraftTorsoTextures(createRandom('minecraft2-body-textures'), palette);
10125
+ const bob = Math.sin(timeMs / 880) * size * 0.014;
10126
+ const bodyYaw = -0.24 + Math.sin(timeMs / 2300) * 0.06 + interaction.bodyOffsetX * 0.16;
10127
+ const bodyPitch = -0.12 + Math.cos(timeMs / 2800) * 0.02 - interaction.bodyOffsetY * 0.06;
10128
+ const headYaw = -0.18 + Math.sin(timeMs / 1900 + 0.6) * 0.05 + interaction.gazeX * 0.62;
10129
+ const headPitch = -0.12 + Math.cos(timeMs / 2400 + 1.1) * 0.03 - interaction.gazeY * 0.38;
10130
+ const sceneCenterX = size * 0.5;
10131
+ const sceneCenterY = size * 0.57;
10132
+ const bodyWidth = size * 0.225;
10133
+ const bodyHeight = size * 0.245;
10134
+ const bodyDepth = size * 0.145;
10135
+ const headSize = size * 0.24;
10136
+ const headLift = size * 0.205;
10137
+ const headForwardShift = interaction.intensity * size * 0.018;
10138
+ const sceneCuboids = [
10139
+ {
10140
+ center: {
10141
+ x: interaction.bodyOffsetX * size * 0.026,
10142
+ y: size * 0.05 + interaction.bodyOffsetY * size * 0.018 + bob,
10143
+ z: 0,
10144
+ },
10145
+ width: bodyWidth,
10146
+ height: bodyHeight,
10147
+ depth: bodyDepth,
10148
+ rotationX: bodyPitch,
10149
+ rotationY: bodyYaw,
10150
+ textures: torsoTextures,
10151
+ outlineColor: `${palette.shadow}cc`,
10152
+ },
10153
+ {
10154
+ center: {
10155
+ x: interaction.bodyOffsetX * size * 0.018 + interaction.gazeX * size * 0.016,
10156
+ y: -headLift + bob * 1.15,
10157
+ z: headForwardShift,
10158
+ },
10159
+ width: headSize,
10160
+ height: headSize,
10161
+ depth: headSize,
10162
+ rotationX: headPitch,
10163
+ rotationY: headYaw,
10164
+ textures: headTextures,
10165
+ outlineColor: `${palette.shadow}dd`,
10166
+ },
10167
+ ];
10168
+ const visibleFaces = sceneCuboids
10169
+ .flatMap((cuboid) => resolveVisibleCuboidFaces(cuboid, size, sceneCenterX, sceneCenterY))
10170
+ .sort((firstFace, secondFace) => firstFace.averageDepth - secondFace.averageDepth);
10171
+ drawAvatarFrame(context, size, palette);
10172
+ drawMinecraftBackdrop(context, size, palette, sceneCenterX, spotlightY, interaction, timeMs);
10173
+ drawMinecraftShadow(context, size, palette, interaction, timeMs);
10174
+ for (const visibleFace of visibleFaces) {
10175
+ drawTexturedProjectedFace(context, visibleFace);
10176
+ }
10177
+ },
10178
+ };
10179
+ /**
10180
+ * Draws the shared background atmosphere behind the Minecraft 3D 2 portrait.
10181
+ *
10182
+ * @param context Canvas 2D context.
10183
+ * @param size Canvas size in CSS pixels.
10035
10184
  * @param palette Derived avatar palette.
10036
- * @param hasHeadband Whether the avatar should render a headband row.
10037
- * @returns 8x8 pixel texture.
10185
+ * @param sceneCenterX Horizontal scene center.
10186
+ * @param spotlightY Vertical spotlight anchor.
10187
+ * @param interaction Smoothed pointer-aware interaction state.
10188
+ * @param timeMs Current animation time in milliseconds.
10038
10189
  *
10039
- * @private helper of `minecraftAvatarVisual`
10190
+ * @private helper of `minecraft2AvatarVisual`
10040
10191
  */
10041
- function createMinecraftFaceTexture(random, palette, hasHeadband) {
10042
- const texture = Array.from({ length: 8 }, () => Array.from({ length: 8 }, () => palette.highlight));
10043
- const hairlineColor = random() < 0.5 ? palette.primary : palette.secondary;
10044
- const cheekColor = random() < 0.5 ? `${palette.accent}bb` : `${palette.secondary}bb`;
10045
- for (let rowIndex = 0; rowIndex < 2; rowIndex++) {
10046
- for (let columnIndex = 0; columnIndex < 8; columnIndex++) {
10047
- texture[rowIndex][columnIndex] = hairlineColor;
10192
+ function drawMinecraftBackdrop(context, size, palette, sceneCenterX, spotlightY, interaction, timeMs) {
10193
+ const spotlightGradient = context.createRadialGradient(sceneCenterX + interaction.gazeX * size * 0.08, spotlightY + interaction.gazeY * size * 0.05, size * 0.03, sceneCenterX, spotlightY, size * 0.52);
10194
+ spotlightGradient.addColorStop(0, `${palette.highlight}66`);
10195
+ spotlightGradient.addColorStop(0.42, `${palette.accent}1d`);
10196
+ spotlightGradient.addColorStop(1, `${palette.highlight}00`);
10197
+ context.fillStyle = spotlightGradient;
10198
+ context.fillRect(0, 0, size, size);
10199
+ const rimGradient = context.createLinearGradient(0, size * 0.14, 0, size * 0.92);
10200
+ rimGradient.addColorStop(0, `${palette.highlight}12`);
10201
+ rimGradient.addColorStop(0.55, `${palette.secondary}0a`);
10202
+ rimGradient.addColorStop(1, `${palette.shadow}00`);
10203
+ context.fillStyle = rimGradient;
10204
+ context.fillRect(0, 0, size, size);
10205
+ context.save();
10206
+ context.globalAlpha = 0.08 + interaction.intensity * 0.04;
10207
+ context.fillStyle = palette.highlight;
10208
+ context.beginPath();
10209
+ context.arc(size * 0.72 + Math.sin(timeMs / 1600) * size * 0.03, size * 0.2 + Math.cos(timeMs / 1400) * size * 0.018, size * 0.025, 0, Math.PI * 2);
10210
+ context.fill();
10211
+ context.restore();
10212
+ }
10213
+ /**
10214
+ * Draws the soft floor shadow used to anchor the cuboids in the frame.
10215
+ *
10216
+ * @param context Canvas 2D context.
10217
+ * @param size Canvas size in CSS pixels.
10218
+ * @param palette Derived avatar palette.
10219
+ * @param interaction Smoothed pointer-aware interaction state.
10220
+ * @param timeMs Current animation time in milliseconds.
10221
+ *
10222
+ * @private helper of `minecraft2AvatarVisual`
10223
+ */
10224
+ function drawMinecraftShadow(context, size, palette, interaction, timeMs) {
10225
+ context.save();
10226
+ context.fillStyle = `${palette.shadow}66`;
10227
+ context.filter = `blur(${size * 0.02}px)`;
10228
+ context.beginPath();
10229
+ 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);
10230
+ context.fill();
10231
+ context.restore();
10232
+ }
10233
+ /**
10234
+ * Resolves all visible projected faces for one scene cuboid.
10235
+ *
10236
+ * @param cuboid Scene cuboid definition.
10237
+ * @param size Canvas size in CSS pixels.
10238
+ * @param sceneCenterX Horizontal scene center.
10239
+ * @param sceneCenterY Vertical scene center.
10240
+ * @returns Visible faces sorted later by depth.
10241
+ *
10242
+ * @private helper of `minecraft2AvatarVisual`
10243
+ */
10244
+ function resolveVisibleCuboidFaces(cuboid, size, sceneCenterX, sceneCenterY) {
10245
+ const halfWidth = cuboid.width / 2;
10246
+ const halfHeight = cuboid.height / 2;
10247
+ const halfDepth = cuboid.depth / 2;
10248
+ const faceDefinitions = [
10249
+ {
10250
+ texture: cuboid.textures.front,
10251
+ corners: [
10252
+ { x: -halfWidth, y: -halfHeight, z: halfDepth },
10253
+ { x: halfWidth, y: -halfHeight, z: halfDepth },
10254
+ { x: halfWidth, y: halfHeight, z: halfDepth },
10255
+ { x: -halfWidth, y: halfHeight, z: halfDepth },
10256
+ ],
10257
+ },
10258
+ {
10259
+ texture: cuboid.textures.back,
10260
+ corners: [
10261
+ { x: halfWidth, y: -halfHeight, z: -halfDepth },
10262
+ { x: -halfWidth, y: -halfHeight, z: -halfDepth },
10263
+ { x: -halfWidth, y: halfHeight, z: -halfDepth },
10264
+ { x: halfWidth, y: halfHeight, z: -halfDepth },
10265
+ ],
10266
+ },
10267
+ {
10268
+ texture: cuboid.textures.right,
10269
+ corners: [
10270
+ { x: halfWidth, y: -halfHeight, z: halfDepth },
10271
+ { x: halfWidth, y: -halfHeight, z: -halfDepth },
10272
+ { x: halfWidth, y: halfHeight, z: -halfDepth },
10273
+ { x: halfWidth, y: halfHeight, z: halfDepth },
10274
+ ],
10275
+ },
10276
+ {
10277
+ texture: cuboid.textures.left,
10278
+ corners: [
10279
+ { x: -halfWidth, y: -halfHeight, z: -halfDepth },
10280
+ { x: -halfWidth, y: -halfHeight, z: halfDepth },
10281
+ { x: -halfWidth, y: halfHeight, z: halfDepth },
10282
+ { x: -halfWidth, y: halfHeight, z: -halfDepth },
10283
+ ],
10284
+ },
10285
+ {
10286
+ texture: cuboid.textures.top,
10287
+ corners: [
10288
+ { x: -halfWidth, y: -halfHeight, z: -halfDepth },
10289
+ { x: halfWidth, y: -halfHeight, z: -halfDepth },
10290
+ { x: halfWidth, y: -halfHeight, z: halfDepth },
10291
+ { x: -halfWidth, y: -halfHeight, z: halfDepth },
10292
+ ],
10293
+ },
10294
+ {
10295
+ texture: cuboid.textures.bottom,
10296
+ corners: [
10297
+ { x: -halfWidth, y: halfHeight, z: halfDepth },
10298
+ { x: halfWidth, y: halfHeight, z: halfDepth },
10299
+ { x: halfWidth, y: halfHeight, z: -halfDepth },
10300
+ { x: -halfWidth, y: halfHeight, z: -halfDepth },
10301
+ ],
10302
+ },
10303
+ ];
10304
+ const visibleFaces = faceDefinitions
10305
+ .map((faceDefinition) => {
10306
+ const transformedCorners = faceDefinition.corners.map((corner) => transformScenePoint(corner, cuboid.center, cuboid.rotationX, cuboid.rotationY));
10307
+ const faceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
10308
+ if (faceNormal.z <= 0.02) {
10309
+ return null;
10048
10310
  }
10311
+ const projectedCorners = transformedCorners.map((corner) => projectScenePoint(corner, size, sceneCenterX, sceneCenterY));
10312
+ return {
10313
+ corners: projectedCorners,
10314
+ texture: faceDefinition.texture,
10315
+ averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
10316
+ lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION), -1, 1),
10317
+ outlineColor: cuboid.outlineColor,
10318
+ };
10319
+ });
10320
+ return visibleFaces.filter((visibleFace) => visibleFace !== null);
10321
+ }
10322
+ /**
10323
+ * Draws one projected textured face by tessellating its texture cells into quads.
10324
+ *
10325
+ * @param context Canvas 2D context.
10326
+ * @param face Visible projected face.
10327
+ *
10328
+ * @private helper of `minecraft2AvatarVisual`
10329
+ */
10330
+ function drawTexturedProjectedFace(context, face) {
10331
+ var _a;
10332
+ const rows = face.texture.length;
10333
+ const columns = ((_a = face.texture[0]) === null || _a === void 0 ? void 0 : _a.length) || 0;
10334
+ if (rows === 0 || columns === 0) {
10335
+ return;
10049
10336
  }
10050
- texture[2][0] = hairlineColor;
10051
- texture[2][7] = hairlineColor;
10052
- texture[3][0] = hairlineColor;
10053
- texture[3][7] = hairlineColor;
10054
- if (hasHeadband) {
10055
- for (let columnIndex = 0; columnIndex < 8; columnIndex++) {
10056
- texture[2][columnIndex] = palette.accent;
10337
+ for (let rowIndex = 0; rowIndex < rows; rowIndex++) {
10338
+ for (let columnIndex = 0; columnIndex < columns; columnIndex++) {
10339
+ const startX = columnIndex / columns;
10340
+ const endX = (columnIndex + 1) / columns;
10341
+ const startY = rowIndex / rows;
10342
+ const endY = (rowIndex + 1) / rows;
10343
+ drawProjectedQuad(context, [
10344
+ interpolateProjectedQuad(face.corners, startX, startY),
10345
+ interpolateProjectedQuad(face.corners, endX, startY),
10346
+ interpolateProjectedQuad(face.corners, endX, endY),
10347
+ interpolateProjectedQuad(face.corners, startX, endY),
10348
+ ], face.texture[rowIndex][columnIndex]);
10057
10349
  }
10058
10350
  }
10059
- texture[3][2] = palette.ink;
10060
- texture[3][5] = palette.ink;
10061
- texture[4][2] = '#ffffff';
10062
- texture[4][5] = '#ffffff';
10063
- texture[5][1] = cheekColor;
10064
- texture[5][6] = cheekColor;
10065
- texture[5][3] = palette.shadow;
10066
- texture[5][4] = palette.shadow;
10067
- texture[6][3] = palette.shadow;
10068
- texture[6][4] = palette.shadow;
10069
- return texture;
10351
+ if (face.lightIntensity > 0) {
10352
+ drawProjectedQuad(context, face.corners, `rgba(255, 255, 255, ${0.15 * face.lightIntensity})`);
10353
+ }
10354
+ else if (face.lightIntensity < 0) {
10355
+ drawProjectedQuad(context, face.corners, `rgba(0, 0, 0, ${0.22 * Math.abs(face.lightIntensity)})`);
10356
+ }
10357
+ context.save();
10358
+ context.beginPath();
10359
+ context.moveTo(face.corners[0].x, face.corners[0].y);
10360
+ for (let cornerIndex = 1; cornerIndex < face.corners.length; cornerIndex++) {
10361
+ context.lineTo(face.corners[cornerIndex].x, face.corners[cornerIndex].y);
10362
+ }
10363
+ context.closePath();
10364
+ context.strokeStyle = face.outlineColor;
10365
+ context.lineWidth = Math.max(1.1, getProjectedQuadPerimeter(face.corners) * 0.0045);
10366
+ context.lineJoin = 'round';
10367
+ context.stroke();
10368
+ context.restore();
10070
10369
  }
10071
10370
  /**
10072
- * Creates the front-face pixel texture for the torso.
10371
+ * Draws one filled projected quad.
10073
10372
  *
10074
- * @param random Seeded random generator.
10075
- * @param palette Derived avatar palette.
10076
- * @returns 8x8 torso texture.
10373
+ * @param context Canvas 2D context.
10374
+ * @param corners Quad corners in clockwise order.
10375
+ * @param fillStyle CSS fill style.
10077
10376
  *
10078
- * @private helper of `minecraftAvatarVisual`
10377
+ * @private helper of `minecraft2AvatarVisual`
10079
10378
  */
10080
- function createMinecraftShirtTexture(random, palette) {
10081
- const texture = Array.from({ length: 8 }, () => Array.from({ length: 8 }, () => palette.primary));
10082
- const stripeColor = random() < 0.5 ? palette.secondary : palette.highlight;
10083
- for (let rowIndex = 0; rowIndex < 2; rowIndex++) {
10084
- for (let columnIndex = 0; columnIndex < 8; columnIndex++) {
10085
- texture[rowIndex][columnIndex] = palette.shadow;
10086
- }
10379
+ function drawProjectedQuad(context, corners, fillStyle) {
10380
+ context.beginPath();
10381
+ context.moveTo(corners[0].x, corners[0].y);
10382
+ context.lineTo(corners[1].x, corners[1].y);
10383
+ context.lineTo(corners[2].x, corners[2].y);
10384
+ context.lineTo(corners[3].x, corners[3].y);
10385
+ context.closePath();
10386
+ context.fillStyle = fillStyle;
10387
+ context.fill();
10388
+ }
10389
+ /**
10390
+ * Interpolates one point inside a projected quad across both quad axes.
10391
+ *
10392
+ * @param corners Quad corners in clockwise order.
10393
+ * @param horizontalRatio Horizontal ratio in the range `[0, 1]`.
10394
+ * @param verticalRatio Vertical ratio in the range `[0, 1]`.
10395
+ * @returns Interpolated projected point.
10396
+ *
10397
+ * @private helper of `minecraft2AvatarVisual`
10398
+ */
10399
+ function interpolateProjectedQuad(corners, horizontalRatio, verticalRatio) {
10400
+ const topPoint = interpolateProjectedPoint(corners[0], corners[1], horizontalRatio);
10401
+ const bottomPoint = interpolateProjectedPoint(corners[3], corners[2], horizontalRatio);
10402
+ return interpolateProjectedPoint(topPoint, bottomPoint, verticalRatio);
10403
+ }
10404
+ /**
10405
+ * Interpolates between two projected points.
10406
+ *
10407
+ * @param startPoint Start point.
10408
+ * @param endPoint End point.
10409
+ * @param ratio Interpolation ratio in the range `[0, 1]`.
10410
+ * @returns Interpolated projected point.
10411
+ *
10412
+ * @private helper of `minecraft2AvatarVisual`
10413
+ */
10414
+ function interpolateProjectedPoint(startPoint, endPoint, ratio) {
10415
+ return {
10416
+ x: startPoint.x + (endPoint.x - startPoint.x) * ratio,
10417
+ y: startPoint.y + (endPoint.y - startPoint.y) * ratio,
10418
+ z: startPoint.z + (endPoint.z - startPoint.z) * ratio,
10419
+ };
10420
+ }
10421
+ /**
10422
+ * Projects one rotated scene point into canvas coordinates.
10423
+ *
10424
+ * @param point Scene point.
10425
+ * @param size Canvas size in CSS pixels.
10426
+ * @param sceneCenterX Horizontal scene center.
10427
+ * @param sceneCenterY Vertical scene center.
10428
+ * @returns Projected point.
10429
+ *
10430
+ * @private helper of `minecraft2AvatarVisual`
10431
+ */
10432
+ function projectScenePoint(point, size, sceneCenterX, sceneCenterY) {
10433
+ const cameraDistance = size * CAMERA_DISTANCE_RATIO;
10434
+ const perspectiveScale = cameraDistance / Math.max(cameraDistance - point.z, cameraDistance * 0.35);
10435
+ return {
10436
+ x: sceneCenterX + point.x * perspectiveScale,
10437
+ y: sceneCenterY + point.y * perspectiveScale,
10438
+ z: point.z,
10439
+ };
10440
+ }
10441
+ /**
10442
+ * Applies the local cuboid rotations and translation to one scene point.
10443
+ *
10444
+ * @param localPoint Point in cuboid-local space.
10445
+ * @param center Cuboid center in scene space.
10446
+ * @param rotationX Cuboid pitch in radians.
10447
+ * @param rotationY Cuboid yaw in radians.
10448
+ * @returns Transformed scene-space point.
10449
+ *
10450
+ * @private helper of `minecraft2AvatarVisual`
10451
+ */
10452
+ function transformScenePoint(localPoint, center, rotationX, rotationY) {
10453
+ const yawedPoint = rotatePointAroundY(localPoint, rotationY);
10454
+ const pitchedPoint = rotatePointAroundX(yawedPoint, rotationX);
10455
+ return {
10456
+ x: center.x + pitchedPoint.x,
10457
+ y: center.y + pitchedPoint.y,
10458
+ z: center.z + pitchedPoint.z,
10459
+ };
10460
+ }
10461
+ /**
10462
+ * Rotates one point around the local Y axis.
10463
+ *
10464
+ * @param point Source point.
10465
+ * @param angle Rotation angle in radians.
10466
+ * @returns Rotated point.
10467
+ *
10468
+ * @private helper of `minecraft2AvatarVisual`
10469
+ */
10470
+ function rotatePointAroundY(point, angle) {
10471
+ const cosine = Math.cos(angle);
10472
+ const sine = Math.sin(angle);
10473
+ return {
10474
+ x: point.x * cosine + point.z * sine,
10475
+ y: point.y,
10476
+ z: -point.x * sine + point.z * cosine,
10477
+ };
10478
+ }
10479
+ /**
10480
+ * Rotates one point around the local X axis.
10481
+ *
10482
+ * @param point Source point.
10483
+ * @param angle Rotation angle in radians.
10484
+ * @returns Rotated point.
10485
+ *
10486
+ * @private helper of `minecraft2AvatarVisual`
10487
+ */
10488
+ function rotatePointAroundX(point, angle) {
10489
+ const cosine = Math.cos(angle);
10490
+ const sine = Math.sin(angle);
10491
+ return {
10492
+ x: point.x,
10493
+ y: point.y * cosine - point.z * sine,
10494
+ z: point.y * sine + point.z * cosine,
10495
+ };
10496
+ }
10497
+ /**
10498
+ * Subtracts one 3D point from another.
10499
+ *
10500
+ * @param leftPoint Left point.
10501
+ * @param rightPoint Right point.
10502
+ * @returns Difference vector.
10503
+ *
10504
+ * @private helper of `minecraft2AvatarVisual`
10505
+ */
10506
+ function subtractPoint3D(leftPoint, rightPoint) {
10507
+ return {
10508
+ x: leftPoint.x - rightPoint.x,
10509
+ y: leftPoint.y - rightPoint.y,
10510
+ z: leftPoint.z - rightPoint.z,
10511
+ };
10512
+ }
10513
+ /**
10514
+ * Computes the 3D cross product of two vectors.
10515
+ *
10516
+ * @param leftVector Left vector.
10517
+ * @param rightVector Right vector.
10518
+ * @returns Cross product.
10519
+ *
10520
+ * @private helper of `minecraft2AvatarVisual`
10521
+ */
10522
+ function crossProduct3D(leftVector, rightVector) {
10523
+ return {
10524
+ x: leftVector.y * rightVector.z - leftVector.z * rightVector.y,
10525
+ y: leftVector.z * rightVector.x - leftVector.x * rightVector.z,
10526
+ z: leftVector.x * rightVector.y - leftVector.y * rightVector.x,
10527
+ };
10528
+ }
10529
+ /**
10530
+ * Computes the 3D dot product of two vectors.
10531
+ *
10532
+ * @param leftVector Left vector.
10533
+ * @param rightVector Right vector.
10534
+ * @returns Dot product.
10535
+ *
10536
+ * @private helper of `minecraft2AvatarVisual`
10537
+ */
10538
+ function dotProduct3D(leftVector, rightVector) {
10539
+ return leftVector.x * rightVector.x + leftVector.y * rightVector.y + leftVector.z * rightVector.z;
10540
+ }
10541
+ /**
10542
+ * Normalizes one 3D vector while keeping zero vectors stable.
10543
+ *
10544
+ * @param vector Source vector.
10545
+ * @returns Normalized vector.
10546
+ *
10547
+ * @private helper of `minecraft2AvatarVisual`
10548
+ */
10549
+ function normalizeVector3(vector) {
10550
+ const length = Math.hypot(vector.x, vector.y, vector.z);
10551
+ if (length === 0) {
10552
+ return vector;
10087
10553
  }
10088
- for (let rowIndex = 2; rowIndex < 8; rowIndex++) {
10089
- texture[rowIndex][3] = stripeColor;
10090
- texture[rowIndex][4] = stripeColor;
10554
+ return {
10555
+ x: vector.x / length,
10556
+ y: vector.y / length,
10557
+ z: vector.z / length,
10558
+ };
10559
+ }
10560
+ /**
10561
+ * Clamps one number into the provided range.
10562
+ *
10563
+ * @param value Input value.
10564
+ * @param minimumValue Inclusive lower bound.
10565
+ * @param maximumValue Inclusive upper bound.
10566
+ * @returns Clamped value.
10567
+ *
10568
+ * @private helper of `minecraft2AvatarVisual`
10569
+ */
10570
+ function clampNumber$1(value, minimumValue, maximumValue) {
10571
+ return Math.min(maximumValue, Math.max(minimumValue, value));
10572
+ }
10573
+ /**
10574
+ * Measures the perimeter of one projected quad.
10575
+ *
10576
+ * @param corners Quad corners.
10577
+ * @returns Perimeter length.
10578
+ *
10579
+ * @private helper of `minecraft2AvatarVisual`
10580
+ */
10581
+ function getProjectedQuadPerimeter(corners) {
10582
+ let perimeter = 0;
10583
+ for (let cornerIndex = 0; cornerIndex < corners.length; cornerIndex++) {
10584
+ const currentCorner = corners[cornerIndex];
10585
+ const nextCorner = corners[(cornerIndex + 1) % corners.length];
10586
+ perimeter += Math.hypot(nextCorner.x - currentCorner.x, nextCorner.y - currentCorner.y);
10091
10587
  }
10092
- texture[4][1] = palette.accent;
10093
- texture[4][6] = palette.accent;
10094
- texture[5][2] = palette.highlight;
10095
- texture[5][5] = palette.highlight;
10096
- return texture;
10588
+ return perimeter;
10097
10589
  }
10098
10590
 
10099
10591
  /* eslint-disable no-magic-numbers */
@@ -11698,6 +12190,7 @@ const AVATAR_VISUALS = [
11698
12190
  octopus3AvatarVisual,
11699
12191
  asciiOctopusAvatarVisual,
11700
12192
  minecraftAvatarVisual,
12193
+ minecraft2AvatarVisual,
11701
12194
  fractalAvatarVisual,
11702
12195
  orbAvatarVisual,
11703
12196
  ];
@@ -13000,7 +13493,11 @@ class PersonaCommitmentDefinition extends BaseCommitmentDefinition {
13000
13493
  // Create new system message with persona at the beginning
13001
13494
  // Format: "You are {agentName}\n{personaContent}"
13002
13495
  // The # PERSONA comment will be removed later by removeCommentsFromSystemMessage
13003
- const personaSection = `# PERSONA\nYou are ${agentName}\n${mergedPersonaContent}`; // <- TODO: Use spaceTrim
13496
+ const personaSection = spaceTrim$1((block) => `
13497
+ # PERSONA
13498
+ You are ${agentName}
13499
+ ${block(mergedPersonaContent)}
13500
+ `);
13004
13501
  const newSystemMessage = cleanedMessage ? `${personaSection}\n\n${cleanedMessage}` : personaSection;
13005
13502
  return {
13006
13503
  ...requirements,
@@ -13902,7 +14399,10 @@ function buildTeamSystemMessageBody(teamEntries) {
13902
14399
  */
13903
14400
  function buildTeamToolDescription(entry) {
13904
14401
  const detailLines = collectTeamEntryDetails(entry).map(({ label, content }) => `${label}: ${content}`);
13905
- return [`Consult teammate ${entry.teammate.label}`, ...detailLines].join('\n');
14402
+ return spaceTrim$1((block) => `
14403
+ Consult teammate ${entry.teammate.label}
14404
+ ${block(detailLines.join('\n'))}
14405
+ `);
13906
14406
  }
13907
14407
  /**
13908
14408
  * Collects structured teammate details that should stay visible to the model.
@@ -19418,7 +19918,10 @@ function createListedTimeoutsAssistantMessage(options) {
19418
19918
  if (hiddenCount > 0) {
19419
19919
  summaryRows.push(`...and ${hiddenCount} more.`);
19420
19920
  }
19421
- return [`Found ${options.total} ${options.total === 1 ? 'timeout' : 'timeouts'}:`, ...summaryRows].join('\n');
19921
+ return spaceTrim$1((block) => `
19922
+ Found ${options.total} ${options.total === 1 ? 'timeout' : 'timeouts'}:
19923
+ ${block(summaryRows.join('\n'))}
19924
+ `);
19422
19925
  }
19423
19926
  /**
19424
19927
  * Formats one timeout row for assistant-visible timeout listings.
@@ -28304,11 +28807,11 @@ function formatChatAttachmentContext(attachments) {
28304
28807
  if (attachments.length === 0) {
28305
28808
  return '';
28306
28809
  }
28307
- return [
28308
- CHAT_ATTACHMENTS_HEADING,
28309
- ...attachments.map((attachment) => formatChatAttachmentLine(attachment)),
28310
- CHAT_ATTACHMENTS_INSTRUCTION,
28311
- ].join('\n');
28810
+ return spaceTrim$1((block) => `
28811
+ ${CHAT_ATTACHMENTS_HEADING}
28812
+ ${block(attachments.map((attachment) => formatChatAttachmentLine(attachment)).join('\n'))}
28813
+ ${CHAT_ATTACHMENTS_INSTRUCTION}
28814
+ `);
28312
28815
  }
28313
28816
 
28314
28817
  /**
@@ -28549,24 +29052,24 @@ function formatResolvedChatAttachmentContent(contentResolution) {
28549
29052
  : ''}`
28550
29053
  : null;
28551
29054
  const warningsLine = contentResolution.warnings.length > 0 ? `Warnings: ${contentResolution.warnings.join(' | ')}` : null;
28552
- if (!contentResolution.content) {
29055
+ const metadataLines = [decodingLine, warningsLine].filter((line) => Boolean(line)).join('\n');
29056
+ const resolvedContent = contentResolution.content;
29057
+ if (!resolvedContent) {
28553
29058
  const reason = contentResolution.reason || 'content unavailable';
28554
- return [`- ${attachmentLabel}: ${reason}. URL: ${contentResolution.attachment.url}`, decodingLine, warningsLine]
28555
- .filter(Boolean)
28556
- .join('\n');
29059
+ return spaceTrim$1((block) => `
29060
+ - ${attachmentLabel}: ${reason}. URL: ${contentResolution.attachment.url}
29061
+ ${block(metadataLines)}
29062
+ `);
28557
29063
  }
28558
29064
  const truncatedLabel = contentResolution.isTruncated ? ' [truncated]' : '';
28559
- return [
28560
- `File: ${attachmentLabel}${truncatedLabel}`,
28561
- `URL: ${contentResolution.attachment.url}`,
28562
- decodingLine,
28563
- warningsLine,
28564
- '```text',
28565
- contentResolution.content,
28566
- '```',
28567
- ]
28568
- .filter(Boolean)
28569
- .join('\n');
29065
+ return spaceTrim$1((block) => `
29066
+ File: ${attachmentLabel}${truncatedLabel}
29067
+ URL: ${contentResolution.attachment.url}
29068
+ ${block(metadataLines)}
29069
+ \`\`\`text
29070
+ ${block(resolvedContent)}
29071
+ \`\`\`
29072
+ `);
28570
29073
  }
28571
29074
  /**
28572
29075
  * Formats inline attachment-content context section for the model.
@@ -28580,11 +29083,14 @@ function formatChatAttachmentContentContext(resolvedContents) {
28580
29083
  if (resolvedContents.length === 0) {
28581
29084
  return '';
28582
29085
  }
28583
- return [
28584
- CHAT_ATTACHMENT_CONTENT_HEADING,
28585
- CHAT_ATTACHMENT_CONTENT_INSTRUCTION,
28586
- ...resolvedContents.map((resolvedContent) => formatResolvedChatAttachmentContent(resolvedContent)),
28587
- ].join('\n\n');
29086
+ return spaceTrim$1((block) => `
29087
+ ${CHAT_ATTACHMENT_CONTENT_HEADING}
29088
+ ${CHAT_ATTACHMENT_CONTENT_INSTRUCTION}
29089
+
29090
+ ${block(resolvedContents
29091
+ .map((resolvedContent) => formatResolvedChatAttachmentContent(resolvedContent))
29092
+ .join('\n\n'))}
29093
+ `);
28588
29094
  }
28589
29095
 
28590
29096
  /**
@@ -29729,9 +30235,11 @@ class OpenAiCompatibleUnsupportedParameterRetrier {
29729
30235
  * Creates the retry-history error message shared by all OpenAI-compatible model variants.
29730
30236
  */
29731
30237
  createAttemptHistoryError(finalErrorMessage) {
29732
- return new PipelineExecutionError(`All attempts failed. Attempt history:\n` +
29733
- formatUnsupportedParameterAttemptHistory(this.attemptStack) +
29734
- `\nFinal error: ${finalErrorMessage}`);
30238
+ return new PipelineExecutionError(spaceTrim$1((block) => `
30239
+ All attempts failed. Attempt history:
30240
+ ${block(formatUnsupportedParameterAttemptHistory(this.attemptStack))}
30241
+ Final error: ${finalErrorMessage}
30242
+ `));
29735
30243
  }
29736
30244
  }
29737
30245
 
@@ -32325,7 +32833,7 @@ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
32325
32833
  });
32326
32834
  }
32327
32835
  let vectorStoreId = cachedVectorStoreId;
32328
- if (this.isNativeKnowledgeSearchEnabled && !vectorStoreId && knowledgeSources && knowledgeSources.length > 0) {
32836
+ if (!vectorStoreId && knowledgeSources && knowledgeSources.length > 0) {
32329
32837
  const vectorStoreResult = await this.createVectorStoreWithKnowledgeSources({
32330
32838
  client: await this.getClient(),
32331
32839
  name,
@@ -32334,19 +32842,13 @@ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
32334
32842
  });
32335
32843
  vectorStoreId = vectorStoreResult.vectorStoreId;
32336
32844
  }
32337
- else if (this.isNativeKnowledgeSearchEnabled && vectorStoreId && this.options.isVerbose) {
32845
+ else if (vectorStoreId && this.options.isVerbose) {
32338
32846
  console.info('[🤰]', 'Using cached vector store for AgentKit agent', {
32339
32847
  name,
32340
32848
  vectorStoreId,
32341
32849
  });
32342
32850
  }
32343
- if (!this.isNativeKnowledgeSearchEnabled) {
32344
- vectorStoreId = undefined;
32345
- }
32346
- const agentKitTools = this.buildAgentKitTools({
32347
- tools,
32348
- vectorStoreId,
32349
- });
32851
+ const agentKitTools = this.buildAgentKitTools({ tools, vectorStoreId });
32350
32852
  const openAiAgentKitAgent = new Agent$1({
32351
32853
  name,
32352
32854
  model: this.agentKitModelName,
@@ -32365,7 +32867,7 @@ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
32365
32867
  name,
32366
32868
  model: this.agentKitModelName,
32367
32869
  toolCount: agentKitTools.length,
32368
- hasVectorStore: this.isNativeKnowledgeSearchEnabled && Boolean(vectorStoreId),
32870
+ hasVectorStore: Boolean(vectorStoreId),
32369
32871
  });
32370
32872
  }
32371
32873
  return preparedAgent;
@@ -32864,12 +33366,6 @@ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
32864
33366
  get agentKitOptions() {
32865
33367
  return this.options;
32866
33368
  }
32867
- /**
32868
- * Returns true when hosted OpenAI vector-store search should back `knowledgeSources`.
32869
- */
32870
- get isNativeKnowledgeSearchEnabled() {
32871
- return this.agentKitOptions.isNativeKnowledgeSearchEnabled !== false;
32872
- }
32873
33369
  /**
32874
33370
  * Discriminant for type guards.
32875
33371
  */