@promptbook/components 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 +982 -264
  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 +1 -1
  27. package/umd/index.umd.js +985 -267
  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
@@ -1,8 +1,8 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react/jsx-runtime'), require('spacetrim'), require('react'), require('crypto-js'), require('crypto-js/enc-hex'), require('path'), require('crypto'), require('moment'), require('mime-types'), require('react-dom'), require('@monaco-editor/react'), require('destroyable'), require('katex'), require('react-dom/client'), require('showdown'), require('rxjs'), require('jspdf'), require('moment/locale/cs'), require('lucide-react'), require('waitasecond'), require('crypto-js/sha256'), require('papaparse'), require('colors'), require('@openai/agents'), require('bottleneck'), require('openai'), require('qrcode')) :
3
- typeof define === 'function' && define.amd ? define(['exports', 'react/jsx-runtime', 'spacetrim', 'react', 'crypto-js', 'crypto-js/enc-hex', 'path', 'crypto', 'moment', 'mime-types', 'react-dom', '@monaco-editor/react', 'destroyable', 'katex', 'react-dom/client', 'showdown', 'rxjs', 'jspdf', 'moment/locale/cs', 'lucide-react', 'waitasecond', 'crypto-js/sha256', 'papaparse', 'colors', '@openai/agents', 'bottleneck', 'openai', 'qrcode'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-components"] = {}, global.jsxRuntime, global.spacetrim, global.react, global.cryptoJs, global.hexEncoder, global.path, global.crypto$1, global.moment, global.mimeTypes, global.reactDom, global.MonacoEditor, global.destroyable, global.katex, global.client, global.showdown, global.rxjs, global.jspdf, null, global.lucideReact, global.waitasecond, global.sha256, global.papaparse, global.colors, global.agents, global.Bottleneck, global.OpenAI, global.QRCode));
5
- })(this, (function (exports, jsxRuntime, spacetrim, react, cryptoJs, hexEncoder, path, crypto$1, moment, mimeTypes, reactDom, MonacoEditor, destroyable, katex, client, showdown, rxjs, jspdf, cs, lucideReact, waitasecond, sha256, papaparse, colors, agents, Bottleneck, OpenAI, QRCode) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react/jsx-runtime'), require('spacetrim'), require('path'), require('react'), require('crypto-js'), require('crypto-js/enc-hex'), require('crypto'), require('moment'), require('mime-types'), require('react-dom'), require('@monaco-editor/react'), require('destroyable'), require('katex'), require('react-dom/client'), require('showdown'), require('rxjs'), require('jspdf'), require('moment/locale/cs'), require('lucide-react'), require('waitasecond'), require('crypto-js/sha256'), require('papaparse'), require('colors'), require('@openai/agents'), require('bottleneck'), require('openai'), require('qrcode')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', 'react/jsx-runtime', 'spacetrim', 'path', 'react', 'crypto-js', 'crypto-js/enc-hex', 'crypto', 'moment', 'mime-types', 'react-dom', '@monaco-editor/react', 'destroyable', 'katex', 'react-dom/client', 'showdown', 'rxjs', 'jspdf', 'moment/locale/cs', 'lucide-react', 'waitasecond', 'crypto-js/sha256', 'papaparse', 'colors', '@openai/agents', 'bottleneck', 'openai', 'qrcode'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-components"] = {}, global.jsxRuntime, global.spacetrim, global.path, global.react, global.cryptoJs, global.hexEncoder, global.crypto$1, global.moment, global.mimeTypes, global.reactDom, global.MonacoEditor, global.destroyable, global.katex, global.client, global.showdown, global.rxjs, global.jspdf, null, global.lucideReact, global.waitasecond, global.sha256, global.papaparse, global.colors, global.agents, global.Bottleneck, global.OpenAI, global.QRCode));
5
+ })(this, (function (exports, jsxRuntime, spacetrim, path, react, cryptoJs, hexEncoder, crypto$1, moment, mimeTypes, reactDom, MonacoEditor, destroyable, katex, client, showdown, rxjs, jspdf, cs, lucideReact, waitasecond, sha256, papaparse, colors, agents, Bottleneck, OpenAI, QRCode) { 'use strict';
6
6
 
7
7
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
8
 
@@ -30,7 +30,7 @@
30
30
  * @generated
31
31
  * @see https://github.com/webgptorg/promptbook
32
32
  */
33
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-64';
33
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-66';
34
34
  /**
35
35
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
36
36
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -963,6 +963,29 @@
963
963
  }
964
964
  // TODO: Maybe implement by mix+hsl
965
965
 
966
+ /**
967
+ * Relative directory name without the `./` prefix for Git ignore rules and glob patterns.
968
+ *
969
+ * @private internal utility for Promptbook-owned temp files
970
+ */
971
+ const PROMPTBOOK_TEMP_DIRECTORY_NAME = '.promptbook';
972
+ /**
973
+ * Builds one project-relative path inside the shared Promptbook working directory.
974
+ *
975
+ * @private internal utility for Promptbook-owned temp files
976
+ */
977
+ function getPromptbookTempPath(...pathSegments) {
978
+ return `./${getPromptbookTempPosixPath(...pathSegments)}`;
979
+ }
980
+ /**
981
+ * Builds one POSIX path fragment inside the shared Promptbook working directory for globs and generated text files.
982
+ *
983
+ * @private internal utility for Promptbook-owned temp files
984
+ */
985
+ function getPromptbookTempPosixPath(...pathSegments) {
986
+ return path.posix.join(PROMPTBOOK_TEMP_DIRECTORY_NAME, ...pathSegments);
987
+ }
988
+
966
989
  /**
967
990
  * Returns the same value that is passed as argument.
968
991
  * No side effects.
@@ -1165,7 +1188,6 @@
1165
1188
  */
1166
1189
  const DEFAULT_MAX_EXECUTION_ATTEMPTS = 7; // <- TODO: [🤹‍♂️]
1167
1190
  // <- TODO: [🕝] Make also `AGENTS_DIRNAME_ALTERNATIVES`
1168
- // TODO: Just `.promptbook` in config, hardcode subfolders like `download-cache` or `execution-cache`
1169
1191
  /**
1170
1192
  * Where to store the temporary downloads
1171
1193
  *
@@ -1173,7 +1195,15 @@
1173
1195
  *
1174
1196
  * @public exported from `@promptbook/core`
1175
1197
  */
1176
- const DEFAULT_DOWNLOAD_CACHE_DIRNAME = './.promptbook/download-cache';
1198
+ const DEFAULT_DOWNLOAD_CACHE_DIRNAME = getPromptbookTempPath('download-cache');
1199
+ /**
1200
+ * Where to store the cache of executions for promptbook CLI
1201
+ *
1202
+ * Note: When the folder does not exist, it is created recursively
1203
+ *
1204
+ * @public exported from `@promptbook/core`
1205
+ */
1206
+ getPromptbookTempPath('execution-cache');
1177
1207
  /**
1178
1208
  * Where to store the scrape cache
1179
1209
  *
@@ -1181,7 +1211,7 @@
1181
1211
  *
1182
1212
  * @public exported from `@promptbook/core`
1183
1213
  */
1184
- const DEFAULT_SCRAPE_CACHE_DIRNAME = './.promptbook/scrape-cache';
1214
+ const DEFAULT_SCRAPE_CACHE_DIRNAME = getPromptbookTempPath('scrape-cache');
1185
1215
  /**
1186
1216
  * Default remote server URL for the Promptbook
1187
1217
  *
@@ -2815,6 +2845,177 @@
2815
2845
  context.restore();
2816
2846
  }
2817
2847
 
2848
+ /* eslint-disable no-magic-numbers */
2849
+ /**
2850
+ * Builds the seeded six-face texture pack used by the Minecraft-style head cuboid.
2851
+ *
2852
+ * @param random Seeded random generator.
2853
+ * @param palette Derived avatar palette.
2854
+ * @param hasHeadband Whether the generated avatar should include a colored headband.
2855
+ * @returns Head cuboid textures.
2856
+ *
2857
+ * @private helper of the Minecraft avatar visuals
2858
+ */
2859
+ function createMinecraftHeadTextures(random, palette, hasHeadband) {
2860
+ const faceTexture = createMinecraftFaceTexture(random, palette, hasHeadband);
2861
+ const hairColor = random() < 0.5 ? palette.primary : palette.secondary;
2862
+ const skinColor = palette.highlight;
2863
+ const headbandColor = hasHeadband ? palette.accent : hairColor;
2864
+ const sideTexture = createFilledTexture(skinColor);
2865
+ const backTexture = createFilledTexture(skinColor);
2866
+ const topTexture = createFilledTexture(hairColor);
2867
+ const bottomTexture = createFilledTexture(`${palette.shadow}cc`);
2868
+ fillTextureRect(sideTexture, 0, 0, 8, 3, hairColor);
2869
+ fillTextureRect(backTexture, 0, 0, 8, 5, hairColor);
2870
+ fillTextureRect(backTexture, 1, 5, 6, 1, hairColor);
2871
+ if (hasHeadband) {
2872
+ fillTextureRect(sideTexture, 0, 2, 8, 1, headbandColor);
2873
+ fillTextureRect(backTexture, 0, 2, 8, 1, headbandColor);
2874
+ fillTextureRect(topTexture, 0, 4, 8, 1, headbandColor);
2875
+ }
2876
+ sideTexture[4][4] = `${palette.shadow}99`;
2877
+ sideTexture[5][4] = `${palette.shadow}cc`;
2878
+ backTexture[6][2] = `${palette.shadow}99`;
2879
+ backTexture[6][5] = `${palette.shadow}99`;
2880
+ return {
2881
+ front: faceTexture,
2882
+ back: backTexture,
2883
+ left: sideTexture,
2884
+ right: mirrorMinecraftTexture(sideTexture),
2885
+ top: topTexture,
2886
+ bottom: bottomTexture,
2887
+ };
2888
+ }
2889
+ /**
2890
+ * Builds the seeded six-face texture pack used by the Minecraft-style torso cuboid.
2891
+ *
2892
+ * @param random Seeded random generator.
2893
+ * @param palette Derived avatar palette.
2894
+ * @returns Torso cuboid textures.
2895
+ *
2896
+ * @private helper of the Minecraft avatar visuals
2897
+ */
2898
+ function createMinecraftTorsoTextures(random, palette) {
2899
+ const frontTexture = createMinecraftShirtTexture(random, palette);
2900
+ const sideTexture = createFilledTexture(palette.primary);
2901
+ const backTexture = createFilledTexture(palette.primary);
2902
+ const topTexture = createFilledTexture(`${palette.highlight}dd`);
2903
+ const bottomTexture = createFilledTexture(`${palette.shadow}dd`);
2904
+ const stripeColor = random() < 0.5 ? palette.secondary : palette.highlight;
2905
+ fillTextureRect(sideTexture, 0, 0, 8, 2, palette.shadow);
2906
+ fillTextureRect(backTexture, 0, 0, 8, 2, palette.shadow);
2907
+ fillTextureRect(backTexture, 3, 2, 2, 6, stripeColor);
2908
+ fillTextureRect(sideTexture, 4, 2, 1, 6, stripeColor);
2909
+ fillTextureRect(topTexture, 0, 0, 8, 2, palette.shadow);
2910
+ fillTextureRect(topTexture, 2, 2, 4, 4, stripeColor);
2911
+ return {
2912
+ front: frontTexture,
2913
+ back: backTexture,
2914
+ left: sideTexture,
2915
+ right: mirrorMinecraftTexture(sideTexture),
2916
+ top: topTexture,
2917
+ bottom: bottomTexture,
2918
+ };
2919
+ }
2920
+ /**
2921
+ * Mirrors one Minecraft texture horizontally.
2922
+ *
2923
+ * @param texture Source texture.
2924
+ * @returns Mirrored texture copy.
2925
+ *
2926
+ * @private helper of the Minecraft avatar visuals
2927
+ */
2928
+ function mirrorMinecraftTexture(texture) {
2929
+ return texture.map((row) => [...row].reverse());
2930
+ }
2931
+ /**
2932
+ * Creates the front-face pixel texture for the cube head.
2933
+ *
2934
+ * @param random Seeded random generator.
2935
+ * @param palette Derived avatar palette.
2936
+ * @param hasHeadband Whether the avatar should render a headband row.
2937
+ * @returns 8x8 pixel texture.
2938
+ *
2939
+ * @private helper of the Minecraft avatar visuals
2940
+ */
2941
+ function createMinecraftFaceTexture(random, palette, hasHeadband) {
2942
+ const texture = createFilledTexture(palette.highlight);
2943
+ const hairlineColor = random() < 0.5 ? palette.primary : palette.secondary;
2944
+ const cheekColor = random() < 0.5 ? `${palette.accent}bb` : `${palette.secondary}bb`;
2945
+ fillTextureRect(texture, 0, 0, 8, 2, hairlineColor);
2946
+ texture[2][0] = hairlineColor;
2947
+ texture[2][7] = hairlineColor;
2948
+ texture[3][0] = hairlineColor;
2949
+ texture[3][7] = hairlineColor;
2950
+ if (hasHeadband) {
2951
+ fillTextureRect(texture, 0, 2, 8, 1, palette.accent);
2952
+ }
2953
+ texture[3][2] = palette.ink;
2954
+ texture[3][5] = palette.ink;
2955
+ texture[4][2] = '#ffffff';
2956
+ texture[4][5] = '#ffffff';
2957
+ texture[5][1] = cheekColor;
2958
+ texture[5][6] = cheekColor;
2959
+ texture[5][3] = palette.shadow;
2960
+ texture[5][4] = palette.shadow;
2961
+ texture[6][3] = palette.shadow;
2962
+ texture[6][4] = palette.shadow;
2963
+ return texture;
2964
+ }
2965
+ /**
2966
+ * Creates the front-face pixel texture for the torso.
2967
+ *
2968
+ * @param random Seeded random generator.
2969
+ * @param palette Derived avatar palette.
2970
+ * @returns 8x8 torso texture.
2971
+ *
2972
+ * @private helper of the Minecraft avatar visuals
2973
+ */
2974
+ function createMinecraftShirtTexture(random, palette) {
2975
+ const texture = createFilledTexture(palette.primary);
2976
+ const stripeColor = random() < 0.5 ? palette.secondary : palette.highlight;
2977
+ fillTextureRect(texture, 0, 0, 8, 2, palette.shadow);
2978
+ for (let rowIndex = 2; rowIndex < 8; rowIndex++) {
2979
+ texture[rowIndex][3] = stripeColor;
2980
+ texture[rowIndex][4] = stripeColor;
2981
+ }
2982
+ texture[4][1] = palette.accent;
2983
+ texture[4][6] = palette.accent;
2984
+ texture[5][2] = palette.highlight;
2985
+ texture[5][5] = palette.highlight;
2986
+ return texture;
2987
+ }
2988
+ /**
2989
+ * Creates one solid-color 8x8 Minecraft texture.
2990
+ *
2991
+ * @param color Fill color.
2992
+ * @returns Filled 8x8 texture.
2993
+ *
2994
+ * @private helper of the Minecraft avatar visuals
2995
+ */
2996
+ function createFilledTexture(color) {
2997
+ return Array.from({ length: 8 }, () => Array.from({ length: 8 }, () => color));
2998
+ }
2999
+ /**
3000
+ * Fills one rectangular area inside a mutable Minecraft texture.
3001
+ *
3002
+ * @param texture Mutable target texture.
3003
+ * @param x Left texture coordinate.
3004
+ * @param y Top texture coordinate.
3005
+ * @param width Rectangle width.
3006
+ * @param height Rectangle height.
3007
+ * @param color Fill color.
3008
+ *
3009
+ * @private helper of the Minecraft avatar visuals
3010
+ */
3011
+ function fillTextureRect(texture, x, y, width, height, color) {
3012
+ for (let rowIndex = y; rowIndex < y + height; rowIndex++) {
3013
+ for (let columnIndex = x; columnIndex < x + width; columnIndex++) {
3014
+ texture[rowIndex][columnIndex] = color;
3015
+ }
3016
+ }
3017
+ }
3018
+
2818
3019
  /* eslint-disable no-magic-numbers */
2819
3020
  /**
2820
3021
  * Minecraft-style 3D avatar visual.
@@ -2839,8 +3040,8 @@
2839
3040
  const bodyX = size * 0.33;
2840
3041
  const bodyY = headY + headSize * 0.96;
2841
3042
  const hasHeadband = random() < 0.5;
2842
- const faceTexture = createMinecraftFaceTexture(createRandom('minecraft-face'), palette, hasHeadband);
2843
- const shirtTexture = createMinecraftShirtTexture(createRandom('minecraft-shirt'), palette);
3043
+ const headTextures = createMinecraftHeadTextures(createRandom('minecraft-face'), palette, hasHeadband);
3044
+ const torsoTextures = createMinecraftTorsoTextures(createRandom('minecraft-shirt'), palette);
2844
3045
  drawAvatarFrame(context, size, palette);
2845
3046
  const spotlight = context.createRadialGradient(size * 0.5, size * 0.18, size * 0.05, size * 0.5, size * 0.18, size * 0.5);
2846
3047
  spotlight.addColorStop(0, `${palette.highlight}66`);
@@ -2860,7 +3061,7 @@
2860
3061
  width: bodyWidth,
2861
3062
  height: bodyHeight,
2862
3063
  depth: bodyDepth,
2863
- frontTexture: shirtTexture,
3064
+ frontTexture: torsoTextures.front,
2864
3065
  topColor: `${palette.highlight}cc`,
2865
3066
  sideColor: `${palette.secondary}dd`,
2866
3067
  outlineColor: `${palette.shadow}aa`,
@@ -2871,7 +3072,7 @@
2871
3072
  width: headSize,
2872
3073
  height: headSize,
2873
3074
  depth,
2874
- frontTexture: faceTexture,
3075
+ frontTexture: headTextures.front,
2875
3076
  topColor: `${palette.highlight}ee`,
2876
3077
  sideColor: `${palette.secondary}ee`,
2877
3078
  outlineColor: `${palette.shadow}cc`,
@@ -2936,72 +3137,505 @@
2936
3137
  context.closePath();
2937
3138
  context.stroke();
2938
3139
  }
3140
+
3141
+ /* eslint-disable no-magic-numbers */
2939
3142
  /**
2940
- * Creates the front-face pixel texture for the cube head.
3143
+ * Fixed scene camera distance used for the proper-3D projection.
2941
3144
  *
2942
- * @param random Seeded random generator.
3145
+ * @private helper of `minecraft2AvatarVisual`
3146
+ */
3147
+ const CAMERA_DISTANCE_RATIO = 1.4;
3148
+ /**
3149
+ * Shared light direction used to shade projected cuboid faces.
3150
+ *
3151
+ * @private helper of `minecraft2AvatarVisual`
3152
+ */
3153
+ const LIGHT_DIRECTION = normalizeVector3({
3154
+ x: 0.4,
3155
+ y: -0.65,
3156
+ z: 0.92,
3157
+ });
3158
+ /**
3159
+ * Minecraft 3D 2 avatar visual.
3160
+ *
3161
+ * @private built-in avatar visual
3162
+ */
3163
+ const minecraft2AvatarVisual = {
3164
+ id: 'minecraft2',
3165
+ title: 'Minecraft 3D 2',
3166
+ description: 'Proper 3D Minecraft-style portrait with textured cuboids and pointer-driven head turns.',
3167
+ isAnimated: true,
3168
+ supportsPointerTracking: true,
3169
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
3170
+ const spotlightY = size * 0.22;
3171
+ const headRandom = createRandom('minecraft2-head');
3172
+ const hasHeadband = headRandom() < 0.5;
3173
+ const headTextures = createMinecraftHeadTextures(createRandom('minecraft2-head-textures'), palette, hasHeadband);
3174
+ const torsoTextures = createMinecraftTorsoTextures(createRandom('minecraft2-body-textures'), palette);
3175
+ const bob = Math.sin(timeMs / 880) * size * 0.014;
3176
+ const bodyYaw = -0.24 + Math.sin(timeMs / 2300) * 0.06 + interaction.bodyOffsetX * 0.16;
3177
+ const bodyPitch = -0.12 + Math.cos(timeMs / 2800) * 0.02 - interaction.bodyOffsetY * 0.06;
3178
+ const headYaw = -0.18 + Math.sin(timeMs / 1900 + 0.6) * 0.05 + interaction.gazeX * 0.62;
3179
+ const headPitch = -0.12 + Math.cos(timeMs / 2400 + 1.1) * 0.03 - interaction.gazeY * 0.38;
3180
+ const sceneCenterX = size * 0.5;
3181
+ const sceneCenterY = size * 0.57;
3182
+ const bodyWidth = size * 0.225;
3183
+ const bodyHeight = size * 0.245;
3184
+ const bodyDepth = size * 0.145;
3185
+ const headSize = size * 0.24;
3186
+ const headLift = size * 0.205;
3187
+ const headForwardShift = interaction.intensity * size * 0.018;
3188
+ const sceneCuboids = [
3189
+ {
3190
+ center: {
3191
+ x: interaction.bodyOffsetX * size * 0.026,
3192
+ y: size * 0.05 + interaction.bodyOffsetY * size * 0.018 + bob,
3193
+ z: 0,
3194
+ },
3195
+ width: bodyWidth,
3196
+ height: bodyHeight,
3197
+ depth: bodyDepth,
3198
+ rotationX: bodyPitch,
3199
+ rotationY: bodyYaw,
3200
+ textures: torsoTextures,
3201
+ outlineColor: `${palette.shadow}cc`,
3202
+ },
3203
+ {
3204
+ center: {
3205
+ x: interaction.bodyOffsetX * size * 0.018 + interaction.gazeX * size * 0.016,
3206
+ y: -headLift + bob * 1.15,
3207
+ z: headForwardShift,
3208
+ },
3209
+ width: headSize,
3210
+ height: headSize,
3211
+ depth: headSize,
3212
+ rotationX: headPitch,
3213
+ rotationY: headYaw,
3214
+ textures: headTextures,
3215
+ outlineColor: `${palette.shadow}dd`,
3216
+ },
3217
+ ];
3218
+ const visibleFaces = sceneCuboids
3219
+ .flatMap((cuboid) => resolveVisibleCuboidFaces(cuboid, size, sceneCenterX, sceneCenterY))
3220
+ .sort((firstFace, secondFace) => firstFace.averageDepth - secondFace.averageDepth);
3221
+ drawAvatarFrame(context, size, palette);
3222
+ drawMinecraftBackdrop(context, size, palette, sceneCenterX, spotlightY, interaction, timeMs);
3223
+ drawMinecraftShadow(context, size, palette, interaction, timeMs);
3224
+ for (const visibleFace of visibleFaces) {
3225
+ drawTexturedProjectedFace(context, visibleFace);
3226
+ }
3227
+ },
3228
+ };
3229
+ /**
3230
+ * Draws the shared background atmosphere behind the Minecraft 3D 2 portrait.
3231
+ *
3232
+ * @param context Canvas 2D context.
3233
+ * @param size Canvas size in CSS pixels.
2943
3234
  * @param palette Derived avatar palette.
2944
- * @param hasHeadband Whether the avatar should render a headband row.
2945
- * @returns 8x8 pixel texture.
3235
+ * @param sceneCenterX Horizontal scene center.
3236
+ * @param spotlightY Vertical spotlight anchor.
3237
+ * @param interaction Smoothed pointer-aware interaction state.
3238
+ * @param timeMs Current animation time in milliseconds.
2946
3239
  *
2947
- * @private helper of `minecraftAvatarVisual`
3240
+ * @private helper of `minecraft2AvatarVisual`
2948
3241
  */
2949
- function createMinecraftFaceTexture(random, palette, hasHeadband) {
2950
- const texture = Array.from({ length: 8 }, () => Array.from({ length: 8 }, () => palette.highlight));
2951
- const hairlineColor = random() < 0.5 ? palette.primary : palette.secondary;
2952
- const cheekColor = random() < 0.5 ? `${palette.accent}bb` : `${palette.secondary}bb`;
2953
- for (let rowIndex = 0; rowIndex < 2; rowIndex++) {
2954
- for (let columnIndex = 0; columnIndex < 8; columnIndex++) {
2955
- texture[rowIndex][columnIndex] = hairlineColor;
3242
+ function drawMinecraftBackdrop(context, size, palette, sceneCenterX, spotlightY, interaction, timeMs) {
3243
+ const spotlightGradient = context.createRadialGradient(sceneCenterX + interaction.gazeX * size * 0.08, spotlightY + interaction.gazeY * size * 0.05, size * 0.03, sceneCenterX, spotlightY, size * 0.52);
3244
+ spotlightGradient.addColorStop(0, `${palette.highlight}66`);
3245
+ spotlightGradient.addColorStop(0.42, `${palette.accent}1d`);
3246
+ spotlightGradient.addColorStop(1, `${palette.highlight}00`);
3247
+ context.fillStyle = spotlightGradient;
3248
+ context.fillRect(0, 0, size, size);
3249
+ const rimGradient = context.createLinearGradient(0, size * 0.14, 0, size * 0.92);
3250
+ rimGradient.addColorStop(0, `${palette.highlight}12`);
3251
+ rimGradient.addColorStop(0.55, `${palette.secondary}0a`);
3252
+ rimGradient.addColorStop(1, `${palette.shadow}00`);
3253
+ context.fillStyle = rimGradient;
3254
+ context.fillRect(0, 0, size, size);
3255
+ context.save();
3256
+ context.globalAlpha = 0.08 + interaction.intensity * 0.04;
3257
+ context.fillStyle = palette.highlight;
3258
+ context.beginPath();
3259
+ 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);
3260
+ context.fill();
3261
+ context.restore();
3262
+ }
3263
+ /**
3264
+ * Draws the soft floor shadow used to anchor the cuboids in the frame.
3265
+ *
3266
+ * @param context Canvas 2D context.
3267
+ * @param size Canvas size in CSS pixels.
3268
+ * @param palette Derived avatar palette.
3269
+ * @param interaction Smoothed pointer-aware interaction state.
3270
+ * @param timeMs Current animation time in milliseconds.
3271
+ *
3272
+ * @private helper of `minecraft2AvatarVisual`
3273
+ */
3274
+ function drawMinecraftShadow(context, size, palette, interaction, timeMs) {
3275
+ context.save();
3276
+ context.fillStyle = `${palette.shadow}66`;
3277
+ context.filter = `blur(${size * 0.02}px)`;
3278
+ context.beginPath();
3279
+ 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);
3280
+ context.fill();
3281
+ context.restore();
3282
+ }
3283
+ /**
3284
+ * Resolves all visible projected faces for one scene cuboid.
3285
+ *
3286
+ * @param cuboid Scene cuboid definition.
3287
+ * @param size Canvas size in CSS pixels.
3288
+ * @param sceneCenterX Horizontal scene center.
3289
+ * @param sceneCenterY Vertical scene center.
3290
+ * @returns Visible faces sorted later by depth.
3291
+ *
3292
+ * @private helper of `minecraft2AvatarVisual`
3293
+ */
3294
+ function resolveVisibleCuboidFaces(cuboid, size, sceneCenterX, sceneCenterY) {
3295
+ const halfWidth = cuboid.width / 2;
3296
+ const halfHeight = cuboid.height / 2;
3297
+ const halfDepth = cuboid.depth / 2;
3298
+ const faceDefinitions = [
3299
+ {
3300
+ texture: cuboid.textures.front,
3301
+ corners: [
3302
+ { x: -halfWidth, y: -halfHeight, z: halfDepth },
3303
+ { x: halfWidth, y: -halfHeight, z: halfDepth },
3304
+ { x: halfWidth, y: halfHeight, z: halfDepth },
3305
+ { x: -halfWidth, y: halfHeight, z: halfDepth },
3306
+ ],
3307
+ },
3308
+ {
3309
+ texture: cuboid.textures.back,
3310
+ corners: [
3311
+ { x: halfWidth, y: -halfHeight, z: -halfDepth },
3312
+ { x: -halfWidth, y: -halfHeight, z: -halfDepth },
3313
+ { x: -halfWidth, y: halfHeight, z: -halfDepth },
3314
+ { x: halfWidth, y: halfHeight, z: -halfDepth },
3315
+ ],
3316
+ },
3317
+ {
3318
+ texture: cuboid.textures.right,
3319
+ corners: [
3320
+ { x: halfWidth, y: -halfHeight, z: halfDepth },
3321
+ { x: halfWidth, y: -halfHeight, z: -halfDepth },
3322
+ { x: halfWidth, y: halfHeight, z: -halfDepth },
3323
+ { x: halfWidth, y: halfHeight, z: halfDepth },
3324
+ ],
3325
+ },
3326
+ {
3327
+ texture: cuboid.textures.left,
3328
+ corners: [
3329
+ { x: -halfWidth, y: -halfHeight, z: -halfDepth },
3330
+ { x: -halfWidth, y: -halfHeight, z: halfDepth },
3331
+ { x: -halfWidth, y: halfHeight, z: halfDepth },
3332
+ { x: -halfWidth, y: halfHeight, z: -halfDepth },
3333
+ ],
3334
+ },
3335
+ {
3336
+ texture: cuboid.textures.top,
3337
+ corners: [
3338
+ { x: -halfWidth, y: -halfHeight, z: -halfDepth },
3339
+ { x: halfWidth, y: -halfHeight, z: -halfDepth },
3340
+ { x: halfWidth, y: -halfHeight, z: halfDepth },
3341
+ { x: -halfWidth, y: -halfHeight, z: halfDepth },
3342
+ ],
3343
+ },
3344
+ {
3345
+ texture: cuboid.textures.bottom,
3346
+ corners: [
3347
+ { x: -halfWidth, y: halfHeight, z: halfDepth },
3348
+ { x: halfWidth, y: halfHeight, z: halfDepth },
3349
+ { x: halfWidth, y: halfHeight, z: -halfDepth },
3350
+ { x: -halfWidth, y: halfHeight, z: -halfDepth },
3351
+ ],
3352
+ },
3353
+ ];
3354
+ const visibleFaces = faceDefinitions
3355
+ .map((faceDefinition) => {
3356
+ const transformedCorners = faceDefinition.corners.map((corner) => transformScenePoint(corner, cuboid.center, cuboid.rotationX, cuboid.rotationY));
3357
+ const faceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
3358
+ if (faceNormal.z <= 0.02) {
3359
+ return null;
2956
3360
  }
3361
+ const projectedCorners = transformedCorners.map((corner) => projectScenePoint(corner, size, sceneCenterX, sceneCenterY));
3362
+ return {
3363
+ corners: projectedCorners,
3364
+ texture: faceDefinition.texture,
3365
+ averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
3366
+ lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION), -1, 1),
3367
+ outlineColor: cuboid.outlineColor,
3368
+ };
3369
+ });
3370
+ return visibleFaces.filter((visibleFace) => visibleFace !== null);
3371
+ }
3372
+ /**
3373
+ * Draws one projected textured face by tessellating its texture cells into quads.
3374
+ *
3375
+ * @param context Canvas 2D context.
3376
+ * @param face Visible projected face.
3377
+ *
3378
+ * @private helper of `minecraft2AvatarVisual`
3379
+ */
3380
+ function drawTexturedProjectedFace(context, face) {
3381
+ var _a;
3382
+ const rows = face.texture.length;
3383
+ const columns = ((_a = face.texture[0]) === null || _a === void 0 ? void 0 : _a.length) || 0;
3384
+ if (rows === 0 || columns === 0) {
3385
+ return;
2957
3386
  }
2958
- texture[2][0] = hairlineColor;
2959
- texture[2][7] = hairlineColor;
2960
- texture[3][0] = hairlineColor;
2961
- texture[3][7] = hairlineColor;
2962
- if (hasHeadband) {
2963
- for (let columnIndex = 0; columnIndex < 8; columnIndex++) {
2964
- texture[2][columnIndex] = palette.accent;
3387
+ for (let rowIndex = 0; rowIndex < rows; rowIndex++) {
3388
+ for (let columnIndex = 0; columnIndex < columns; columnIndex++) {
3389
+ const startX = columnIndex / columns;
3390
+ const endX = (columnIndex + 1) / columns;
3391
+ const startY = rowIndex / rows;
3392
+ const endY = (rowIndex + 1) / rows;
3393
+ drawProjectedQuad(context, [
3394
+ interpolateProjectedQuad(face.corners, startX, startY),
3395
+ interpolateProjectedQuad(face.corners, endX, startY),
3396
+ interpolateProjectedQuad(face.corners, endX, endY),
3397
+ interpolateProjectedQuad(face.corners, startX, endY),
3398
+ ], face.texture[rowIndex][columnIndex]);
2965
3399
  }
2966
3400
  }
2967
- texture[3][2] = palette.ink;
2968
- texture[3][5] = palette.ink;
2969
- texture[4][2] = '#ffffff';
2970
- texture[4][5] = '#ffffff';
2971
- texture[5][1] = cheekColor;
2972
- texture[5][6] = cheekColor;
2973
- texture[5][3] = palette.shadow;
2974
- texture[5][4] = palette.shadow;
2975
- texture[6][3] = palette.shadow;
2976
- texture[6][4] = palette.shadow;
2977
- return texture;
3401
+ if (face.lightIntensity > 0) {
3402
+ drawProjectedQuad(context, face.corners, `rgba(255, 255, 255, ${0.15 * face.lightIntensity})`);
3403
+ }
3404
+ else if (face.lightIntensity < 0) {
3405
+ drawProjectedQuad(context, face.corners, `rgba(0, 0, 0, ${0.22 * Math.abs(face.lightIntensity)})`);
3406
+ }
3407
+ context.save();
3408
+ context.beginPath();
3409
+ context.moveTo(face.corners[0].x, face.corners[0].y);
3410
+ for (let cornerIndex = 1; cornerIndex < face.corners.length; cornerIndex++) {
3411
+ context.lineTo(face.corners[cornerIndex].x, face.corners[cornerIndex].y);
3412
+ }
3413
+ context.closePath();
3414
+ context.strokeStyle = face.outlineColor;
3415
+ context.lineWidth = Math.max(1.1, getProjectedQuadPerimeter(face.corners) * 0.0045);
3416
+ context.lineJoin = 'round';
3417
+ context.stroke();
3418
+ context.restore();
2978
3419
  }
2979
3420
  /**
2980
- * Creates the front-face pixel texture for the torso.
3421
+ * Draws one filled projected quad.
2981
3422
  *
2982
- * @param random Seeded random generator.
2983
- * @param palette Derived avatar palette.
2984
- * @returns 8x8 torso texture.
3423
+ * @param context Canvas 2D context.
3424
+ * @param corners Quad corners in clockwise order.
3425
+ * @param fillStyle CSS fill style.
2985
3426
  *
2986
- * @private helper of `minecraftAvatarVisual`
3427
+ * @private helper of `minecraft2AvatarVisual`
2987
3428
  */
2988
- function createMinecraftShirtTexture(random, palette) {
2989
- const texture = Array.from({ length: 8 }, () => Array.from({ length: 8 }, () => palette.primary));
2990
- const stripeColor = random() < 0.5 ? palette.secondary : palette.highlight;
2991
- for (let rowIndex = 0; rowIndex < 2; rowIndex++) {
2992
- for (let columnIndex = 0; columnIndex < 8; columnIndex++) {
2993
- texture[rowIndex][columnIndex] = palette.shadow;
2994
- }
3429
+ function drawProjectedQuad(context, corners, fillStyle) {
3430
+ context.beginPath();
3431
+ context.moveTo(corners[0].x, corners[0].y);
3432
+ context.lineTo(corners[1].x, corners[1].y);
3433
+ context.lineTo(corners[2].x, corners[2].y);
3434
+ context.lineTo(corners[3].x, corners[3].y);
3435
+ context.closePath();
3436
+ context.fillStyle = fillStyle;
3437
+ context.fill();
3438
+ }
3439
+ /**
3440
+ * Interpolates one point inside a projected quad across both quad axes.
3441
+ *
3442
+ * @param corners Quad corners in clockwise order.
3443
+ * @param horizontalRatio Horizontal ratio in the range `[0, 1]`.
3444
+ * @param verticalRatio Vertical ratio in the range `[0, 1]`.
3445
+ * @returns Interpolated projected point.
3446
+ *
3447
+ * @private helper of `minecraft2AvatarVisual`
3448
+ */
3449
+ function interpolateProjectedQuad(corners, horizontalRatio, verticalRatio) {
3450
+ const topPoint = interpolateProjectedPoint(corners[0], corners[1], horizontalRatio);
3451
+ const bottomPoint = interpolateProjectedPoint(corners[3], corners[2], horizontalRatio);
3452
+ return interpolateProjectedPoint(topPoint, bottomPoint, verticalRatio);
3453
+ }
3454
+ /**
3455
+ * Interpolates between two projected points.
3456
+ *
3457
+ * @param startPoint Start point.
3458
+ * @param endPoint End point.
3459
+ * @param ratio Interpolation ratio in the range `[0, 1]`.
3460
+ * @returns Interpolated projected point.
3461
+ *
3462
+ * @private helper of `minecraft2AvatarVisual`
3463
+ */
3464
+ function interpolateProjectedPoint(startPoint, endPoint, ratio) {
3465
+ return {
3466
+ x: startPoint.x + (endPoint.x - startPoint.x) * ratio,
3467
+ y: startPoint.y + (endPoint.y - startPoint.y) * ratio,
3468
+ z: startPoint.z + (endPoint.z - startPoint.z) * ratio,
3469
+ };
3470
+ }
3471
+ /**
3472
+ * Projects one rotated scene point into canvas coordinates.
3473
+ *
3474
+ * @param point Scene point.
3475
+ * @param size Canvas size in CSS pixels.
3476
+ * @param sceneCenterX Horizontal scene center.
3477
+ * @param sceneCenterY Vertical scene center.
3478
+ * @returns Projected point.
3479
+ *
3480
+ * @private helper of `minecraft2AvatarVisual`
3481
+ */
3482
+ function projectScenePoint(point, size, sceneCenterX, sceneCenterY) {
3483
+ const cameraDistance = size * CAMERA_DISTANCE_RATIO;
3484
+ const perspectiveScale = cameraDistance / Math.max(cameraDistance - point.z, cameraDistance * 0.35);
3485
+ return {
3486
+ x: sceneCenterX + point.x * perspectiveScale,
3487
+ y: sceneCenterY + point.y * perspectiveScale,
3488
+ z: point.z,
3489
+ };
3490
+ }
3491
+ /**
3492
+ * Applies the local cuboid rotations and translation to one scene point.
3493
+ *
3494
+ * @param localPoint Point in cuboid-local space.
3495
+ * @param center Cuboid center in scene space.
3496
+ * @param rotationX Cuboid pitch in radians.
3497
+ * @param rotationY Cuboid yaw in radians.
3498
+ * @returns Transformed scene-space point.
3499
+ *
3500
+ * @private helper of `minecraft2AvatarVisual`
3501
+ */
3502
+ function transformScenePoint(localPoint, center, rotationX, rotationY) {
3503
+ const yawedPoint = rotatePointAroundY(localPoint, rotationY);
3504
+ const pitchedPoint = rotatePointAroundX(yawedPoint, rotationX);
3505
+ return {
3506
+ x: center.x + pitchedPoint.x,
3507
+ y: center.y + pitchedPoint.y,
3508
+ z: center.z + pitchedPoint.z,
3509
+ };
3510
+ }
3511
+ /**
3512
+ * Rotates one point around the local Y axis.
3513
+ *
3514
+ * @param point Source point.
3515
+ * @param angle Rotation angle in radians.
3516
+ * @returns Rotated point.
3517
+ *
3518
+ * @private helper of `minecraft2AvatarVisual`
3519
+ */
3520
+ function rotatePointAroundY(point, angle) {
3521
+ const cosine = Math.cos(angle);
3522
+ const sine = Math.sin(angle);
3523
+ return {
3524
+ x: point.x * cosine + point.z * sine,
3525
+ y: point.y,
3526
+ z: -point.x * sine + point.z * cosine,
3527
+ };
3528
+ }
3529
+ /**
3530
+ * Rotates one point around the local X axis.
3531
+ *
3532
+ * @param point Source point.
3533
+ * @param angle Rotation angle in radians.
3534
+ * @returns Rotated point.
3535
+ *
3536
+ * @private helper of `minecraft2AvatarVisual`
3537
+ */
3538
+ function rotatePointAroundX(point, angle) {
3539
+ const cosine = Math.cos(angle);
3540
+ const sine = Math.sin(angle);
3541
+ return {
3542
+ x: point.x,
3543
+ y: point.y * cosine - point.z * sine,
3544
+ z: point.y * sine + point.z * cosine,
3545
+ };
3546
+ }
3547
+ /**
3548
+ * Subtracts one 3D point from another.
3549
+ *
3550
+ * @param leftPoint Left point.
3551
+ * @param rightPoint Right point.
3552
+ * @returns Difference vector.
3553
+ *
3554
+ * @private helper of `minecraft2AvatarVisual`
3555
+ */
3556
+ function subtractPoint3D(leftPoint, rightPoint) {
3557
+ return {
3558
+ x: leftPoint.x - rightPoint.x,
3559
+ y: leftPoint.y - rightPoint.y,
3560
+ z: leftPoint.z - rightPoint.z,
3561
+ };
3562
+ }
3563
+ /**
3564
+ * Computes the 3D cross product of two vectors.
3565
+ *
3566
+ * @param leftVector Left vector.
3567
+ * @param rightVector Right vector.
3568
+ * @returns Cross product.
3569
+ *
3570
+ * @private helper of `minecraft2AvatarVisual`
3571
+ */
3572
+ function crossProduct3D(leftVector, rightVector) {
3573
+ return {
3574
+ x: leftVector.y * rightVector.z - leftVector.z * rightVector.y,
3575
+ y: leftVector.z * rightVector.x - leftVector.x * rightVector.z,
3576
+ z: leftVector.x * rightVector.y - leftVector.y * rightVector.x,
3577
+ };
3578
+ }
3579
+ /**
3580
+ * Computes the 3D dot product of two vectors.
3581
+ *
3582
+ * @param leftVector Left vector.
3583
+ * @param rightVector Right vector.
3584
+ * @returns Dot product.
3585
+ *
3586
+ * @private helper of `minecraft2AvatarVisual`
3587
+ */
3588
+ function dotProduct3D(leftVector, rightVector) {
3589
+ return leftVector.x * rightVector.x + leftVector.y * rightVector.y + leftVector.z * rightVector.z;
3590
+ }
3591
+ /**
3592
+ * Normalizes one 3D vector while keeping zero vectors stable.
3593
+ *
3594
+ * @param vector Source vector.
3595
+ * @returns Normalized vector.
3596
+ *
3597
+ * @private helper of `minecraft2AvatarVisual`
3598
+ */
3599
+ function normalizeVector3(vector) {
3600
+ const length = Math.hypot(vector.x, vector.y, vector.z);
3601
+ if (length === 0) {
3602
+ return vector;
2995
3603
  }
2996
- for (let rowIndex = 2; rowIndex < 8; rowIndex++) {
2997
- texture[rowIndex][3] = stripeColor;
2998
- texture[rowIndex][4] = stripeColor;
3604
+ return {
3605
+ x: vector.x / length,
3606
+ y: vector.y / length,
3607
+ z: vector.z / length,
3608
+ };
3609
+ }
3610
+ /**
3611
+ * Clamps one number into the provided range.
3612
+ *
3613
+ * @param value Input value.
3614
+ * @param minimumValue Inclusive lower bound.
3615
+ * @param maximumValue Inclusive upper bound.
3616
+ * @returns Clamped value.
3617
+ *
3618
+ * @private helper of `minecraft2AvatarVisual`
3619
+ */
3620
+ function clampNumber$1(value, minimumValue, maximumValue) {
3621
+ return Math.min(maximumValue, Math.max(minimumValue, value));
3622
+ }
3623
+ /**
3624
+ * Measures the perimeter of one projected quad.
3625
+ *
3626
+ * @param corners Quad corners.
3627
+ * @returns Perimeter length.
3628
+ *
3629
+ * @private helper of `minecraft2AvatarVisual`
3630
+ */
3631
+ function getProjectedQuadPerimeter(corners) {
3632
+ let perimeter = 0;
3633
+ for (let cornerIndex = 0; cornerIndex < corners.length; cornerIndex++) {
3634
+ const currentCorner = corners[cornerIndex];
3635
+ const nextCorner = corners[(cornerIndex + 1) % corners.length];
3636
+ perimeter += Math.hypot(nextCorner.x - currentCorner.x, nextCorner.y - currentCorner.y);
2999
3637
  }
3000
- texture[4][1] = palette.accent;
3001
- texture[4][6] = palette.accent;
3002
- texture[5][2] = palette.highlight;
3003
- texture[5][5] = palette.highlight;
3004
- return texture;
3638
+ return perimeter;
3005
3639
  }
3006
3640
 
3007
3641
  /* eslint-disable no-magic-numbers */
@@ -4606,6 +5240,7 @@
4606
5240
  octopus3AvatarVisual,
4607
5241
  asciiOctopusAvatarVisual,
4608
5242
  minecraftAvatarVisual,
5243
+ minecraft2AvatarVisual,
4609
5244
  fractalAvatarVisual,
4610
5245
  orbAvatarVisual,
4611
5246
  ];
@@ -10888,18 +11523,6 @@
10888
11523
  }
10889
11524
  // Note: [💞] Ignore a discrepancy between file name and entity name
10890
11525
 
10891
- /**
10892
- * Name of the tool used by agents to search configured `KNOWLEDGE` sources.
10893
- *
10894
- * @public exported from `@promptbook/core`
10895
- */
10896
- const KNOWLEDGE_SEARCH_TOOL_NAME = 'knowledge_search';
10897
- /**
10898
- * Title of the system-message section generated for `KNOWLEDGE` commitments.
10899
- *
10900
- * @private constant of `KnowledgeCommitmentDefinition`
10901
- */
10902
- const KNOWLEDGE_SEARCH_SYSTEM_SECTION_TITLE = 'Knowledge Search';
10903
11526
  /**
10904
11527
  * KNOWLEDGE commitment definition
10905
11528
  *
@@ -11021,17 +11644,9 @@
11021
11644
  knowledgeInfoEntries.push(`Knowledge Source Inline: ${inlineSource.filename} (derived from inline content and processed for retrieval during chat)`);
11022
11645
  }
11023
11646
  if (knowledgeInfoEntries.length === 0) {
11024
- return addKnowledgeSearchToolAndSystemSection(nextRequirements);
11647
+ return nextRequirements;
11025
11648
  }
11026
- return addKnowledgeSearchToolAndSystemSection(nextRequirements);
11027
- }
11028
- /**
11029
- * Gets human-readable titles for tool functions provided by this commitment.
11030
- */
11031
- getToolTitles() {
11032
- return {
11033
- [KNOWLEDGE_SEARCH_TOOL_NAME]: 'Knowledge search',
11034
- };
11649
+ return this.appendToSystemMessage(nextRequirements, knowledgeInfoEntries.join('\n'), '\n\n');
11035
11650
  }
11036
11651
  }
11037
11652
  /**
@@ -11045,128 +11660,6 @@
11045
11660
  const significantText = contentWithoutUrls.replace(/[\s.,!?;:'"`()[\]{}<>/-]+/g, '');
11046
11661
  return significantText.length > 0;
11047
11662
  }
11048
- /**
11049
- * Adds the shared `knowledge_search` tool definition and the consolidated system-message section.
11050
- *
11051
- * @param requirements - Requirements after one `KNOWLEDGE` commitment was applied.
11052
- * @returns Requirements with the knowledge search instructions and tool definition.
11053
- *
11054
- * @private internal utility of `KnowledgeCommitmentDefinition`
11055
- */
11056
- function addKnowledgeSearchToolAndSystemSection(requirements) {
11057
- const nextRequirements = addKnowledgeSearchTool(requirements);
11058
- const section = createKnowledgeSearchSystemSection(nextRequirements);
11059
- const sectionHeader = `## ${KNOWLEDGE_SEARCH_SYSTEM_SECTION_TITLE}`;
11060
- if (nextRequirements.systemMessage.includes(sectionHeader)) {
11061
- return {
11062
- ...nextRequirements,
11063
- systemMessage: nextRequirements.systemMessage.replace(new RegExp(`## ${KNOWLEDGE_SEARCH_SYSTEM_SECTION_TITLE.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?(?=\\n\\n##|$)`), section),
11064
- };
11065
- }
11066
- return {
11067
- ...nextRequirements,
11068
- systemMessage: nextRequirements.systemMessage.trim()
11069
- ? `${nextRequirements.systemMessage}\n\n${section}`
11070
- : section,
11071
- };
11072
- }
11073
- /**
11074
- * Adds the `knowledge_search` model tool when it is not already present.
11075
- *
11076
- * @param requirements - Current model requirements.
11077
- * @returns Requirements with the tool definition available to the model.
11078
- *
11079
- * @private internal utility of `KnowledgeCommitmentDefinition`
11080
- */
11081
- function addKnowledgeSearchTool(requirements) {
11082
- const existingTools = requirements.tools || [];
11083
- if (existingTools.some((tool) => tool.name === KNOWLEDGE_SEARCH_TOOL_NAME)) {
11084
- return requirements;
11085
- }
11086
- return {
11087
- ...requirements,
11088
- tools: [
11089
- ...existingTools,
11090
- {
11091
- name: KNOWLEDGE_SEARCH_TOOL_NAME,
11092
- description: spacetrim.spaceTrim(`
11093
- Search the agent's configured knowledge sources and return relevant excerpts with citation ids.
11094
- Use this before answering questions that may depend on the agent's KNOWLEDGE commitments.
11095
- `),
11096
- parameters: {
11097
- type: 'object',
11098
- properties: {
11099
- query: {
11100
- type: 'string',
11101
- description: 'The natural-language search query for the knowledge base.',
11102
- },
11103
- limit: {
11104
- type: 'integer',
11105
- description: 'Maximum number of matching source excerpts to return.',
11106
- },
11107
- },
11108
- required: ['query'],
11109
- },
11110
- },
11111
- ],
11112
- };
11113
- }
11114
- /**
11115
- * Creates the model-facing system-message section for knowledge search.
11116
- *
11117
- * @param requirements - Current model requirements.
11118
- * @returns Markdown system-message section.
11119
- *
11120
- * @private internal utility of `KnowledgeCommitmentDefinition`
11121
- */
11122
- function createKnowledgeSearchSystemSection(requirements) {
11123
- const sourceEntries = createKnowledgeSourceSystemEntries(requirements);
11124
- const sourceList = sourceEntries.length > 0 ? sourceEntries.map((entry) => `- ${entry}`).join('\n') : '- None';
11125
- return spacetrim.spaceTrim(`
11126
- ## ${KNOWLEDGE_SEARCH_SYSTEM_SECTION_TITLE}
11127
-
11128
- - Use \`${KNOWLEDGE_SEARCH_TOOL_NAME}\` to search the configured knowledge sources before answering questions that depend on this agent's knowledge base.
11129
- - Base source-backed factual answers on the returned excerpts.
11130
- - When you use a returned excerpt, include its citation marker in the answer body, for example \`[0:0]\`.
11131
- - If the search returns no relevant information, say that the knowledge base did not contain the answer instead of inventing it.
11132
-
11133
- Configured knowledge sources:
11134
- ${sourceList}
11135
- `);
11136
- }
11137
- /**
11138
- * Builds a stable list of configured knowledge sources for system-message diagnostics.
11139
- *
11140
- * @param requirements - Current model requirements.
11141
- * @returns Human-readable source entries.
11142
- *
11143
- * @private internal utility of `KnowledgeCommitmentDefinition`
11144
- */
11145
- function createKnowledgeSourceSystemEntries(requirements) {
11146
- var _a;
11147
- const entries = [];
11148
- const seenEntries = new Set();
11149
- for (const source of requirements.knowledgeSources || []) {
11150
- const entry = `Source URL: ${source} (processed for retrieval during chat)`;
11151
- if (seenEntries.has(entry)) {
11152
- continue;
11153
- }
11154
- seenEntries.add(entry);
11155
- entries.push(entry);
11156
- }
11157
- const inlineSources = (((_a = requirements._metadata) === null || _a === void 0 ? void 0 : _a.inlineKnowledgeSources) || [])
11158
- .map((source) => source.filename)
11159
- .filter(Boolean);
11160
- for (const filename of inlineSources) {
11161
- const entry = `Knowledge Source Inline: ${filename} (Inline source: processed for retrieval during chat)`;
11162
- if (seenEntries.has(entry)) {
11163
- continue;
11164
- }
11165
- seenEntries.add(entry);
11166
- entries.push(entry);
11167
- }
11168
- return entries;
11169
- }
11170
11663
 
11171
11664
  /**
11172
11665
  * LANGUAGE commitment definition
@@ -13754,7 +14247,11 @@
13754
14247
  // Create new system message with persona at the beginning
13755
14248
  // Format: "You are {agentName}\n{personaContent}"
13756
14249
  // The # PERSONA comment will be removed later by removeCommentsFromSystemMessage
13757
- const personaSection = `# PERSONA\nYou are ${agentName}\n${mergedPersonaContent}`; // <- TODO: Use spaceTrim
14250
+ const personaSection = spacetrim.spaceTrim((block) => `
14251
+ # PERSONA
14252
+ You are ${agentName}
14253
+ ${block(mergedPersonaContent)}
14254
+ `);
13758
14255
  const newSystemMessage = cleanedMessage ? `${personaSection}\n\n${cleanedMessage}` : personaSection;
13759
14256
  return {
13760
14257
  ...requirements,
@@ -14656,7 +15153,10 @@
14656
15153
  */
14657
15154
  function buildTeamToolDescription(entry) {
14658
15155
  const detailLines = collectTeamEntryDetails(entry).map(({ label, content }) => `${label}: ${content}`);
14659
- return [`Consult teammate ${entry.teammate.label}`, ...detailLines].join('\n');
15156
+ return spacetrim.spaceTrim((block) => `
15157
+ Consult teammate ${entry.teammate.label}
15158
+ ${block(detailLines.join('\n'))}
15159
+ `);
14660
15160
  }
14661
15161
  /**
14662
15162
  * Collects structured teammate details that should stay visible to the model.
@@ -20172,7 +20672,10 @@
20172
20672
  if (hiddenCount > 0) {
20173
20673
  summaryRows.push(`...and ${hiddenCount} more.`);
20174
20674
  }
20175
- return [`Found ${options.total} ${options.total === 1 ? 'timeout' : 'timeouts'}:`, ...summaryRows].join('\n');
20675
+ return spacetrim.spaceTrim((block) => `
20676
+ Found ${options.total} ${options.total === 1 ? 'timeout' : 'timeouts'}:
20677
+ ${block(summaryRows.join('\n'))}
20678
+ `);
20176
20679
  }
20177
20680
  /**
20178
20681
  * Formats one timeout row for assistant-visible timeout listings.
@@ -38157,6 +38660,174 @@
38157
38660
  .filter(Boolean);
38158
38661
  }
38159
38662
 
38663
+ /**
38664
+ * End-to-end examples used by the standalone Book language documentation.
38665
+ *
38666
+ * These are intentionally compact but complete, so they can be copy-pasted and
38667
+ * used as practical starting points.
38668
+ *
38669
+ * @private internal utility of `createStandaloneBookLanguageMarkdown`
38670
+ */
38671
+ [
38672
+ {
38673
+ id: 'minimal-hello-world-agent',
38674
+ title: 'Minimal hello-world agent',
38675
+ goal: 'Create the smallest useful agent with identity and greeting.',
38676
+ source: spacetrim.spaceTrim(`
38677
+ Hello World Agent
38678
+
38679
+ GOAL Be a concise and friendly assistant.
38680
+ INITIAL MESSAGE Hello! I am ready to help.
38681
+ CLOSED
38682
+ `),
38683
+ walkthrough: [
38684
+ 'The first line (`Hello World Agent`) is the agent name.',
38685
+ '`GOAL` defines the effective role and profile text.',
38686
+ '`INITIAL MESSAGE` sets a deterministic first message for a new chat.',
38687
+ '`CLOSED` prevents conversational self-modification.',
38688
+ ],
38689
+ },
38690
+ {
38691
+ id: 'tool-using-browser-search-agent',
38692
+ title: 'Tool-using agent (Browser + Search engine)',
38693
+ goal: 'Enable internet research with clear sourcing behavior.',
38694
+ source: spacetrim.spaceTrim(`
38695
+ Web Research Assistant
38696
+
38697
+ GOAL Research topics using fresh and verifiable information.
38698
+ USE SEARCH ENGINE Prefer official sources and recent publications.
38699
+ USE BROWSER
38700
+ RULE Verify important claims across multiple sources when possible.
38701
+ RULE Include source links in your final answer.
38702
+ INITIAL MESSAGE Ask me what topic you want to research and how deep the report should be.
38703
+ `),
38704
+ walkthrough: [
38705
+ '`USE SEARCH ENGINE` provides web search tooling and optional search instructions.',
38706
+ '`USE BROWSER` enables URL fetching and interactive browsing tools.',
38707
+ '`RULE` commitments make reliability behavior explicit and repeatable.',
38708
+ 'This pattern is ideal for current-events and fact-checking agents.',
38709
+ ],
38710
+ },
38711
+ {
38712
+ id: 'rule-and-knowledge-agent',
38713
+ title: 'Agent with RULE and KNOWLEDGE',
38714
+ goal: 'Ground responses in explicit constraints and curated sources.',
38715
+ source: spacetrim.spaceTrim(`
38716
+ Support Policy Assistant
38717
+
38718
+ GOAL Answer questions about support policy.
38719
+ KNOWLEDGE Refunds are available within 30 days with proof of purchase.
38720
+ KNOWLEDGE https://example.com/support-policy
38721
+ RULE If a policy item is missing in available knowledge, say it explicitly.
38722
+ RULE Never invent legal or policy statements.
38723
+ INITIAL MESSAGE I can explain refund and support rules from provided knowledge.
38724
+ `),
38725
+ walkthrough: [
38726
+ '`KNOWLEDGE` may be inline text or an external URL/document.',
38727
+ '`RULE` commitments define non-negotiable behavior constraints.',
38728
+ 'Combining both creates predictable, grounded policy responses.',
38729
+ 'Use this pattern for compliance, support, and internal procedures.',
38730
+ ],
38731
+ },
38732
+ {
38733
+ id: 'memory-agent-with-long-term-memory',
38734
+ title: 'MEMORY agent with long-term memory',
38735
+ goal: 'Persist user preferences across conversations.',
38736
+ source: spacetrim.spaceTrim(`
38737
+ Customer Success Memory Agent
38738
+
38739
+ GOAL Support SaaS customers while remembering relevant setup and preference context.
38740
+ MEMORY Remember product setup, user goals, and communication preferences.
38741
+ RULE Store only user-approved preferences and project context.
38742
+ RULE Never store secrets or sensitive data unless explicitly requested and allowed.
38743
+ INITIAL MESSAGE I can remember your setup and preferences for future sessions.
38744
+ `),
38745
+ walkthrough: [
38746
+ '`MEMORY` adds runtime memory tools and memory-specific system guidance.',
38747
+ '`RULE` commitments narrow what should be remembered to reduce privacy risks.',
38748
+ 'In Agents Server, memory is runtime-backed and user-scoped.',
38749
+ 'Use this for assistants that must preserve context over time.',
38750
+ ],
38751
+ },
38752
+ {
38753
+ id: 'use-project-and-wallet-integration-agent',
38754
+ title: 'USE PROJECT and WALLET external integration',
38755
+ goal: 'Work with GitHub repositories and wallet-backed credentials.',
38756
+ source: spacetrim.spaceTrim(`
38757
+ Repository Maintainer
38758
+
38759
+ GOAL Maintain a GitHub repository and prepare safe pull requests.
38760
+ USE PROJECT https://github.com/acme/website
38761
+ WALLET Store credentials for repository operations.
38762
+ RULE Before editing files, explain the planned change and impacted paths.
38763
+ RULE Never reveal raw credentials in chat output.
38764
+ INITIAL MESSAGE I can inspect the repository and help you prepare PR-ready changes.
38765
+ `),
38766
+ walkthrough: [
38767
+ '`USE PROJECT` enables repository tools for listing, reading, editing files, and creating PRs.',
38768
+ 'Credentials are resolved from wallet records at runtime in Agents Server.',
38769
+ '`WALLET` is kept here as a compatibility marker, but current Book 2.0 parsing treats it as deprecated/ignored.',
38770
+ 'In current runtime behavior, wallet-backed integrations are driven by commitments such as `USE PROJECT` and `USE EMAIL`.',
38771
+ ],
38772
+ },
38773
+ {
38774
+ id: 'use-calendar-integration-agent',
38775
+ title: 'USE CALENDAR integration',
38776
+ goal: 'Coordinate meetings and schedules through a connected Google Calendar.',
38777
+ source: spacetrim.spaceTrim(`
38778
+ Calendar Assistant
38779
+
38780
+ GOAL Schedule meetings and keep the calendar conflict-free.
38781
+ USE CALENDAR https://calendar.google.com/calendar/u/0/r
38782
+ SCOPES https://www.googleapis.com/auth/calendar
38783
+ RULE Confirm destructive actions before deleting an event.
38784
+ INITIAL MESSAGE Tell me the meeting details and I will schedule it in your calendar.
38785
+ `),
38786
+ walkthrough: [
38787
+ '`USE CALENDAR` enables calendar tools for listing, reading, creating, updating, and deleting events.',
38788
+ 'The first calendar URL identifies which calendar integration should be used.',
38789
+ '`SCOPES` can explicitly request required Google Calendar OAuth permissions.',
38790
+ 'Credentials are resolved from wallet-backed Google Calendar OAuth records at runtime in Agents Server.',
38791
+ ],
38792
+ },
38793
+ {
38794
+ id: 'agents-team-example',
38795
+ title: 'Agents TEAM (with in-book teammates)',
38796
+ goal: 'Delegate sub-tasks to specialized teammates.',
38797
+ source: spacetrim.spaceTrim(`
38798
+ Team Manager
38799
+
38800
+ GOAL Coordinate specialists and deliver one consolidated answer.
38801
+ TEAM Ask {Legal Reviewer} for legal constraints and {Implementation Reviewer} for technical feasibility.
38802
+ RULE Always summarize teammate outputs into one action plan.
38803
+
38804
+ ---
38805
+
38806
+ Legal Reviewer
38807
+
38808
+ FROM VOID
38809
+ GOAL Review legal and compliance risk.
38810
+ RULE Flag legal/compliance risk and uncertainty clearly.
38811
+ CLOSED
38812
+
38813
+ ---
38814
+
38815
+ Implementation Reviewer
38816
+
38817
+ FROM VOID
38818
+ GOAL Review implementation effort and delivery risk.
38819
+ RULE Estimate complexity and identify blockers.
38820
+ CLOSED
38821
+ `),
38822
+ walkthrough: [
38823
+ 'The main agent delegates via `TEAM` commitment.',
38824
+ 'References in `{...}` are resolved against embedded agents inside the same book (split by `---`).',
38825
+ 'Each teammate can be isolated with `FROM VOID` for deterministic specialization.',
38826
+ 'This pattern works well for multi-role review and decision support.',
38827
+ ],
38828
+ },
38829
+ ];
38830
+
38160
38831
  /**
38161
38832
  * Gets all tool titles provided by all commitments
38162
38833
  *
@@ -38407,6 +39078,72 @@
38407
39078
  },
38408
39079
  });
38409
39080
 
39081
+ /**
39082
+ * Matcher is form of app that evaluates (spreadsheet) content against defined criteria or patterns,
39083
+ * determining if it matches or meets specific requirements. Used for classification,
39084
+ * validation, filtering, and quality assessment of inputs.
39085
+ *
39086
+ * @public exported from `@promptbook/core`
39087
+ */
39088
+ ({
39089
+ name: 'EXPERIMENTAL_MATCHER',
39090
+ description: spacetrim.spaceTrim(`
39091
+ An evaluation system that determines whether content meets specific criteria or patterns.
39092
+ Used for content validation, quality assessment, and intelligent filtering tasks. Currently in experimental phase.
39093
+ `),
39094
+ documentationUrl: `https://github.com/webgptorg/promptbook/discussions/177`,
39095
+ pipelineInterface: {
39096
+ inputParameters: [
39097
+ /* Input parameters for content to be matched and criteria to match against */
39098
+ {
39099
+ name: 'nonce',
39100
+ description: 'Just to prevent EXPERIMENTAL_MATCHER to be set as implicit formfactor',
39101
+ isInput: true,
39102
+ isOutput: false,
39103
+ },
39104
+ ],
39105
+ outputParameters: [
39106
+ /* Output parameters containing match results, confidence scores, and relevant metadata */
39107
+ ],
39108
+ },
39109
+ });
39110
+
39111
+ /**
39112
+ * Translator is form of app that transforms input text from one form to another,
39113
+ * such as language translation, style conversion, tone modification, or other text transformations.
39114
+ *
39115
+ * @public exported from `@promptbook/core`
39116
+ */
39117
+ ({
39118
+ name: 'TRANSLATOR',
39119
+ description: spacetrim.spaceTrim(`
39120
+ A text transformation system that converts input content into different forms,
39121
+ including language translations, paraphrasing, style conversions, and tone adjustments.
39122
+ This form factor takes one input and produces one transformed output.
39123
+ `),
39124
+ documentationUrl: `https://github.com/webgptorg/promptbook/discussions/175`,
39125
+ pipelineInterface: {
39126
+ inputParameters: [
39127
+ {
39128
+ name: 'inputMessage',
39129
+ description: `Input message to be translated`,
39130
+ isInput: true,
39131
+ isOutput: false,
39132
+ },
39133
+ ],
39134
+ outputParameters: [
39135
+ {
39136
+ name: 'outputMessage',
39137
+ description: `Translated output message`,
39138
+ isInput: false,
39139
+ isOutput: true,
39140
+ },
39141
+ ],
39142
+ // <- TODO: [🤓] Maybe add {summary}
39143
+ // <- TODO: [🧠] maybe change to {inputText}, {inputText} / or make system for any name of first input and first outpur parameter
39144
+ },
39145
+ });
39146
+
38410
39147
  /**
38411
39148
  * Register for LLM tools metadata.
38412
39149
  *
@@ -38584,11 +39321,11 @@
38584
39321
  if (attachments.length === 0) {
38585
39322
  return '';
38586
39323
  }
38587
- return [
38588
- CHAT_ATTACHMENTS_HEADING,
38589
- ...attachments.map((attachment) => formatChatAttachmentLine(attachment)),
38590
- CHAT_ATTACHMENTS_INSTRUCTION,
38591
- ].join('\n');
39324
+ return spacetrim.spaceTrim((block) => `
39325
+ ${CHAT_ATTACHMENTS_HEADING}
39326
+ ${block(attachments.map((attachment) => formatChatAttachmentLine(attachment)).join('\n'))}
39327
+ ${CHAT_ATTACHMENTS_INSTRUCTION}
39328
+ `);
38592
39329
  }
38593
39330
 
38594
39331
  /**
@@ -38829,24 +39566,24 @@
38829
39566
  : ''}`
38830
39567
  : null;
38831
39568
  const warningsLine = contentResolution.warnings.length > 0 ? `Warnings: ${contentResolution.warnings.join(' | ')}` : null;
38832
- if (!contentResolution.content) {
39569
+ const metadataLines = [decodingLine, warningsLine].filter((line) => Boolean(line)).join('\n');
39570
+ const resolvedContent = contentResolution.content;
39571
+ if (!resolvedContent) {
38833
39572
  const reason = contentResolution.reason || 'content unavailable';
38834
- return [`- ${attachmentLabel}: ${reason}. URL: ${contentResolution.attachment.url}`, decodingLine, warningsLine]
38835
- .filter(Boolean)
38836
- .join('\n');
39573
+ return spacetrim.spaceTrim((block) => `
39574
+ - ${attachmentLabel}: ${reason}. URL: ${contentResolution.attachment.url}
39575
+ ${block(metadataLines)}
39576
+ `);
38837
39577
  }
38838
39578
  const truncatedLabel = contentResolution.isTruncated ? ' [truncated]' : '';
38839
- return [
38840
- `File: ${attachmentLabel}${truncatedLabel}`,
38841
- `URL: ${contentResolution.attachment.url}`,
38842
- decodingLine,
38843
- warningsLine,
38844
- '```text',
38845
- contentResolution.content,
38846
- '```',
38847
- ]
38848
- .filter(Boolean)
38849
- .join('\n');
39579
+ return spacetrim.spaceTrim((block) => `
39580
+ File: ${attachmentLabel}${truncatedLabel}
39581
+ URL: ${contentResolution.attachment.url}
39582
+ ${block(metadataLines)}
39583
+ \`\`\`text
39584
+ ${block(resolvedContent)}
39585
+ \`\`\`
39586
+ `);
38850
39587
  }
38851
39588
  /**
38852
39589
  * Formats inline attachment-content context section for the model.
@@ -38860,11 +39597,14 @@
38860
39597
  if (resolvedContents.length === 0) {
38861
39598
  return '';
38862
39599
  }
38863
- return [
38864
- CHAT_ATTACHMENT_CONTENT_HEADING,
38865
- CHAT_ATTACHMENT_CONTENT_INSTRUCTION,
38866
- ...resolvedContents.map((resolvedContent) => formatResolvedChatAttachmentContent(resolvedContent)),
38867
- ].join('\n\n');
39600
+ return spacetrim.spaceTrim((block) => `
39601
+ ${CHAT_ATTACHMENT_CONTENT_HEADING}
39602
+ ${CHAT_ATTACHMENT_CONTENT_INSTRUCTION}
39603
+
39604
+ ${block(resolvedContents
39605
+ .map((resolvedContent) => formatResolvedChatAttachmentContent(resolvedContent))
39606
+ .join('\n\n'))}
39607
+ `);
38868
39608
  }
38869
39609
 
38870
39610
  /**
@@ -40009,9 +40749,11 @@
40009
40749
  * Creates the retry-history error message shared by all OpenAI-compatible model variants.
40010
40750
  */
40011
40751
  createAttemptHistoryError(finalErrorMessage) {
40012
- return new PipelineExecutionError(`All attempts failed. Attempt history:\n` +
40013
- formatUnsupportedParameterAttemptHistory(this.attemptStack) +
40014
- `\nFinal error: ${finalErrorMessage}`);
40752
+ return new PipelineExecutionError(spacetrim.spaceTrim((block) => `
40753
+ All attempts failed. Attempt history:
40754
+ ${block(formatUnsupportedParameterAttemptHistory(this.attemptStack))}
40755
+ Final error: ${finalErrorMessage}
40756
+ `));
40015
40757
  }
40016
40758
  }
40017
40759
 
@@ -42605,7 +43347,7 @@
42605
43347
  });
42606
43348
  }
42607
43349
  let vectorStoreId = cachedVectorStoreId;
42608
- if (this.isNativeKnowledgeSearchEnabled && !vectorStoreId && knowledgeSources && knowledgeSources.length > 0) {
43350
+ if (!vectorStoreId && knowledgeSources && knowledgeSources.length > 0) {
42609
43351
  const vectorStoreResult = await this.createVectorStoreWithKnowledgeSources({
42610
43352
  client: await this.getClient(),
42611
43353
  name,
@@ -42614,19 +43356,13 @@
42614
43356
  });
42615
43357
  vectorStoreId = vectorStoreResult.vectorStoreId;
42616
43358
  }
42617
- else if (this.isNativeKnowledgeSearchEnabled && vectorStoreId && this.options.isVerbose) {
43359
+ else if (vectorStoreId && this.options.isVerbose) {
42618
43360
  console.info('[🤰]', 'Using cached vector store for AgentKit agent', {
42619
43361
  name,
42620
43362
  vectorStoreId,
42621
43363
  });
42622
43364
  }
42623
- if (!this.isNativeKnowledgeSearchEnabled) {
42624
- vectorStoreId = undefined;
42625
- }
42626
- const agentKitTools = this.buildAgentKitTools({
42627
- tools,
42628
- vectorStoreId,
42629
- });
43365
+ const agentKitTools = this.buildAgentKitTools({ tools, vectorStoreId });
42630
43366
  const openAiAgentKitAgent = new agents.Agent({
42631
43367
  name,
42632
43368
  model: this.agentKitModelName,
@@ -42645,7 +43381,7 @@
42645
43381
  name,
42646
43382
  model: this.agentKitModelName,
42647
43383
  toolCount: agentKitTools.length,
42648
- hasVectorStore: this.isNativeKnowledgeSearchEnabled && Boolean(vectorStoreId),
43384
+ hasVectorStore: Boolean(vectorStoreId),
42649
43385
  });
42650
43386
  }
42651
43387
  return preparedAgent;
@@ -43144,12 +43880,6 @@
43144
43880
  get agentKitOptions() {
43145
43881
  return this.options;
43146
43882
  }
43147
- /**
43148
- * Returns true when hosted OpenAI vector-store search should back `knowledgeSources`.
43149
- */
43150
- get isNativeKnowledgeSearchEnabled() {
43151
- return this.agentKitOptions.isNativeKnowledgeSearchEnabled !== false;
43152
- }
43153
43883
  /**
43154
43884
  * Discriminant for type guards.
43155
43885
  */
@@ -48877,7 +49607,6 @@
48877
49607
  request_wallet_record: { title: 'Requesting wallet', emoji: '👛' },
48878
49608
  web_search: { title: 'Searching the web', emoji: '🔎' },
48879
49609
  deep_search: { title: 'Deep research', emoji: '🔬' },
48880
- knowledge_search: { title: 'Searching knowledge', emoji: '📚' },
48881
49610
  useSearchEngine: { title: 'Searching the web', emoji: '🔎' },
48882
49611
  search: { title: 'Searching the web', emoji: '🔎' },
48883
49612
  useBrowser: { title: 'Browsing the web', emoji: '🌐' },
@@ -49753,13 +50482,6 @@
49753
50482
  content: sanitizedContentWithoutButtons,
49754
50483
  citations: message.citations,
49755
50484
  }), [message.citations, sanitizedContentWithoutButtons]);
49756
- const structuredSourceCitations = react.useMemo(() => {
49757
- const footnoteSourceKeys = new Set(citationFootnoteRenderModel.footnotes.map((footnote) => footnote.citation.source.trim().toLowerCase()));
49758
- return (message.sources || []).filter((source) => {
49759
- const sourceKey = source.source.trim().toLowerCase();
49760
- return sourceKey && !footnoteSourceKeys.has(sourceKey);
49761
- });
49762
- }, [citationFootnoteRenderModel.footnotes, message.sources]);
49763
50485
  const contentSegments = react.useMemo(() => splitMessageContentIntoSegments(citationFootnoteRenderModel.content), [citationFootnoteRenderModel.content]);
49764
50486
  const hasMapSegment = react.useMemo(() => contentSegments.some((segment) => segment.type === 'map'), [contentSegments]);
49765
50487
  const [localHoveredRating, setLocalHoveredRating] = react.useState(0);
@@ -50031,7 +50753,7 @@
50031
50753
  '--message-bg-color': isAgentArticleMode ? articleModeBackgroundColor : color.toHex(),
50032
50754
  '--message-text-color': isAgentArticleMode ? articleModeTextColor : colorOfText.toHex(),
50033
50755
  '--chat-message-swipe-offset': swipeTranslation,
50034
- }, onPointerDown: handleReplyPointerDown, onPointerMove: handleReplyPointerMove, onPointerUp: handleReplyPointerEnd, onPointerCancel: resetReplySwipe, children: [isReplyActionEnabled && (jsxRuntime.jsx("div", { className: classNames(styles$5.replySwipeIndicator, isMe && styles$5.replySwipeIndicatorRight, isReplySwipeArmed && styles$5.replySwipeIndicatorActive), "aria-hidden": "true", children: jsxRuntime.jsx(lucideReact.Reply, { className: styles$5.replySwipeIndicatorIcon }) })), !shouldRenderArticleActionsBar && renderMessageReadAndCopyControls(), message.isVoiceCall && (jsxRuntime.jsx("div", { className: styles$5.voiceCallIndicator, children: jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: jsxRuntime.jsx("path", { d: "M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z" }) }) })), replyingToMessage && replyPreviewText && replySenderLabel && (jsxRuntime.jsx(ChatReplyPreview, { label: replyPreviewLabel, senderLabel: replySenderLabel, previewText: replyPreviewText, className: styles$5.replyBubblePreview })), jsxRuntime.jsx("div", { ref: contentWithoutButtonsRef, children: jsxRuntime.jsx(ChatMessageRichContent, { content: message.content, contentSegments: contentSegments, streamingFeaturePlaceholderKind: streamingFeaturePlaceholderKind, onCreateAgent: onCreateAgent, mode: mode }) }), message.attachments && message.attachments.length > 0 && (jsxRuntime.jsx("div", { className: styles$5.attachments, children: message.attachments.map((attachment, index) => (jsxRuntime.jsxs("a", { href: attachment.url, target: "_blank", rel: "noopener noreferrer", className: styles$5.attachment, title: attachment.name, children: [jsxRuntime.jsx("span", { className: styles$5.attachmentIcon, children: "\uD83D\uDCCE" }), jsxRuntime.jsx("span", { className: styles$5.attachmentName, children: attachment.name })] }, index))) })), jsxRuntime.jsx(ChatMessageToolCallChips, { chips: toolCallChips, onToolCallClick: onToolCallClick }), citationFootnoteRenderModel.footnotes.length > 0 && (jsxRuntime.jsx("div", { className: styles$5.citationFootnotes, children: citationFootnoteRenderModel.footnotes.map((footnote) => (jsxRuntime.jsxs("div", { className: styles$5.citationFootnoteItem, children: [jsxRuntime.jsx("span", { className: styles$5.citationFootnoteNumber, children: footnote.number }), jsxRuntime.jsx(SourceChip, { citation: footnote.citation, onClick: onCitationClick, isCitationIdVisible: false })] }, `citation-footnote-${footnote.number}-${footnote.citation.source}`))) })), structuredSourceCitations.length > 0 && (jsxRuntime.jsx("div", { className: styles$5.sourceCitations, children: structuredSourceCitations.map((citation, index) => (jsxRuntime.jsx(SourceChip, { citation: citation, onClick: onCitationClick, isCitationIdVisible: false }, `message-source-${citation.source}-${citation.id}-${index}`))) })), transitiveCitations.length > 0 && (jsxRuntime.jsx("div", { className: styles$5.sourceCitations, children: transitiveCitations.map((citation, index) => (jsxRuntime.jsx(SourceChip, { citation: citation, suffix: `by ${citation.origin.label}`, onClick: onCitationClick }, `team-source-${citation.source}-${index}`))) })), shouldShowButtons && (jsxRuntime.jsx("div", { className: styles$5.messageButtons, children: renderableButtons.map(({ button, buttonIndex }) => (jsxRuntime.jsx("button", { type: "button", className: classNames(styles$5.messageButton, button.type === 'action' && styles$5.actionMessageButton), onClick: (event) => {
50756
+ }, onPointerDown: handleReplyPointerDown, onPointerMove: handleReplyPointerMove, onPointerUp: handleReplyPointerEnd, onPointerCancel: resetReplySwipe, children: [isReplyActionEnabled && (jsxRuntime.jsx("div", { className: classNames(styles$5.replySwipeIndicator, isMe && styles$5.replySwipeIndicatorRight, isReplySwipeArmed && styles$5.replySwipeIndicatorActive), "aria-hidden": "true", children: jsxRuntime.jsx(lucideReact.Reply, { className: styles$5.replySwipeIndicatorIcon }) })), !shouldRenderArticleActionsBar && renderMessageReadAndCopyControls(), message.isVoiceCall && (jsxRuntime.jsx("div", { className: styles$5.voiceCallIndicator, children: jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: jsxRuntime.jsx("path", { d: "M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z" }) }) })), replyingToMessage && replyPreviewText && replySenderLabel && (jsxRuntime.jsx(ChatReplyPreview, { label: replyPreviewLabel, senderLabel: replySenderLabel, previewText: replyPreviewText, className: styles$5.replyBubblePreview })), jsxRuntime.jsx("div", { ref: contentWithoutButtonsRef, children: jsxRuntime.jsx(ChatMessageRichContent, { content: message.content, contentSegments: contentSegments, streamingFeaturePlaceholderKind: streamingFeaturePlaceholderKind, onCreateAgent: onCreateAgent, mode: mode }) }), message.attachments && message.attachments.length > 0 && (jsxRuntime.jsx("div", { className: styles$5.attachments, children: message.attachments.map((attachment, index) => (jsxRuntime.jsxs("a", { href: attachment.url, target: "_blank", rel: "noopener noreferrer", className: styles$5.attachment, title: attachment.name, children: [jsxRuntime.jsx("span", { className: styles$5.attachmentIcon, children: "\uD83D\uDCCE" }), jsxRuntime.jsx("span", { className: styles$5.attachmentName, children: attachment.name })] }, index))) })), jsxRuntime.jsx(ChatMessageToolCallChips, { chips: toolCallChips, onToolCallClick: onToolCallClick }), citationFootnoteRenderModel.footnotes.length > 0 && (jsxRuntime.jsx("div", { className: styles$5.citationFootnotes, children: citationFootnoteRenderModel.footnotes.map((footnote) => (jsxRuntime.jsxs("div", { className: styles$5.citationFootnoteItem, children: [jsxRuntime.jsx("span", { className: styles$5.citationFootnoteNumber, children: footnote.number }), jsxRuntime.jsx(SourceChip, { citation: footnote.citation, onClick: onCitationClick, isCitationIdVisible: false })] }, `citation-footnote-${footnote.number}-${footnote.citation.source}`))) })), transitiveCitations.length > 0 && (jsxRuntime.jsx("div", { className: styles$5.sourceCitations, children: transitiveCitations.map((citation, index) => (jsxRuntime.jsx(SourceChip, { citation: citation, suffix: `by ${citation.origin.label}`, onClick: onCitationClick }, `team-source-${citation.source}-${index}`))) })), shouldShowButtons && (jsxRuntime.jsx("div", { className: styles$5.messageButtons, children: renderableButtons.map(({ button, buttonIndex }) => (jsxRuntime.jsx("button", { type: "button", className: classNames(styles$5.messageButton, button.type === 'action' && styles$5.actionMessageButton), onClick: (event) => {
50035
50757
  event.stopPropagation();
50036
50758
  if (button.type === 'message') {
50037
50759
  const quickMessageHandler = onQuickMessageButton || onMessage;
@@ -50083,9 +50805,6 @@
50083
50805
  if (prev.message.citations !== next.message.citations) {
50084
50806
  return false;
50085
50807
  }
50086
- if (prev.message.sources !== next.message.sources) {
50087
- return false;
50088
- }
50089
50808
  if (JSON.stringify(prev.message.attachments) !== JSON.stringify(next.message.attachments)) {
50090
50809
  return false;
50091
50810
  }
@@ -51500,7 +52219,7 @@
51500
52219
  const { results, rawText } = extractSearchResults(resultRaw);
51501
52220
  const hasResults = results.length > 0;
51502
52221
  const hasRawText = !hasResults && !!rawText && rawText.trim().length > 0;
51503
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: styles$5.searchModalHeader, children: [jsxRuntime.jsx("span", { className: styles$5.searchModalIcon, children: "\uD83D\uDD0E" }), jsxRuntime.jsx("h3", { className: styles$5.searchModalQuery, children: args.query || args.searchText || 'Search Results' })] }), jsxRuntime.jsx("div", { className: styles$5.searchModalContent, children: hasResults ? (jsxRuntime.jsx("div", { className: styles$5.searchResultsList, children: results.map((item, index) => (jsxRuntime.jsxs("div", { className: styles$5.searchResultItem, children: [jsxRuntime.jsx("div", { className: styles$5.searchResultUrl, children: item.url && (jsxRuntime.jsx("a", { href: item.url, target: "_blank", rel: "noreferrer", children: item.url })) }), jsxRuntime.jsx("h4", { className: styles$5.searchResultTitle, children: item.url ? (jsxRuntime.jsx("a", { href: item.url, target: "_blank", rel: "noreferrer", children: item.title || item.source || 'Untitled' })) : (item.title || item.source || 'Untitled') }), jsxRuntime.jsx("p", { className: styles$5.searchResultSnippet, children: item.snippet || item.content || item.excerpt || '' })] }, index))) })) : hasRawText ? (jsxRuntime.jsx(MarkdownContent, { className: styles$5.searchResultsRaw, content: rawText })) : toolCallState !== 'COMPLETE' ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [renderToolCallProgressPlaceholder({
52222
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: styles$5.searchModalHeader, children: [jsxRuntime.jsx("span", { className: styles$5.searchModalIcon, children: "\uD83D\uDD0E" }), jsxRuntime.jsx("h3", { className: styles$5.searchModalQuery, children: args.query || args.searchText || 'Search Results' })] }), jsxRuntime.jsx("div", { className: styles$5.searchModalContent, children: hasResults ? (jsxRuntime.jsx("div", { className: styles$5.searchResultsList, children: results.map((item, index) => (jsxRuntime.jsxs("div", { className: styles$5.searchResultItem, children: [jsxRuntime.jsx("div", { className: styles$5.searchResultUrl, children: item.url && (jsxRuntime.jsx("a", { href: item.url, target: "_blank", rel: "noreferrer", children: item.url })) }), jsxRuntime.jsx("h4", { className: styles$5.searchResultTitle, children: item.url ? (jsxRuntime.jsx("a", { href: item.url, target: "_blank", rel: "noreferrer", children: item.title || 'Untitled' })) : (item.title || 'Untitled') }), jsxRuntime.jsx("p", { className: styles$5.searchResultSnippet, children: item.snippet || item.content || '' })] }, index))) })) : hasRawText ? (jsxRuntime.jsx(MarkdownContent, { className: styles$5.searchResultsRaw, content: rawText })) : toolCallState !== 'COMPLETE' ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [renderToolCallProgressPlaceholder({
51504
52223
  title: 'Search results pending',
51505
52224
  message: resolveToolCallProgressMessage(toolCall),
51506
52225
  }), jsxRuntime.jsxs("div", { className: styles$5.toolCallDetailsCard, children: [jsxRuntime.jsxs("div", { className: styles$5.toolCallDetailsCardRow, children: [jsxRuntime.jsx("strong", { children: "Query" }), jsxRuntime.jsx("span", { children: String(args.query || args.searchText || 'Search query is being prepared.') })] }), args.location && (jsxRuntime.jsxs("div", { className: styles$5.toolCallDetailsCardRow, children: [jsxRuntime.jsx("strong", { children: "Location" }), jsxRuntime.jsx("span", { children: String(args.location) })] })), args.engine && (jsxRuntime.jsxs("div", { className: styles$5.toolCallDetailsCardRow, children: [jsxRuntime.jsx("strong", { children: "Engine" }), jsxRuntime.jsx("span", { children: String(args.engine) })] }))] })] })) : (jsxRuntime.jsx("div", { className: styles$5.noResults, children: resultRaw ? 'No search results found.' : 'Search results are not available.' })) })] }));
@@ -52080,7 +52799,6 @@
52080
52799
  function isSearchToolCallName(toolName) {
52081
52800
  return (toolName === 'web_search' ||
52082
52801
  toolName === 'deep_search' ||
52083
- toolName === 'knowledge_search' ||
52084
52802
  toolName === 'useSearchEngine' ||
52085
52803
  toolName === 'search');
52086
52804
  }