@promptbook/node 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 +786 -258
  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 +785 -257
  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/umd/index.umd.js CHANGED
@@ -48,7 +48,7 @@
48
48
  * @generated
49
49
  * @see https://github.com/webgptorg/promptbook
50
50
  */
51
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-64';
51
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-66';
52
52
  /**
53
53
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
54
54
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -967,6 +967,29 @@
967
967
  }
968
968
  // TODO: Maybe implement by mix+hsl
969
969
 
970
+ /**
971
+ * Relative directory name without the `./` prefix for Git ignore rules and glob patterns.
972
+ *
973
+ * @private internal utility for Promptbook-owned temp files
974
+ */
975
+ const PROMPTBOOK_TEMP_DIRECTORY_NAME = '.promptbook';
976
+ /**
977
+ * Builds one project-relative path inside the shared Promptbook working directory.
978
+ *
979
+ * @private internal utility for Promptbook-owned temp files
980
+ */
981
+ function getPromptbookTempPath(...pathSegments) {
982
+ return `./${getPromptbookTempPosixPath(...pathSegments)}`;
983
+ }
984
+ /**
985
+ * Builds one POSIX path fragment inside the shared Promptbook working directory for globs and generated text files.
986
+ *
987
+ * @private internal utility for Promptbook-owned temp files
988
+ */
989
+ function getPromptbookTempPosixPath(...pathSegments) {
990
+ return path.posix.join(PROMPTBOOK_TEMP_DIRECTORY_NAME, ...pathSegments);
991
+ }
992
+
970
993
  /**
971
994
  * Returns the same value that is passed as argument.
972
995
  * No side effects.
@@ -1161,7 +1184,6 @@
1161
1184
  */
1162
1185
  const DEFAULT_MAX_EXECUTION_ATTEMPTS = 7; // <- TODO: [🤹‍♂️]
1163
1186
  // <- TODO: [🕝] Make also `AGENTS_DIRNAME_ALTERNATIVES`
1164
- // TODO: Just `.promptbook` in config, hardcode subfolders like `download-cache` or `execution-cache`
1165
1187
  /**
1166
1188
  * Where to store the temporary downloads
1167
1189
  *
@@ -1169,7 +1191,15 @@
1169
1191
  *
1170
1192
  * @public exported from `@promptbook/core`
1171
1193
  */
1172
- const DEFAULT_DOWNLOAD_CACHE_DIRNAME = './.promptbook/download-cache';
1194
+ const DEFAULT_DOWNLOAD_CACHE_DIRNAME = getPromptbookTempPath('download-cache');
1195
+ /**
1196
+ * Where to store the cache of executions for promptbook CLI
1197
+ *
1198
+ * Note: When the folder does not exist, it is created recursively
1199
+ *
1200
+ * @public exported from `@promptbook/core`
1201
+ */
1202
+ getPromptbookTempPath('execution-cache');
1173
1203
  /**
1174
1204
  * Where to store the scrape cache
1175
1205
  *
@@ -1177,7 +1207,7 @@
1177
1207
  *
1178
1208
  * @public exported from `@promptbook/core`
1179
1209
  */
1180
- const DEFAULT_SCRAPE_CACHE_DIRNAME = './.promptbook/scrape-cache';
1210
+ const DEFAULT_SCRAPE_CACHE_DIRNAME = getPromptbookTempPath('scrape-cache');
1181
1211
  /*
1182
1212
  TODO: [🌃]
1183
1213
  /**
@@ -8856,8 +8886,10 @@
8856
8886
  */
8857
8887
  const MatcherFormfactorDefinition = {
8858
8888
  name: 'EXPERIMENTAL_MATCHER',
8859
- description: `An evaluation system that determines whether content meets specific criteria or patterns.
8860
- Used for content validation, quality assessment, and intelligent filtering tasks. Currently in experimental phase.`,
8889
+ description: _spaceTrim.spaceTrim(`
8890
+ An evaluation system that determines whether content meets specific criteria or patterns.
8891
+ Used for content validation, quality assessment, and intelligent filtering tasks. Currently in experimental phase.
8892
+ `),
8861
8893
  documentationUrl: `https://github.com/webgptorg/promptbook/discussions/177`,
8862
8894
  pipelineInterface: {
8863
8895
  inputParameters: [
@@ -8914,9 +8946,11 @@
8914
8946
  */
8915
8947
  const TranslatorFormfactorDefinition = {
8916
8948
  name: 'TRANSLATOR',
8917
- description: `A text transformation system that converts input content into different forms,
8918
- including language translations, paraphrasing, style conversions, and tone adjustments.
8919
- This form factor takes one input and produces one transformed output.`,
8949
+ description: _spaceTrim.spaceTrim(`
8950
+ A text transformation system that converts input content into different forms,
8951
+ including language translations, paraphrasing, style conversions, and tone adjustments.
8952
+ This form factor takes one input and produces one transformed output.
8953
+ `),
8920
8954
  documentationUrl: `https://github.com/webgptorg/promptbook/discussions/175`,
8921
8955
  pipelineInterface: {
8922
8956
  inputParameters: [
@@ -10291,12 +10325,21 @@
10291
10325
  // <- TODO: [🧠] Make this dynamic, async, prepare-phase HLAs
10292
10326
  ],
10293
10327
  });
10294
- // TODO: Use spaceTrim in multiline strings
10295
10328
  $pipelineJson.tasks.push({
10296
10329
  taskType: 'PROMPT_TASK',
10297
10330
  name: 'create-an-answer',
10298
10331
  title: 'Create an answer',
10299
- content: 'Write a response to the user message:\n\n**Question from user**\n\n> {userMessage}\n\n**Previous conversation**\n\n> {previousConversationSummary}',
10332
+ content: _spaceTrim.spaceTrim(`
10333
+ Write a response to the user message:
10334
+
10335
+ **Question from user**
10336
+
10337
+ > {userMessage}
10338
+
10339
+ **Previous conversation**
10340
+
10341
+ > {previousConversationSummary}
10342
+ `),
10300
10343
  resultingParameterName: 'chatbotResponse',
10301
10344
  personaName,
10302
10345
  dependentParameterNames: [
@@ -10308,7 +10351,26 @@
10308
10351
  taskType: 'PROMPT_TASK',
10309
10352
  name: 'summarize-the-conversation',
10310
10353
  title: 'Summarize the conversation',
10311
- content: 'Summarize the conversation in a few words:\n\n## Rules\n\n- Summarise the text of the conversation in a few words\n- Convert the text to its basic idea\n- Imagine you are writing the headline or subject line of an email\n- Respond with a few words of summary only\n\n## Conversation\n\n**User:**\n\n> {userMessage}\n\n**You:**\n\n> {chatbotResponse}',
10354
+ content: _spaceTrim.spaceTrim(`
10355
+ Summarize the conversation in a few words:
10356
+
10357
+ ## Rules
10358
+
10359
+ - Summarise the text of the conversation in a few words
10360
+ - Convert the text to its basic idea
10361
+ - Imagine you are writing the headline or subject line of an email
10362
+ - Respond with a few words of summary only
10363
+
10364
+ ## Conversation
10365
+
10366
+ **User:**
10367
+
10368
+ > {userMessage}
10369
+
10370
+ **You:**
10371
+
10372
+ > {chatbotResponse}
10373
+ `),
10312
10374
  resultingParameterName: 'conversationSummary',
10313
10375
  personaName,
10314
10376
  expectations: {
@@ -15851,18 +15913,6 @@
15851
15913
  }
15852
15914
  // Note: [💞] Ignore a discrepancy between file name and entity name
15853
15915
 
15854
- /**
15855
- * Name of the tool used by agents to search configured `KNOWLEDGE` sources.
15856
- *
15857
- * @public exported from `@promptbook/core`
15858
- */
15859
- const KNOWLEDGE_SEARCH_TOOL_NAME = 'knowledge_search';
15860
- /**
15861
- * Title of the system-message section generated for `KNOWLEDGE` commitments.
15862
- *
15863
- * @private constant of `KnowledgeCommitmentDefinition`
15864
- */
15865
- const KNOWLEDGE_SEARCH_SYSTEM_SECTION_TITLE = 'Knowledge Search';
15866
15916
  /**
15867
15917
  * KNOWLEDGE commitment definition
15868
15918
  *
@@ -15984,17 +16034,9 @@
15984
16034
  knowledgeInfoEntries.push(`Knowledge Source Inline: ${inlineSource.filename} (derived from inline content and processed for retrieval during chat)`);
15985
16035
  }
15986
16036
  if (knowledgeInfoEntries.length === 0) {
15987
- return addKnowledgeSearchToolAndSystemSection(nextRequirements);
16037
+ return nextRequirements;
15988
16038
  }
15989
- return addKnowledgeSearchToolAndSystemSection(nextRequirements);
15990
- }
15991
- /**
15992
- * Gets human-readable titles for tool functions provided by this commitment.
15993
- */
15994
- getToolTitles() {
15995
- return {
15996
- [KNOWLEDGE_SEARCH_TOOL_NAME]: 'Knowledge search',
15997
- };
16039
+ return this.appendToSystemMessage(nextRequirements, knowledgeInfoEntries.join('\n'), '\n\n');
15998
16040
  }
15999
16041
  }
16000
16042
  /**
@@ -16008,128 +16050,6 @@
16008
16050
  const significantText = contentWithoutUrls.replace(/[\s.,!?;:'"`()[\]{}<>/-]+/g, '');
16009
16051
  return significantText.length > 0;
16010
16052
  }
16011
- /**
16012
- * Adds the shared `knowledge_search` tool definition and the consolidated system-message section.
16013
- *
16014
- * @param requirements - Requirements after one `KNOWLEDGE` commitment was applied.
16015
- * @returns Requirements with the knowledge search instructions and tool definition.
16016
- *
16017
- * @private internal utility of `KnowledgeCommitmentDefinition`
16018
- */
16019
- function addKnowledgeSearchToolAndSystemSection(requirements) {
16020
- const nextRequirements = addKnowledgeSearchTool(requirements);
16021
- const section = createKnowledgeSearchSystemSection(nextRequirements);
16022
- const sectionHeader = `## ${KNOWLEDGE_SEARCH_SYSTEM_SECTION_TITLE}`;
16023
- if (nextRequirements.systemMessage.includes(sectionHeader)) {
16024
- return {
16025
- ...nextRequirements,
16026
- systemMessage: nextRequirements.systemMessage.replace(new RegExp(`## ${KNOWLEDGE_SEARCH_SYSTEM_SECTION_TITLE.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?(?=\\n\\n##|$)`), section),
16027
- };
16028
- }
16029
- return {
16030
- ...nextRequirements,
16031
- systemMessage: nextRequirements.systemMessage.trim()
16032
- ? `${nextRequirements.systemMessage}\n\n${section}`
16033
- : section,
16034
- };
16035
- }
16036
- /**
16037
- * Adds the `knowledge_search` model tool when it is not already present.
16038
- *
16039
- * @param requirements - Current model requirements.
16040
- * @returns Requirements with the tool definition available to the model.
16041
- *
16042
- * @private internal utility of `KnowledgeCommitmentDefinition`
16043
- */
16044
- function addKnowledgeSearchTool(requirements) {
16045
- const existingTools = requirements.tools || [];
16046
- if (existingTools.some((tool) => tool.name === KNOWLEDGE_SEARCH_TOOL_NAME)) {
16047
- return requirements;
16048
- }
16049
- return {
16050
- ...requirements,
16051
- tools: [
16052
- ...existingTools,
16053
- {
16054
- name: KNOWLEDGE_SEARCH_TOOL_NAME,
16055
- description: _spaceTrim.spaceTrim(`
16056
- Search the agent's configured knowledge sources and return relevant excerpts with citation ids.
16057
- Use this before answering questions that may depend on the agent's KNOWLEDGE commitments.
16058
- `),
16059
- parameters: {
16060
- type: 'object',
16061
- properties: {
16062
- query: {
16063
- type: 'string',
16064
- description: 'The natural-language search query for the knowledge base.',
16065
- },
16066
- limit: {
16067
- type: 'integer',
16068
- description: 'Maximum number of matching source excerpts to return.',
16069
- },
16070
- },
16071
- required: ['query'],
16072
- },
16073
- },
16074
- ],
16075
- };
16076
- }
16077
- /**
16078
- * Creates the model-facing system-message section for knowledge search.
16079
- *
16080
- * @param requirements - Current model requirements.
16081
- * @returns Markdown system-message section.
16082
- *
16083
- * @private internal utility of `KnowledgeCommitmentDefinition`
16084
- */
16085
- function createKnowledgeSearchSystemSection(requirements) {
16086
- const sourceEntries = createKnowledgeSourceSystemEntries(requirements);
16087
- const sourceList = sourceEntries.length > 0 ? sourceEntries.map((entry) => `- ${entry}`).join('\n') : '- None';
16088
- return _spaceTrim.spaceTrim(`
16089
- ## ${KNOWLEDGE_SEARCH_SYSTEM_SECTION_TITLE}
16090
-
16091
- - Use \`${KNOWLEDGE_SEARCH_TOOL_NAME}\` to search the configured knowledge sources before answering questions that depend on this agent's knowledge base.
16092
- - Base source-backed factual answers on the returned excerpts.
16093
- - When you use a returned excerpt, include its citation marker in the answer body, for example \`[0:0]\`.
16094
- - If the search returns no relevant information, say that the knowledge base did not contain the answer instead of inventing it.
16095
-
16096
- Configured knowledge sources:
16097
- ${sourceList}
16098
- `);
16099
- }
16100
- /**
16101
- * Builds a stable list of configured knowledge sources for system-message diagnostics.
16102
- *
16103
- * @param requirements - Current model requirements.
16104
- * @returns Human-readable source entries.
16105
- *
16106
- * @private internal utility of `KnowledgeCommitmentDefinition`
16107
- */
16108
- function createKnowledgeSourceSystemEntries(requirements) {
16109
- var _a;
16110
- const entries = [];
16111
- const seenEntries = new Set();
16112
- for (const source of requirements.knowledgeSources || []) {
16113
- const entry = `Source URL: ${source} (processed for retrieval during chat)`;
16114
- if (seenEntries.has(entry)) {
16115
- continue;
16116
- }
16117
- seenEntries.add(entry);
16118
- entries.push(entry);
16119
- }
16120
- const inlineSources = (((_a = requirements._metadata) === null || _a === void 0 ? void 0 : _a.inlineKnowledgeSources) || [])
16121
- .map((source) => source.filename)
16122
- .filter(Boolean);
16123
- for (const filename of inlineSources) {
16124
- const entry = `Knowledge Source Inline: ${filename} (Inline source: processed for retrieval during chat)`;
16125
- if (seenEntries.has(entry)) {
16126
- continue;
16127
- }
16128
- seenEntries.add(entry);
16129
- entries.push(entry);
16130
- }
16131
- return entries;
16132
- }
16133
16053
 
16134
16054
  /**
16135
16055
  * LANGUAGE commitment definition
@@ -18781,6 +18701,177 @@
18781
18701
  context.restore();
18782
18702
  }
18783
18703
 
18704
+ /* eslint-disable no-magic-numbers */
18705
+ /**
18706
+ * Builds the seeded six-face texture pack used by the Minecraft-style head cuboid.
18707
+ *
18708
+ * @param random Seeded random generator.
18709
+ * @param palette Derived avatar palette.
18710
+ * @param hasHeadband Whether the generated avatar should include a colored headband.
18711
+ * @returns Head cuboid textures.
18712
+ *
18713
+ * @private helper of the Minecraft avatar visuals
18714
+ */
18715
+ function createMinecraftHeadTextures(random, palette, hasHeadband) {
18716
+ const faceTexture = createMinecraftFaceTexture(random, palette, hasHeadband);
18717
+ const hairColor = random() < 0.5 ? palette.primary : palette.secondary;
18718
+ const skinColor = palette.highlight;
18719
+ const headbandColor = hasHeadband ? palette.accent : hairColor;
18720
+ const sideTexture = createFilledTexture(skinColor);
18721
+ const backTexture = createFilledTexture(skinColor);
18722
+ const topTexture = createFilledTexture(hairColor);
18723
+ const bottomTexture = createFilledTexture(`${palette.shadow}cc`);
18724
+ fillTextureRect(sideTexture, 0, 0, 8, 3, hairColor);
18725
+ fillTextureRect(backTexture, 0, 0, 8, 5, hairColor);
18726
+ fillTextureRect(backTexture, 1, 5, 6, 1, hairColor);
18727
+ if (hasHeadband) {
18728
+ fillTextureRect(sideTexture, 0, 2, 8, 1, headbandColor);
18729
+ fillTextureRect(backTexture, 0, 2, 8, 1, headbandColor);
18730
+ fillTextureRect(topTexture, 0, 4, 8, 1, headbandColor);
18731
+ }
18732
+ sideTexture[4][4] = `${palette.shadow}99`;
18733
+ sideTexture[5][4] = `${palette.shadow}cc`;
18734
+ backTexture[6][2] = `${palette.shadow}99`;
18735
+ backTexture[6][5] = `${palette.shadow}99`;
18736
+ return {
18737
+ front: faceTexture,
18738
+ back: backTexture,
18739
+ left: sideTexture,
18740
+ right: mirrorMinecraftTexture(sideTexture),
18741
+ top: topTexture,
18742
+ bottom: bottomTexture,
18743
+ };
18744
+ }
18745
+ /**
18746
+ * Builds the seeded six-face texture pack used by the Minecraft-style torso cuboid.
18747
+ *
18748
+ * @param random Seeded random generator.
18749
+ * @param palette Derived avatar palette.
18750
+ * @returns Torso cuboid textures.
18751
+ *
18752
+ * @private helper of the Minecraft avatar visuals
18753
+ */
18754
+ function createMinecraftTorsoTextures(random, palette) {
18755
+ const frontTexture = createMinecraftShirtTexture(random, palette);
18756
+ const sideTexture = createFilledTexture(palette.primary);
18757
+ const backTexture = createFilledTexture(palette.primary);
18758
+ const topTexture = createFilledTexture(`${palette.highlight}dd`);
18759
+ const bottomTexture = createFilledTexture(`${palette.shadow}dd`);
18760
+ const stripeColor = random() < 0.5 ? palette.secondary : palette.highlight;
18761
+ fillTextureRect(sideTexture, 0, 0, 8, 2, palette.shadow);
18762
+ fillTextureRect(backTexture, 0, 0, 8, 2, palette.shadow);
18763
+ fillTextureRect(backTexture, 3, 2, 2, 6, stripeColor);
18764
+ fillTextureRect(sideTexture, 4, 2, 1, 6, stripeColor);
18765
+ fillTextureRect(topTexture, 0, 0, 8, 2, palette.shadow);
18766
+ fillTextureRect(topTexture, 2, 2, 4, 4, stripeColor);
18767
+ return {
18768
+ front: frontTexture,
18769
+ back: backTexture,
18770
+ left: sideTexture,
18771
+ right: mirrorMinecraftTexture(sideTexture),
18772
+ top: topTexture,
18773
+ bottom: bottomTexture,
18774
+ };
18775
+ }
18776
+ /**
18777
+ * Mirrors one Minecraft texture horizontally.
18778
+ *
18779
+ * @param texture Source texture.
18780
+ * @returns Mirrored texture copy.
18781
+ *
18782
+ * @private helper of the Minecraft avatar visuals
18783
+ */
18784
+ function mirrorMinecraftTexture(texture) {
18785
+ return texture.map((row) => [...row].reverse());
18786
+ }
18787
+ /**
18788
+ * Creates the front-face pixel texture for the cube head.
18789
+ *
18790
+ * @param random Seeded random generator.
18791
+ * @param palette Derived avatar palette.
18792
+ * @param hasHeadband Whether the avatar should render a headband row.
18793
+ * @returns 8x8 pixel texture.
18794
+ *
18795
+ * @private helper of the Minecraft avatar visuals
18796
+ */
18797
+ function createMinecraftFaceTexture(random, palette, hasHeadband) {
18798
+ const texture = createFilledTexture(palette.highlight);
18799
+ const hairlineColor = random() < 0.5 ? palette.primary : palette.secondary;
18800
+ const cheekColor = random() < 0.5 ? `${palette.accent}bb` : `${palette.secondary}bb`;
18801
+ fillTextureRect(texture, 0, 0, 8, 2, hairlineColor);
18802
+ texture[2][0] = hairlineColor;
18803
+ texture[2][7] = hairlineColor;
18804
+ texture[3][0] = hairlineColor;
18805
+ texture[3][7] = hairlineColor;
18806
+ if (hasHeadband) {
18807
+ fillTextureRect(texture, 0, 2, 8, 1, palette.accent);
18808
+ }
18809
+ texture[3][2] = palette.ink;
18810
+ texture[3][5] = palette.ink;
18811
+ texture[4][2] = '#ffffff';
18812
+ texture[4][5] = '#ffffff';
18813
+ texture[5][1] = cheekColor;
18814
+ texture[5][6] = cheekColor;
18815
+ texture[5][3] = palette.shadow;
18816
+ texture[5][4] = palette.shadow;
18817
+ texture[6][3] = palette.shadow;
18818
+ texture[6][4] = palette.shadow;
18819
+ return texture;
18820
+ }
18821
+ /**
18822
+ * Creates the front-face pixel texture for the torso.
18823
+ *
18824
+ * @param random Seeded random generator.
18825
+ * @param palette Derived avatar palette.
18826
+ * @returns 8x8 torso texture.
18827
+ *
18828
+ * @private helper of the Minecraft avatar visuals
18829
+ */
18830
+ function createMinecraftShirtTexture(random, palette) {
18831
+ const texture = createFilledTexture(palette.primary);
18832
+ const stripeColor = random() < 0.5 ? palette.secondary : palette.highlight;
18833
+ fillTextureRect(texture, 0, 0, 8, 2, palette.shadow);
18834
+ for (let rowIndex = 2; rowIndex < 8; rowIndex++) {
18835
+ texture[rowIndex][3] = stripeColor;
18836
+ texture[rowIndex][4] = stripeColor;
18837
+ }
18838
+ texture[4][1] = palette.accent;
18839
+ texture[4][6] = palette.accent;
18840
+ texture[5][2] = palette.highlight;
18841
+ texture[5][5] = palette.highlight;
18842
+ return texture;
18843
+ }
18844
+ /**
18845
+ * Creates one solid-color 8x8 Minecraft texture.
18846
+ *
18847
+ * @param color Fill color.
18848
+ * @returns Filled 8x8 texture.
18849
+ *
18850
+ * @private helper of the Minecraft avatar visuals
18851
+ */
18852
+ function createFilledTexture(color) {
18853
+ return Array.from({ length: 8 }, () => Array.from({ length: 8 }, () => color));
18854
+ }
18855
+ /**
18856
+ * Fills one rectangular area inside a mutable Minecraft texture.
18857
+ *
18858
+ * @param texture Mutable target texture.
18859
+ * @param x Left texture coordinate.
18860
+ * @param y Top texture coordinate.
18861
+ * @param width Rectangle width.
18862
+ * @param height Rectangle height.
18863
+ * @param color Fill color.
18864
+ *
18865
+ * @private helper of the Minecraft avatar visuals
18866
+ */
18867
+ function fillTextureRect(texture, x, y, width, height, color) {
18868
+ for (let rowIndex = y; rowIndex < y + height; rowIndex++) {
18869
+ for (let columnIndex = x; columnIndex < x + width; columnIndex++) {
18870
+ texture[rowIndex][columnIndex] = color;
18871
+ }
18872
+ }
18873
+ }
18874
+
18784
18875
  /* eslint-disable no-magic-numbers */
18785
18876
  /**
18786
18877
  * Minecraft-style 3D avatar visual.
@@ -18805,8 +18896,8 @@
18805
18896
  const bodyX = size * 0.33;
18806
18897
  const bodyY = headY + headSize * 0.96;
18807
18898
  const hasHeadband = random() < 0.5;
18808
- const faceTexture = createMinecraftFaceTexture(createRandom('minecraft-face'), palette, hasHeadband);
18809
- const shirtTexture = createMinecraftShirtTexture(createRandom('minecraft-shirt'), palette);
18899
+ const headTextures = createMinecraftHeadTextures(createRandom('minecraft-face'), palette, hasHeadband);
18900
+ const torsoTextures = createMinecraftTorsoTextures(createRandom('minecraft-shirt'), palette);
18810
18901
  drawAvatarFrame(context, size, palette);
18811
18902
  const spotlight = context.createRadialGradient(size * 0.5, size * 0.18, size * 0.05, size * 0.5, size * 0.18, size * 0.5);
18812
18903
  spotlight.addColorStop(0, `${palette.highlight}66`);
@@ -18826,7 +18917,7 @@
18826
18917
  width: bodyWidth,
18827
18918
  height: bodyHeight,
18828
18919
  depth: bodyDepth,
18829
- frontTexture: shirtTexture,
18920
+ frontTexture: torsoTextures.front,
18830
18921
  topColor: `${palette.highlight}cc`,
18831
18922
  sideColor: `${palette.secondary}dd`,
18832
18923
  outlineColor: `${palette.shadow}aa`,
@@ -18837,7 +18928,7 @@
18837
18928
  width: headSize,
18838
18929
  height: headSize,
18839
18930
  depth,
18840
- frontTexture: faceTexture,
18931
+ frontTexture: headTextures.front,
18841
18932
  topColor: `${palette.highlight}ee`,
18842
18933
  sideColor: `${palette.secondary}ee`,
18843
18934
  outlineColor: `${palette.shadow}cc`,
@@ -18902,72 +18993,505 @@
18902
18993
  context.closePath();
18903
18994
  context.stroke();
18904
18995
  }
18996
+
18997
+ /* eslint-disable no-magic-numbers */
18905
18998
  /**
18906
- * Creates the front-face pixel texture for the cube head.
18999
+ * Fixed scene camera distance used for the proper-3D projection.
18907
19000
  *
18908
- * @param random Seeded random generator.
19001
+ * @private helper of `minecraft2AvatarVisual`
19002
+ */
19003
+ const CAMERA_DISTANCE_RATIO = 1.4;
19004
+ /**
19005
+ * Shared light direction used to shade projected cuboid faces.
19006
+ *
19007
+ * @private helper of `minecraft2AvatarVisual`
19008
+ */
19009
+ const LIGHT_DIRECTION = normalizeVector3({
19010
+ x: 0.4,
19011
+ y: -0.65,
19012
+ z: 0.92,
19013
+ });
19014
+ /**
19015
+ * Minecraft 3D 2 avatar visual.
19016
+ *
19017
+ * @private built-in avatar visual
19018
+ */
19019
+ const minecraft2AvatarVisual = {
19020
+ id: 'minecraft2',
19021
+ title: 'Minecraft 3D 2',
19022
+ description: 'Proper 3D Minecraft-style portrait with textured cuboids and pointer-driven head turns.',
19023
+ isAnimated: true,
19024
+ supportsPointerTracking: true,
19025
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
19026
+ const spotlightY = size * 0.22;
19027
+ const headRandom = createRandom('minecraft2-head');
19028
+ const hasHeadband = headRandom() < 0.5;
19029
+ const headTextures = createMinecraftHeadTextures(createRandom('minecraft2-head-textures'), palette, hasHeadband);
19030
+ const torsoTextures = createMinecraftTorsoTextures(createRandom('minecraft2-body-textures'), palette);
19031
+ const bob = Math.sin(timeMs / 880) * size * 0.014;
19032
+ const bodyYaw = -0.24 + Math.sin(timeMs / 2300) * 0.06 + interaction.bodyOffsetX * 0.16;
19033
+ const bodyPitch = -0.12 + Math.cos(timeMs / 2800) * 0.02 - interaction.bodyOffsetY * 0.06;
19034
+ const headYaw = -0.18 + Math.sin(timeMs / 1900 + 0.6) * 0.05 + interaction.gazeX * 0.62;
19035
+ const headPitch = -0.12 + Math.cos(timeMs / 2400 + 1.1) * 0.03 - interaction.gazeY * 0.38;
19036
+ const sceneCenterX = size * 0.5;
19037
+ const sceneCenterY = size * 0.57;
19038
+ const bodyWidth = size * 0.225;
19039
+ const bodyHeight = size * 0.245;
19040
+ const bodyDepth = size * 0.145;
19041
+ const headSize = size * 0.24;
19042
+ const headLift = size * 0.205;
19043
+ const headForwardShift = interaction.intensity * size * 0.018;
19044
+ const sceneCuboids = [
19045
+ {
19046
+ center: {
19047
+ x: interaction.bodyOffsetX * size * 0.026,
19048
+ y: size * 0.05 + interaction.bodyOffsetY * size * 0.018 + bob,
19049
+ z: 0,
19050
+ },
19051
+ width: bodyWidth,
19052
+ height: bodyHeight,
19053
+ depth: bodyDepth,
19054
+ rotationX: bodyPitch,
19055
+ rotationY: bodyYaw,
19056
+ textures: torsoTextures,
19057
+ outlineColor: `${palette.shadow}cc`,
19058
+ },
19059
+ {
19060
+ center: {
19061
+ x: interaction.bodyOffsetX * size * 0.018 + interaction.gazeX * size * 0.016,
19062
+ y: -headLift + bob * 1.15,
19063
+ z: headForwardShift,
19064
+ },
19065
+ width: headSize,
19066
+ height: headSize,
19067
+ depth: headSize,
19068
+ rotationX: headPitch,
19069
+ rotationY: headYaw,
19070
+ textures: headTextures,
19071
+ outlineColor: `${palette.shadow}dd`,
19072
+ },
19073
+ ];
19074
+ const visibleFaces = sceneCuboids
19075
+ .flatMap((cuboid) => resolveVisibleCuboidFaces(cuboid, size, sceneCenterX, sceneCenterY))
19076
+ .sort((firstFace, secondFace) => firstFace.averageDepth - secondFace.averageDepth);
19077
+ drawAvatarFrame(context, size, palette);
19078
+ drawMinecraftBackdrop(context, size, palette, sceneCenterX, spotlightY, interaction, timeMs);
19079
+ drawMinecraftShadow(context, size, palette, interaction, timeMs);
19080
+ for (const visibleFace of visibleFaces) {
19081
+ drawTexturedProjectedFace(context, visibleFace);
19082
+ }
19083
+ },
19084
+ };
19085
+ /**
19086
+ * Draws the shared background atmosphere behind the Minecraft 3D 2 portrait.
19087
+ *
19088
+ * @param context Canvas 2D context.
19089
+ * @param size Canvas size in CSS pixels.
18909
19090
  * @param palette Derived avatar palette.
18910
- * @param hasHeadband Whether the avatar should render a headband row.
18911
- * @returns 8x8 pixel texture.
19091
+ * @param sceneCenterX Horizontal scene center.
19092
+ * @param spotlightY Vertical spotlight anchor.
19093
+ * @param interaction Smoothed pointer-aware interaction state.
19094
+ * @param timeMs Current animation time in milliseconds.
18912
19095
  *
18913
- * @private helper of `minecraftAvatarVisual`
19096
+ * @private helper of `minecraft2AvatarVisual`
18914
19097
  */
18915
- function createMinecraftFaceTexture(random, palette, hasHeadband) {
18916
- const texture = Array.from({ length: 8 }, () => Array.from({ length: 8 }, () => palette.highlight));
18917
- const hairlineColor = random() < 0.5 ? palette.primary : palette.secondary;
18918
- const cheekColor = random() < 0.5 ? `${palette.accent}bb` : `${palette.secondary}bb`;
18919
- for (let rowIndex = 0; rowIndex < 2; rowIndex++) {
18920
- for (let columnIndex = 0; columnIndex < 8; columnIndex++) {
18921
- texture[rowIndex][columnIndex] = hairlineColor;
19098
+ function drawMinecraftBackdrop(context, size, palette, sceneCenterX, spotlightY, interaction, timeMs) {
19099
+ const spotlightGradient = context.createRadialGradient(sceneCenterX + interaction.gazeX * size * 0.08, spotlightY + interaction.gazeY * size * 0.05, size * 0.03, sceneCenterX, spotlightY, size * 0.52);
19100
+ spotlightGradient.addColorStop(0, `${palette.highlight}66`);
19101
+ spotlightGradient.addColorStop(0.42, `${palette.accent}1d`);
19102
+ spotlightGradient.addColorStop(1, `${palette.highlight}00`);
19103
+ context.fillStyle = spotlightGradient;
19104
+ context.fillRect(0, 0, size, size);
19105
+ const rimGradient = context.createLinearGradient(0, size * 0.14, 0, size * 0.92);
19106
+ rimGradient.addColorStop(0, `${palette.highlight}12`);
19107
+ rimGradient.addColorStop(0.55, `${palette.secondary}0a`);
19108
+ rimGradient.addColorStop(1, `${palette.shadow}00`);
19109
+ context.fillStyle = rimGradient;
19110
+ context.fillRect(0, 0, size, size);
19111
+ context.save();
19112
+ context.globalAlpha = 0.08 + interaction.intensity * 0.04;
19113
+ context.fillStyle = palette.highlight;
19114
+ context.beginPath();
19115
+ 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);
19116
+ context.fill();
19117
+ context.restore();
19118
+ }
19119
+ /**
19120
+ * Draws the soft floor shadow used to anchor the cuboids in the frame.
19121
+ *
19122
+ * @param context Canvas 2D context.
19123
+ * @param size Canvas size in CSS pixels.
19124
+ * @param palette Derived avatar palette.
19125
+ * @param interaction Smoothed pointer-aware interaction state.
19126
+ * @param timeMs Current animation time in milliseconds.
19127
+ *
19128
+ * @private helper of `minecraft2AvatarVisual`
19129
+ */
19130
+ function drawMinecraftShadow(context, size, palette, interaction, timeMs) {
19131
+ context.save();
19132
+ context.fillStyle = `${palette.shadow}66`;
19133
+ context.filter = `blur(${size * 0.02}px)`;
19134
+ context.beginPath();
19135
+ 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);
19136
+ context.fill();
19137
+ context.restore();
19138
+ }
19139
+ /**
19140
+ * Resolves all visible projected faces for one scene cuboid.
19141
+ *
19142
+ * @param cuboid Scene cuboid definition.
19143
+ * @param size Canvas size in CSS pixels.
19144
+ * @param sceneCenterX Horizontal scene center.
19145
+ * @param sceneCenterY Vertical scene center.
19146
+ * @returns Visible faces sorted later by depth.
19147
+ *
19148
+ * @private helper of `minecraft2AvatarVisual`
19149
+ */
19150
+ function resolveVisibleCuboidFaces(cuboid, size, sceneCenterX, sceneCenterY) {
19151
+ const halfWidth = cuboid.width / 2;
19152
+ const halfHeight = cuboid.height / 2;
19153
+ const halfDepth = cuboid.depth / 2;
19154
+ const faceDefinitions = [
19155
+ {
19156
+ texture: cuboid.textures.front,
19157
+ corners: [
19158
+ { x: -halfWidth, y: -halfHeight, z: halfDepth },
19159
+ { x: halfWidth, y: -halfHeight, z: halfDepth },
19160
+ { x: halfWidth, y: halfHeight, z: halfDepth },
19161
+ { x: -halfWidth, y: halfHeight, z: halfDepth },
19162
+ ],
19163
+ },
19164
+ {
19165
+ texture: cuboid.textures.back,
19166
+ corners: [
19167
+ { x: halfWidth, y: -halfHeight, z: -halfDepth },
19168
+ { x: -halfWidth, y: -halfHeight, z: -halfDepth },
19169
+ { x: -halfWidth, y: halfHeight, z: -halfDepth },
19170
+ { x: halfWidth, y: halfHeight, z: -halfDepth },
19171
+ ],
19172
+ },
19173
+ {
19174
+ texture: cuboid.textures.right,
19175
+ corners: [
19176
+ { x: halfWidth, y: -halfHeight, z: halfDepth },
19177
+ { x: halfWidth, y: -halfHeight, z: -halfDepth },
19178
+ { x: halfWidth, y: halfHeight, z: -halfDepth },
19179
+ { x: halfWidth, y: halfHeight, z: halfDepth },
19180
+ ],
19181
+ },
19182
+ {
19183
+ texture: cuboid.textures.left,
19184
+ corners: [
19185
+ { x: -halfWidth, y: -halfHeight, z: -halfDepth },
19186
+ { x: -halfWidth, y: -halfHeight, z: halfDepth },
19187
+ { x: -halfWidth, y: halfHeight, z: halfDepth },
19188
+ { x: -halfWidth, y: halfHeight, z: -halfDepth },
19189
+ ],
19190
+ },
19191
+ {
19192
+ texture: cuboid.textures.top,
19193
+ corners: [
19194
+ { x: -halfWidth, y: -halfHeight, z: -halfDepth },
19195
+ { x: halfWidth, y: -halfHeight, z: -halfDepth },
19196
+ { x: halfWidth, y: -halfHeight, z: halfDepth },
19197
+ { x: -halfWidth, y: -halfHeight, z: halfDepth },
19198
+ ],
19199
+ },
19200
+ {
19201
+ texture: cuboid.textures.bottom,
19202
+ corners: [
19203
+ { x: -halfWidth, y: halfHeight, z: halfDepth },
19204
+ { x: halfWidth, y: halfHeight, z: halfDepth },
19205
+ { x: halfWidth, y: halfHeight, z: -halfDepth },
19206
+ { x: -halfWidth, y: halfHeight, z: -halfDepth },
19207
+ ],
19208
+ },
19209
+ ];
19210
+ const visibleFaces = faceDefinitions
19211
+ .map((faceDefinition) => {
19212
+ const transformedCorners = faceDefinition.corners.map((corner) => transformScenePoint(corner, cuboid.center, cuboid.rotationX, cuboid.rotationY));
19213
+ const faceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
19214
+ if (faceNormal.z <= 0.02) {
19215
+ return null;
18922
19216
  }
19217
+ const projectedCorners = transformedCorners.map((corner) => projectScenePoint(corner, size, sceneCenterX, sceneCenterY));
19218
+ return {
19219
+ corners: projectedCorners,
19220
+ texture: faceDefinition.texture,
19221
+ averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
19222
+ lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION), -1, 1),
19223
+ outlineColor: cuboid.outlineColor,
19224
+ };
19225
+ });
19226
+ return visibleFaces.filter((visibleFace) => visibleFace !== null);
19227
+ }
19228
+ /**
19229
+ * Draws one projected textured face by tessellating its texture cells into quads.
19230
+ *
19231
+ * @param context Canvas 2D context.
19232
+ * @param face Visible projected face.
19233
+ *
19234
+ * @private helper of `minecraft2AvatarVisual`
19235
+ */
19236
+ function drawTexturedProjectedFace(context, face) {
19237
+ var _a;
19238
+ const rows = face.texture.length;
19239
+ const columns = ((_a = face.texture[0]) === null || _a === void 0 ? void 0 : _a.length) || 0;
19240
+ if (rows === 0 || columns === 0) {
19241
+ return;
18923
19242
  }
18924
- texture[2][0] = hairlineColor;
18925
- texture[2][7] = hairlineColor;
18926
- texture[3][0] = hairlineColor;
18927
- texture[3][7] = hairlineColor;
18928
- if (hasHeadband) {
18929
- for (let columnIndex = 0; columnIndex < 8; columnIndex++) {
18930
- texture[2][columnIndex] = palette.accent;
19243
+ for (let rowIndex = 0; rowIndex < rows; rowIndex++) {
19244
+ for (let columnIndex = 0; columnIndex < columns; columnIndex++) {
19245
+ const startX = columnIndex / columns;
19246
+ const endX = (columnIndex + 1) / columns;
19247
+ const startY = rowIndex / rows;
19248
+ const endY = (rowIndex + 1) / rows;
19249
+ drawProjectedQuad(context, [
19250
+ interpolateProjectedQuad(face.corners, startX, startY),
19251
+ interpolateProjectedQuad(face.corners, endX, startY),
19252
+ interpolateProjectedQuad(face.corners, endX, endY),
19253
+ interpolateProjectedQuad(face.corners, startX, endY),
19254
+ ], face.texture[rowIndex][columnIndex]);
18931
19255
  }
18932
19256
  }
18933
- texture[3][2] = palette.ink;
18934
- texture[3][5] = palette.ink;
18935
- texture[4][2] = '#ffffff';
18936
- texture[4][5] = '#ffffff';
18937
- texture[5][1] = cheekColor;
18938
- texture[5][6] = cheekColor;
18939
- texture[5][3] = palette.shadow;
18940
- texture[5][4] = palette.shadow;
18941
- texture[6][3] = palette.shadow;
18942
- texture[6][4] = palette.shadow;
18943
- return texture;
19257
+ if (face.lightIntensity > 0) {
19258
+ drawProjectedQuad(context, face.corners, `rgba(255, 255, 255, ${0.15 * face.lightIntensity})`);
19259
+ }
19260
+ else if (face.lightIntensity < 0) {
19261
+ drawProjectedQuad(context, face.corners, `rgba(0, 0, 0, ${0.22 * Math.abs(face.lightIntensity)})`);
19262
+ }
19263
+ context.save();
19264
+ context.beginPath();
19265
+ context.moveTo(face.corners[0].x, face.corners[0].y);
19266
+ for (let cornerIndex = 1; cornerIndex < face.corners.length; cornerIndex++) {
19267
+ context.lineTo(face.corners[cornerIndex].x, face.corners[cornerIndex].y);
19268
+ }
19269
+ context.closePath();
19270
+ context.strokeStyle = face.outlineColor;
19271
+ context.lineWidth = Math.max(1.1, getProjectedQuadPerimeter(face.corners) * 0.0045);
19272
+ context.lineJoin = 'round';
19273
+ context.stroke();
19274
+ context.restore();
18944
19275
  }
18945
19276
  /**
18946
- * Creates the front-face pixel texture for the torso.
19277
+ * Draws one filled projected quad.
18947
19278
  *
18948
- * @param random Seeded random generator.
18949
- * @param palette Derived avatar palette.
18950
- * @returns 8x8 torso texture.
19279
+ * @param context Canvas 2D context.
19280
+ * @param corners Quad corners in clockwise order.
19281
+ * @param fillStyle CSS fill style.
18951
19282
  *
18952
- * @private helper of `minecraftAvatarVisual`
19283
+ * @private helper of `minecraft2AvatarVisual`
18953
19284
  */
18954
- function createMinecraftShirtTexture(random, palette) {
18955
- const texture = Array.from({ length: 8 }, () => Array.from({ length: 8 }, () => palette.primary));
18956
- const stripeColor = random() < 0.5 ? palette.secondary : palette.highlight;
18957
- for (let rowIndex = 0; rowIndex < 2; rowIndex++) {
18958
- for (let columnIndex = 0; columnIndex < 8; columnIndex++) {
18959
- texture[rowIndex][columnIndex] = palette.shadow;
18960
- }
19285
+ function drawProjectedQuad(context, corners, fillStyle) {
19286
+ context.beginPath();
19287
+ context.moveTo(corners[0].x, corners[0].y);
19288
+ context.lineTo(corners[1].x, corners[1].y);
19289
+ context.lineTo(corners[2].x, corners[2].y);
19290
+ context.lineTo(corners[3].x, corners[3].y);
19291
+ context.closePath();
19292
+ context.fillStyle = fillStyle;
19293
+ context.fill();
19294
+ }
19295
+ /**
19296
+ * Interpolates one point inside a projected quad across both quad axes.
19297
+ *
19298
+ * @param corners Quad corners in clockwise order.
19299
+ * @param horizontalRatio Horizontal ratio in the range `[0, 1]`.
19300
+ * @param verticalRatio Vertical ratio in the range `[0, 1]`.
19301
+ * @returns Interpolated projected point.
19302
+ *
19303
+ * @private helper of `minecraft2AvatarVisual`
19304
+ */
19305
+ function interpolateProjectedQuad(corners, horizontalRatio, verticalRatio) {
19306
+ const topPoint = interpolateProjectedPoint(corners[0], corners[1], horizontalRatio);
19307
+ const bottomPoint = interpolateProjectedPoint(corners[3], corners[2], horizontalRatio);
19308
+ return interpolateProjectedPoint(topPoint, bottomPoint, verticalRatio);
19309
+ }
19310
+ /**
19311
+ * Interpolates between two projected points.
19312
+ *
19313
+ * @param startPoint Start point.
19314
+ * @param endPoint End point.
19315
+ * @param ratio Interpolation ratio in the range `[0, 1]`.
19316
+ * @returns Interpolated projected point.
19317
+ *
19318
+ * @private helper of `minecraft2AvatarVisual`
19319
+ */
19320
+ function interpolateProjectedPoint(startPoint, endPoint, ratio) {
19321
+ return {
19322
+ x: startPoint.x + (endPoint.x - startPoint.x) * ratio,
19323
+ y: startPoint.y + (endPoint.y - startPoint.y) * ratio,
19324
+ z: startPoint.z + (endPoint.z - startPoint.z) * ratio,
19325
+ };
19326
+ }
19327
+ /**
19328
+ * Projects one rotated scene point into canvas coordinates.
19329
+ *
19330
+ * @param point Scene point.
19331
+ * @param size Canvas size in CSS pixels.
19332
+ * @param sceneCenterX Horizontal scene center.
19333
+ * @param sceneCenterY Vertical scene center.
19334
+ * @returns Projected point.
19335
+ *
19336
+ * @private helper of `minecraft2AvatarVisual`
19337
+ */
19338
+ function projectScenePoint(point, size, sceneCenterX, sceneCenterY) {
19339
+ const cameraDistance = size * CAMERA_DISTANCE_RATIO;
19340
+ const perspectiveScale = cameraDistance / Math.max(cameraDistance - point.z, cameraDistance * 0.35);
19341
+ return {
19342
+ x: sceneCenterX + point.x * perspectiveScale,
19343
+ y: sceneCenterY + point.y * perspectiveScale,
19344
+ z: point.z,
19345
+ };
19346
+ }
19347
+ /**
19348
+ * Applies the local cuboid rotations and translation to one scene point.
19349
+ *
19350
+ * @param localPoint Point in cuboid-local space.
19351
+ * @param center Cuboid center in scene space.
19352
+ * @param rotationX Cuboid pitch in radians.
19353
+ * @param rotationY Cuboid yaw in radians.
19354
+ * @returns Transformed scene-space point.
19355
+ *
19356
+ * @private helper of `minecraft2AvatarVisual`
19357
+ */
19358
+ function transformScenePoint(localPoint, center, rotationX, rotationY) {
19359
+ const yawedPoint = rotatePointAroundY(localPoint, rotationY);
19360
+ const pitchedPoint = rotatePointAroundX(yawedPoint, rotationX);
19361
+ return {
19362
+ x: center.x + pitchedPoint.x,
19363
+ y: center.y + pitchedPoint.y,
19364
+ z: center.z + pitchedPoint.z,
19365
+ };
19366
+ }
19367
+ /**
19368
+ * Rotates one point around the local Y axis.
19369
+ *
19370
+ * @param point Source point.
19371
+ * @param angle Rotation angle in radians.
19372
+ * @returns Rotated point.
19373
+ *
19374
+ * @private helper of `minecraft2AvatarVisual`
19375
+ */
19376
+ function rotatePointAroundY(point, angle) {
19377
+ const cosine = Math.cos(angle);
19378
+ const sine = Math.sin(angle);
19379
+ return {
19380
+ x: point.x * cosine + point.z * sine,
19381
+ y: point.y,
19382
+ z: -point.x * sine + point.z * cosine,
19383
+ };
19384
+ }
19385
+ /**
19386
+ * Rotates one point around the local X axis.
19387
+ *
19388
+ * @param point Source point.
19389
+ * @param angle Rotation angle in radians.
19390
+ * @returns Rotated point.
19391
+ *
19392
+ * @private helper of `minecraft2AvatarVisual`
19393
+ */
19394
+ function rotatePointAroundX(point, angle) {
19395
+ const cosine = Math.cos(angle);
19396
+ const sine = Math.sin(angle);
19397
+ return {
19398
+ x: point.x,
19399
+ y: point.y * cosine - point.z * sine,
19400
+ z: point.y * sine + point.z * cosine,
19401
+ };
19402
+ }
19403
+ /**
19404
+ * Subtracts one 3D point from another.
19405
+ *
19406
+ * @param leftPoint Left point.
19407
+ * @param rightPoint Right point.
19408
+ * @returns Difference vector.
19409
+ *
19410
+ * @private helper of `minecraft2AvatarVisual`
19411
+ */
19412
+ function subtractPoint3D(leftPoint, rightPoint) {
19413
+ return {
19414
+ x: leftPoint.x - rightPoint.x,
19415
+ y: leftPoint.y - rightPoint.y,
19416
+ z: leftPoint.z - rightPoint.z,
19417
+ };
19418
+ }
19419
+ /**
19420
+ * Computes the 3D cross product of two vectors.
19421
+ *
19422
+ * @param leftVector Left vector.
19423
+ * @param rightVector Right vector.
19424
+ * @returns Cross product.
19425
+ *
19426
+ * @private helper of `minecraft2AvatarVisual`
19427
+ */
19428
+ function crossProduct3D(leftVector, rightVector) {
19429
+ return {
19430
+ x: leftVector.y * rightVector.z - leftVector.z * rightVector.y,
19431
+ y: leftVector.z * rightVector.x - leftVector.x * rightVector.z,
19432
+ z: leftVector.x * rightVector.y - leftVector.y * rightVector.x,
19433
+ };
19434
+ }
19435
+ /**
19436
+ * Computes the 3D dot product of two vectors.
19437
+ *
19438
+ * @param leftVector Left vector.
19439
+ * @param rightVector Right vector.
19440
+ * @returns Dot product.
19441
+ *
19442
+ * @private helper of `minecraft2AvatarVisual`
19443
+ */
19444
+ function dotProduct3D(leftVector, rightVector) {
19445
+ return leftVector.x * rightVector.x + leftVector.y * rightVector.y + leftVector.z * rightVector.z;
19446
+ }
19447
+ /**
19448
+ * Normalizes one 3D vector while keeping zero vectors stable.
19449
+ *
19450
+ * @param vector Source vector.
19451
+ * @returns Normalized vector.
19452
+ *
19453
+ * @private helper of `minecraft2AvatarVisual`
19454
+ */
19455
+ function normalizeVector3(vector) {
19456
+ const length = Math.hypot(vector.x, vector.y, vector.z);
19457
+ if (length === 0) {
19458
+ return vector;
18961
19459
  }
18962
- for (let rowIndex = 2; rowIndex < 8; rowIndex++) {
18963
- texture[rowIndex][3] = stripeColor;
18964
- texture[rowIndex][4] = stripeColor;
19460
+ return {
19461
+ x: vector.x / length,
19462
+ y: vector.y / length,
19463
+ z: vector.z / length,
19464
+ };
19465
+ }
19466
+ /**
19467
+ * Clamps one number into the provided range.
19468
+ *
19469
+ * @param value Input value.
19470
+ * @param minimumValue Inclusive lower bound.
19471
+ * @param maximumValue Inclusive upper bound.
19472
+ * @returns Clamped value.
19473
+ *
19474
+ * @private helper of `minecraft2AvatarVisual`
19475
+ */
19476
+ function clampNumber$1(value, minimumValue, maximumValue) {
19477
+ return Math.min(maximumValue, Math.max(minimumValue, value));
19478
+ }
19479
+ /**
19480
+ * Measures the perimeter of one projected quad.
19481
+ *
19482
+ * @param corners Quad corners.
19483
+ * @returns Perimeter length.
19484
+ *
19485
+ * @private helper of `minecraft2AvatarVisual`
19486
+ */
19487
+ function getProjectedQuadPerimeter(corners) {
19488
+ let perimeter = 0;
19489
+ for (let cornerIndex = 0; cornerIndex < corners.length; cornerIndex++) {
19490
+ const currentCorner = corners[cornerIndex];
19491
+ const nextCorner = corners[(cornerIndex + 1) % corners.length];
19492
+ perimeter += Math.hypot(nextCorner.x - currentCorner.x, nextCorner.y - currentCorner.y);
18965
19493
  }
18966
- texture[4][1] = palette.accent;
18967
- texture[4][6] = palette.accent;
18968
- texture[5][2] = palette.highlight;
18969
- texture[5][5] = palette.highlight;
18970
- return texture;
19494
+ return perimeter;
18971
19495
  }
18972
19496
 
18973
19497
  /* eslint-disable no-magic-numbers */
@@ -20572,6 +21096,7 @@
20572
21096
  octopus3AvatarVisual,
20573
21097
  asciiOctopusAvatarVisual,
20574
21098
  minecraftAvatarVisual,
21099
+ minecraft2AvatarVisual,
20575
21100
  fractalAvatarVisual,
20576
21101
  orbAvatarVisual,
20577
21102
  ];
@@ -21874,7 +22399,11 @@
21874
22399
  // Create new system message with persona at the beginning
21875
22400
  // Format: "You are {agentName}\n{personaContent}"
21876
22401
  // The # PERSONA comment will be removed later by removeCommentsFromSystemMessage
21877
- const personaSection = `# PERSONA\nYou are ${agentName}\n${mergedPersonaContent}`; // <- TODO: Use spaceTrim
22402
+ const personaSection = _spaceTrim.spaceTrim((block) => `
22403
+ # PERSONA
22404
+ You are ${agentName}
22405
+ ${block(mergedPersonaContent)}
22406
+ `);
21878
22407
  const newSystemMessage = cleanedMessage ? `${personaSection}\n\n${cleanedMessage}` : personaSection;
21879
22408
  return {
21880
22409
  ...requirements,
@@ -22776,7 +23305,10 @@
22776
23305
  */
22777
23306
  function buildTeamToolDescription(entry) {
22778
23307
  const detailLines = collectTeamEntryDetails(entry).map(({ label, content }) => `${label}: ${content}`);
22779
- return [`Consult teammate ${entry.teammate.label}`, ...detailLines].join('\n');
23308
+ return _spaceTrim.spaceTrim((block) => `
23309
+ Consult teammate ${entry.teammate.label}
23310
+ ${block(detailLines.join('\n'))}
23311
+ `);
22780
23312
  }
22781
23313
  /**
22782
23314
  * Collects structured teammate details that should stay visible to the model.
@@ -28273,7 +28805,10 @@
28273
28805
  if (hiddenCount > 0) {
28274
28806
  summaryRows.push(`...and ${hiddenCount} more.`);
28275
28807
  }
28276
- return [`Found ${options.total} ${options.total === 1 ? 'timeout' : 'timeouts'}:`, ...summaryRows].join('\n');
28808
+ return _spaceTrim.spaceTrim((block) => `
28809
+ Found ${options.total} ${options.total === 1 ? 'timeout' : 'timeouts'}:
28810
+ ${block(summaryRows.join('\n'))}
28811
+ `);
28277
28812
  }
28278
28813
  /**
28279
28814
  * Formats one timeout row for assistant-visible timeout listings.
@@ -34096,11 +34631,11 @@
34096
34631
  if (attachments.length === 0) {
34097
34632
  return '';
34098
34633
  }
34099
- return [
34100
- CHAT_ATTACHMENTS_HEADING,
34101
- ...attachments.map((attachment) => formatChatAttachmentLine(attachment)),
34102
- CHAT_ATTACHMENTS_INSTRUCTION,
34103
- ].join('\n');
34634
+ return _spaceTrim.spaceTrim((block) => `
34635
+ ${CHAT_ATTACHMENTS_HEADING}
34636
+ ${block(attachments.map((attachment) => formatChatAttachmentLine(attachment)).join('\n'))}
34637
+ ${CHAT_ATTACHMENTS_INSTRUCTION}
34638
+ `);
34104
34639
  }
34105
34640
 
34106
34641
  /**
@@ -34341,24 +34876,24 @@
34341
34876
  : ''}`
34342
34877
  : null;
34343
34878
  const warningsLine = contentResolution.warnings.length > 0 ? `Warnings: ${contentResolution.warnings.join(' | ')}` : null;
34344
- if (!contentResolution.content) {
34879
+ const metadataLines = [decodingLine, warningsLine].filter((line) => Boolean(line)).join('\n');
34880
+ const resolvedContent = contentResolution.content;
34881
+ if (!resolvedContent) {
34345
34882
  const reason = contentResolution.reason || 'content unavailable';
34346
- return [`- ${attachmentLabel}: ${reason}. URL: ${contentResolution.attachment.url}`, decodingLine, warningsLine]
34347
- .filter(Boolean)
34348
- .join('\n');
34883
+ return _spaceTrim.spaceTrim((block) => `
34884
+ - ${attachmentLabel}: ${reason}. URL: ${contentResolution.attachment.url}
34885
+ ${block(metadataLines)}
34886
+ `);
34349
34887
  }
34350
34888
  const truncatedLabel = contentResolution.isTruncated ? ' [truncated]' : '';
34351
- return [
34352
- `File: ${attachmentLabel}${truncatedLabel}`,
34353
- `URL: ${contentResolution.attachment.url}`,
34354
- decodingLine,
34355
- warningsLine,
34356
- '```text',
34357
- contentResolution.content,
34358
- '```',
34359
- ]
34360
- .filter(Boolean)
34361
- .join('\n');
34889
+ return _spaceTrim.spaceTrim((block) => `
34890
+ File: ${attachmentLabel}${truncatedLabel}
34891
+ URL: ${contentResolution.attachment.url}
34892
+ ${block(metadataLines)}
34893
+ \`\`\`text
34894
+ ${block(resolvedContent)}
34895
+ \`\`\`
34896
+ `);
34362
34897
  }
34363
34898
  /**
34364
34899
  * Formats inline attachment-content context section for the model.
@@ -34372,11 +34907,14 @@
34372
34907
  if (resolvedContents.length === 0) {
34373
34908
  return '';
34374
34909
  }
34375
- return [
34376
- CHAT_ATTACHMENT_CONTENT_HEADING,
34377
- CHAT_ATTACHMENT_CONTENT_INSTRUCTION,
34378
- ...resolvedContents.map((resolvedContent) => formatResolvedChatAttachmentContent(resolvedContent)),
34379
- ].join('\n\n');
34910
+ return _spaceTrim.spaceTrim((block) => `
34911
+ ${CHAT_ATTACHMENT_CONTENT_HEADING}
34912
+ ${CHAT_ATTACHMENT_CONTENT_INSTRUCTION}
34913
+
34914
+ ${block(resolvedContents
34915
+ .map((resolvedContent) => formatResolvedChatAttachmentContent(resolvedContent))
34916
+ .join('\n\n'))}
34917
+ `);
34380
34918
  }
34381
34919
 
34382
34920
  /**
@@ -35521,9 +36059,11 @@
35521
36059
  * Creates the retry-history error message shared by all OpenAI-compatible model variants.
35522
36060
  */
35523
36061
  createAttemptHistoryError(finalErrorMessage) {
35524
- return new PipelineExecutionError(`All attempts failed. Attempt history:\n` +
35525
- formatUnsupportedParameterAttemptHistory(this.attemptStack) +
35526
- `\nFinal error: ${finalErrorMessage}`);
36062
+ return new PipelineExecutionError(_spaceTrim.spaceTrim((block) => `
36063
+ All attempts failed. Attempt history:
36064
+ ${block(formatUnsupportedParameterAttemptHistory(this.attemptStack))}
36065
+ Final error: ${finalErrorMessage}
36066
+ `));
35527
36067
  }
35528
36068
  }
35529
36069
 
@@ -38117,7 +38657,7 @@
38117
38657
  });
38118
38658
  }
38119
38659
  let vectorStoreId = cachedVectorStoreId;
38120
- if (this.isNativeKnowledgeSearchEnabled && !vectorStoreId && knowledgeSources && knowledgeSources.length > 0) {
38660
+ if (!vectorStoreId && knowledgeSources && knowledgeSources.length > 0) {
38121
38661
  const vectorStoreResult = await this.createVectorStoreWithKnowledgeSources({
38122
38662
  client: await this.getClient(),
38123
38663
  name,
@@ -38126,19 +38666,13 @@
38126
38666
  });
38127
38667
  vectorStoreId = vectorStoreResult.vectorStoreId;
38128
38668
  }
38129
- else if (this.isNativeKnowledgeSearchEnabled && vectorStoreId && this.options.isVerbose) {
38669
+ else if (vectorStoreId && this.options.isVerbose) {
38130
38670
  console.info('[🤰]', 'Using cached vector store for AgentKit agent', {
38131
38671
  name,
38132
38672
  vectorStoreId,
38133
38673
  });
38134
38674
  }
38135
- if (!this.isNativeKnowledgeSearchEnabled) {
38136
- vectorStoreId = undefined;
38137
- }
38138
- const agentKitTools = this.buildAgentKitTools({
38139
- tools,
38140
- vectorStoreId,
38141
- });
38675
+ const agentKitTools = this.buildAgentKitTools({ tools, vectorStoreId });
38142
38676
  const openAiAgentKitAgent = new agents.Agent({
38143
38677
  name,
38144
38678
  model: this.agentKitModelName,
@@ -38157,7 +38691,7 @@
38157
38691
  name,
38158
38692
  model: this.agentKitModelName,
38159
38693
  toolCount: agentKitTools.length,
38160
- hasVectorStore: this.isNativeKnowledgeSearchEnabled && Boolean(vectorStoreId),
38694
+ hasVectorStore: Boolean(vectorStoreId),
38161
38695
  });
38162
38696
  }
38163
38697
  return preparedAgent;
@@ -38656,12 +39190,6 @@
38656
39190
  get agentKitOptions() {
38657
39191
  return this.options;
38658
39192
  }
38659
- /**
38660
- * Returns true when hosted OpenAI vector-store search should back `knowledgeSources`.
38661
- */
38662
- get isNativeKnowledgeSearchEnabled() {
38663
- return this.agentKitOptions.isNativeKnowledgeSearchEnabled !== false;
38664
- }
38665
39193
  /**
38666
39194
  * Discriminant for type guards.
38667
39195
  */