@promptbook/remote-server 0.112.0-73 → 0.112.0-80

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 (74) hide show
  1. package/README.md +9 -9
  2. package/esm/index.es.js +823 -374
  3. package/esm/index.es.js.map +1 -1
  4. package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
  5. package/esm/src/avatars/visuals/octopus3d2AvatarVisual.d.ts +7 -0
  6. package/esm/src/avatars/visuals/octopus3dAvatarVisualShared.d.ts +37 -0
  7. package/esm/src/book-components/Chat/save/_common/chatExportRendering.d.ts +47 -0
  8. package/esm/src/book-components/Chat/save/html/htmlSaveFormatDefinition.d.ts +12 -0
  9. package/esm/src/book-components/Chat/save/index.d.ts +2 -2
  10. package/esm/src/book-components/Chat/save/markdown/mdSaveFormatDefinition.d.ts +5 -3
  11. package/esm/src/book-components/Chat/save/pdf/buildChatPdf.d.ts +3 -3
  12. package/esm/src/book-components/Chat/save/pdf/pdfSaveFormatDefinition.d.ts +1 -1
  13. package/esm/src/cli/cli-commands/agent/agentProjectPaths.d.ts +8 -8
  14. package/esm/src/cli/cli-commands/agent/initializeAgentRunnerCommand.d.ts +1 -1
  15. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +56 -0
  16. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer.test.d.ts +1 -0
  17. package/esm/src/cli/cli-commands/agents-server/ensureAgentsServerEnvFile.d.ts +7 -0
  18. package/esm/src/cli/cli-commands/agents-server/ensureAgentsServerGitignoreFile.d.ts +7 -0
  19. package/esm/src/cli/cli-commands/agents-server/init.d.ts +9 -0
  20. package/esm/src/cli/cli-commands/agents-server/init.test.d.ts +1 -0
  21. package/esm/src/cli/cli-commands/agents-server/initializeAgentsServerProjectConfiguration.d.ts +17 -0
  22. package/esm/src/cli/cli-commands/agents-server/printAgentsServerInitializationSummary.d.ts +7 -0
  23. package/esm/src/cli/cli-commands/agents-server/run.d.ts +14 -0
  24. package/esm/src/cli/cli-commands/agents-server/run.test.d.ts +1 -0
  25. package/esm/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +23 -0
  26. package/esm/src/cli/cli-commands/agents-server.d.ts +8 -0
  27. package/esm/src/cli/cli-commands/common/projectInitialization.d.ts +65 -0
  28. package/esm/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +44 -0
  29. package/esm/src/cli/common/$deprecateCliCommand.d.ts +8 -0
  30. package/esm/src/cli/common/$deprecateCliCommand.test.d.ts +1 -0
  31. package/esm/src/utils/color/Color.d.ts +4 -44
  32. package/esm/src/utils/color/ColorValue.d.ts +55 -0
  33. package/esm/src/utils/color/isHexColorString.d.ts +10 -0
  34. package/esm/src/utils/color/parseColorString.d.ts +11 -0
  35. package/esm/src/version.d.ts +1 -1
  36. package/package.json +2 -2
  37. package/umd/index.umd.js +823 -374
  38. package/umd/index.umd.js.map +1 -1
  39. package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
  40. package/umd/src/avatars/visuals/octopus3d2AvatarVisual.d.ts +7 -0
  41. package/umd/src/avatars/visuals/octopus3dAvatarVisualShared.d.ts +37 -0
  42. package/umd/src/book-components/Chat/save/_common/chatExportRendering.d.ts +47 -0
  43. package/umd/src/book-components/Chat/save/html/htmlSaveFormatDefinition.d.ts +12 -0
  44. package/umd/src/book-components/Chat/save/index.d.ts +2 -2
  45. package/umd/src/book-components/Chat/save/markdown/mdSaveFormatDefinition.d.ts +5 -3
  46. package/umd/src/book-components/Chat/save/pdf/buildChatPdf.d.ts +3 -3
  47. package/umd/src/book-components/Chat/save/pdf/pdfSaveFormatDefinition.d.ts +1 -1
  48. package/umd/src/cli/cli-commands/agent/agentProjectPaths.d.ts +8 -8
  49. package/umd/src/cli/cli-commands/agent/initializeAgentRunnerCommand.d.ts +1 -1
  50. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +56 -0
  51. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer.test.d.ts +1 -0
  52. package/umd/src/cli/cli-commands/agents-server/ensureAgentsServerEnvFile.d.ts +7 -0
  53. package/umd/src/cli/cli-commands/agents-server/ensureAgentsServerGitignoreFile.d.ts +7 -0
  54. package/umd/src/cli/cli-commands/agents-server/init.d.ts +9 -0
  55. package/umd/src/cli/cli-commands/agents-server/init.test.d.ts +1 -0
  56. package/umd/src/cli/cli-commands/agents-server/initializeAgentsServerProjectConfiguration.d.ts +17 -0
  57. package/umd/src/cli/cli-commands/agents-server/printAgentsServerInitializationSummary.d.ts +7 -0
  58. package/umd/src/cli/cli-commands/agents-server/run.d.ts +14 -0
  59. package/umd/src/cli/cli-commands/agents-server/run.test.d.ts +1 -0
  60. package/umd/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +23 -0
  61. package/umd/src/cli/cli-commands/agents-server.d.ts +8 -0
  62. package/umd/src/cli/cli-commands/common/projectInitialization.d.ts +65 -0
  63. package/umd/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +44 -0
  64. package/umd/src/cli/common/$deprecateCliCommand.d.ts +8 -0
  65. package/umd/src/cli/common/$deprecateCliCommand.test.d.ts +1 -0
  66. package/umd/src/utils/color/Color.d.ts +4 -44
  67. package/umd/src/utils/color/ColorValue.d.ts +55 -0
  68. package/umd/src/utils/color/isHexColorString.d.ts +10 -0
  69. package/umd/src/utils/color/parseColorString.d.ts +11 -0
  70. package/umd/src/version.d.ts +1 -1
  71. package/esm/src/cli/cli-commands/coder/appendBlock.d.ts +0 -6
  72. package/esm/src/cli/cli-commands/coder/readTextFileIfExists.d.ts +0 -6
  73. package/umd/src/cli/cli-commands/coder/appendBlock.d.ts +0 -6
  74. package/umd/src/cli/cli-commands/coder/readTextFileIfExists.d.ts +0 -6
package/esm/index.es.js CHANGED
@@ -40,7 +40,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
40
40
  * @generated
41
41
  * @see https://github.com/webgptorg/promptbook
42
42
  */
43
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-73';
43
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-80';
44
44
  /**
45
45
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
46
46
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -290,6 +290,111 @@ function checkChannelValue(channelName, value) {
290
290
  }
291
291
  }
292
292
 
293
+ /**
294
+ * Shared immutable channel storage and serialization helpers for `Color`.
295
+ *
296
+ * @private base class of Color
297
+ */
298
+ class ColorValue {
299
+ constructor(red, green, blue, alpha = 255) {
300
+ this.red = red;
301
+ this.green = green;
302
+ this.blue = blue;
303
+ this.alpha = alpha;
304
+ checkChannelValue('Red', red);
305
+ checkChannelValue('Green', green);
306
+ checkChannelValue('Blue', blue);
307
+ checkChannelValue('Alpha', alpha);
308
+ }
309
+ /**
310
+ * Shortcut for `red` property
311
+ * Number from 0 to 255
312
+ * @alias red
313
+ */
314
+ get r() {
315
+ return this.red;
316
+ }
317
+ /**
318
+ * Shortcut for `green` property
319
+ * Number from 0 to 255
320
+ * @alias green
321
+ */
322
+ get g() {
323
+ return this.green;
324
+ }
325
+ /**
326
+ * Shortcut for `blue` property
327
+ * Number from 0 to 255
328
+ * @alias blue
329
+ */
330
+ get b() {
331
+ return this.blue;
332
+ }
333
+ /**
334
+ * Shortcut for `alpha` property
335
+ * Number from 0 (transparent) to 255 (opaque)
336
+ * @alias alpha
337
+ */
338
+ get a() {
339
+ return this.alpha;
340
+ }
341
+ /**
342
+ * Shortcut for `alpha` property
343
+ * Number from 0 (transparent) to 255 (opaque)
344
+ * @alias alpha
345
+ */
346
+ get opacity() {
347
+ return this.alpha;
348
+ }
349
+ /**
350
+ * Shortcut for 1-`alpha` property
351
+ */
352
+ get transparency() {
353
+ return 255 - this.alpha;
354
+ }
355
+ clone() {
356
+ return take(this.createColor(this.red, this.green, this.blue, this.alpha));
357
+ }
358
+ toString() {
359
+ return this.toHex();
360
+ }
361
+ toHex() {
362
+ if (this.alpha === 255) {
363
+ return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
364
+ .toString(16)
365
+ .padStart(2, '0')}`;
366
+ }
367
+ else {
368
+ return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
369
+ .toString(16)
370
+ .padStart(2, '0')}${this.alpha.toString(16).padStart(2, '0')}`;
371
+ }
372
+ }
373
+ toRgb() {
374
+ if (this.alpha === 255) {
375
+ return `rgb(${this.red}, ${this.green}, ${this.blue})`;
376
+ }
377
+ else {
378
+ return `rgba(${this.red}, ${this.green}, ${this.blue}, ${Math.round((this.alpha / 255) * 100)}%)`;
379
+ }
380
+ }
381
+ toHsl() {
382
+ throw new Error(`Getting HSL is not implemented`);
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Checks if the given value is a valid hex color string
388
+ *
389
+ * @param value - value to check
390
+ * @returns true if the value is a valid hex color string (e.g., `#009edd`, `#fff`, etc.)
391
+ *
392
+ * @private function of Color
393
+ */
394
+ function isHexColorString(value) {
395
+ return (typeof value === 'string' && /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value));
396
+ }
397
+
293
398
  /**
294
399
  * Constant for short hex lengths.
295
400
  */
@@ -501,16 +606,53 @@ function parseAlphaValue(value) {
501
606
 
502
607
  /**
503
608
  * Pattern matching hsl regex.
609
+ *
610
+ * @private function of Color
504
611
  */
505
612
  const HSL_REGEX_PATTERN = /^hsl\(\s*([0-9.]+)\s*,\s*([0-9.]+)%\s*,\s*([0-9.]+)%\s*\)$/;
506
613
  /**
507
614
  * Pattern matching RGB regex.
615
+ *
616
+ * @private function of Color
508
617
  */
509
618
  const RGB_REGEX_PATTERN = /^rgb\(\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*\)$/;
510
619
  /**
511
620
  * Pattern matching rgba regex.
621
+ *
622
+ * @private function of Color
512
623
  */
513
624
  const RGBA_REGEX_PATTERN = /^rgba\(\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*\)$/;
625
+ /**
626
+ * Parses a supported color string into RGBA channels.
627
+ *
628
+ * @param color as a string for example `#009edd`, `rgb(0,158,221)`, `rgb(0%,62%,86.7%)`, `hsl(197.1,100%,43.3%)`, `red`, `darkgrey`,...
629
+ * @returns RGBA channel values.
630
+ *
631
+ * @private function of Color
632
+ */
633
+ function parseColorString(color) {
634
+ const trimmed = color.trim();
635
+ const cssColor = CSS_COLORS[trimmed];
636
+ if (cssColor) {
637
+ return parseColorString(cssColor);
638
+ }
639
+ else if (isHexColorString(trimmed)) {
640
+ return parseHexColor(trimmed);
641
+ }
642
+ if (HSL_REGEX_PATTERN.test(trimmed)) {
643
+ return parseHslColor(trimmed);
644
+ }
645
+ else if (RGB_REGEX_PATTERN.test(trimmed)) {
646
+ return parseRgbColor(trimmed);
647
+ }
648
+ else if (RGBA_REGEX_PATTERN.test(trimmed)) {
649
+ return parseRgbaColor(trimmed);
650
+ }
651
+ else {
652
+ throw new Error(`Can not create a new Color instance from string "${trimmed}".`);
653
+ }
654
+ }
655
+
514
656
  /**
515
657
  * Color object represents an RGB color with alpha channel
516
658
  *
@@ -518,7 +660,7 @@ const RGBA_REGEX_PATTERN = /^rgba\(\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*,\s*([0-9.
518
660
  *
519
661
  * @public exported from `@promptbook/color`
520
662
  */
521
- class Color {
663
+ class Color extends ColorValue {
522
664
  /**
523
665
  * Creates a new Color instance from miscellaneous formats
524
666
  * - It can receive Color instance and just return the same instance
@@ -591,25 +733,7 @@ class Color {
591
733
  * @returns Color object
592
734
  */
593
735
  static fromString(color) {
594
- const trimmed = color.trim();
595
- if (CSS_COLORS[trimmed]) {
596
- return Color.fromString(CSS_COLORS[trimmed]);
597
- }
598
- else if (Color.isHexColorString(trimmed)) {
599
- return Color.fromHex(trimmed);
600
- }
601
- if (HSL_REGEX_PATTERN.test(trimmed)) {
602
- return Color.fromHsl(trimmed);
603
- }
604
- else if (RGB_REGEX_PATTERN.test(trimmed)) {
605
- return Color.fromRgbString(trimmed);
606
- }
607
- else if (RGBA_REGEX_PATTERN.test(trimmed)) {
608
- return Color.fromRgbaString(trimmed);
609
- }
610
- else {
611
- throw new Error(`Can not create a new Color instance from string "${trimmed}".`);
612
- }
736
+ return Color.fromColorChannels(parseColorString(color));
613
737
  }
614
738
  /**
615
739
  * Gets common color
@@ -639,8 +763,7 @@ class Color {
639
763
  * @returns Color object
640
764
  */
641
765
  static fromHex(hex) {
642
- const { red, green, blue, alpha } = parseHexColor(hex);
643
- return take(new Color(red, green, blue, alpha));
766
+ return Color.fromColorChannels(parseHexColor(hex));
644
767
  }
645
768
  /**
646
769
  * Creates a new Color instance from color in hsl format
@@ -649,8 +772,7 @@ class Color {
649
772
  * @returns Color object
650
773
  */
651
774
  static fromHsl(hsl) {
652
- const { red, green, blue, alpha } = parseHslColor(hsl);
653
- return take(new Color(red, green, blue, alpha));
775
+ return Color.fromColorChannels(parseHslColor(hsl));
654
776
  }
655
777
  /**
656
778
  * Creates a new Color instance from color in rgb format
@@ -659,8 +781,7 @@ class Color {
659
781
  * @returns Color object
660
782
  */
661
783
  static fromRgbString(rgb) {
662
- const { red, green, blue, alpha } = parseRgbColor(rgb);
663
- return take(new Color(red, green, blue, alpha));
784
+ return Color.fromColorChannels(parseRgbColor(rgb));
664
785
  }
665
786
  /**
666
787
  * Creates a new Color instance from color in rbga format
@@ -669,8 +790,7 @@ class Color {
669
790
  * @returns Color object
670
791
  */
671
792
  static fromRgbaString(rgba) {
672
- const { red, green, blue, alpha } = parseRgbaColor(rgba);
673
- return take(new Color(red, green, blue, alpha));
793
+ return Color.fromColorChannels(parseRgbaColor(rgba));
674
794
  }
675
795
  /**
676
796
  * Creates a new Color for color channels values
@@ -682,7 +802,7 @@ class Color {
682
802
  * @returns Color object
683
803
  */
684
804
  static fromValues(red, green, blue, alpha = 255) {
685
- return take(new Color(red, green, blue, alpha));
805
+ return Color.fromColorChannels({ red, green, blue, alpha });
686
806
  }
687
807
  /**
688
808
  * Checks if the given value is a valid Color object.
@@ -715,8 +835,7 @@ class Color {
715
835
  * @returns true if the value is a valid hex color string (e.g., `#009edd`, `#fff`, etc.)
716
836
  */
717
837
  static isHexColorString(value) {
718
- return (typeof value === 'string' &&
719
- /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value));
838
+ return isHexColorString(value);
720
839
  }
721
840
  /**
722
841
  * Creates new Color object
@@ -729,89 +848,13 @@ class Color {
729
848
  * @param alpha number from 0 (transparent) to 255 (opaque)
730
849
  */
731
850
  constructor(red, green, blue, alpha = 255) {
732
- this.red = red;
733
- this.green = green;
734
- this.blue = blue;
735
- this.alpha = alpha;
736
- checkChannelValue('Red', red);
737
- checkChannelValue('Green', green);
738
- checkChannelValue('Blue', blue);
739
- checkChannelValue('Alpha', alpha);
740
- }
741
- /**
742
- * Shortcut for `red` property
743
- * Number from 0 to 255
744
- * @alias red
745
- */
746
- get r() {
747
- return this.red;
748
- }
749
- /**
750
- * Shortcut for `green` property
751
- * Number from 0 to 255
752
- * @alias green
753
- */
754
- get g() {
755
- return this.green;
756
- }
757
- /**
758
- * Shortcut for `blue` property
759
- * Number from 0 to 255
760
- * @alias blue
761
- */
762
- get b() {
763
- return this.blue;
764
- }
765
- /**
766
- * Shortcut for `alpha` property
767
- * Number from 0 (transparent) to 255 (opaque)
768
- * @alias alpha
769
- */
770
- get a() {
771
- return this.alpha;
772
- }
773
- /**
774
- * Shortcut for `alpha` property
775
- * Number from 0 (transparent) to 255 (opaque)
776
- * @alias alpha
777
- */
778
- get opacity() {
779
- return this.alpha;
780
- }
781
- /**
782
- * Shortcut for 1-`alpha` property
783
- */
784
- get transparency() {
785
- return 255 - this.alpha;
786
- }
787
- clone() {
788
- return take(new Color(this.red, this.green, this.blue, this.alpha));
789
- }
790
- toString() {
791
- return this.toHex();
792
- }
793
- toHex() {
794
- if (this.alpha === 255) {
795
- return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
796
- .toString(16)
797
- .padStart(2, '0')}`;
798
- }
799
- else {
800
- return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
801
- .toString(16)
802
- .padStart(2, '0')}${this.alpha.toString(16).padStart(2, '0')}`;
803
- }
851
+ super(red, green, blue, alpha);
804
852
  }
805
- toRgb() {
806
- if (this.alpha === 255) {
807
- return `rgb(${this.red}, ${this.green}, ${this.blue})`;
808
- }
809
- else {
810
- return `rgba(${this.red}, ${this.green}, ${this.blue}, ${Math.round((this.alpha / 255) * 100)}%)`;
811
- }
853
+ createColor(red, green, blue, alpha) {
854
+ return new Color(red, green, blue, alpha);
812
855
  }
813
- toHsl() {
814
- throw new Error(`Getting HSL is not implemented`);
856
+ static fromColorChannels({ red, green, blue, alpha }) {
857
+ return take(new Color(red, green, blue, alpha));
815
858
  }
816
859
  }
817
860
 
@@ -2171,120 +2214,183 @@ function $deepFreeze(objectValue) {
2171
2214
  * @public exported from `@promptbook/utils`
2172
2215
  */
2173
2216
  function checkSerializableAsJson(options) {
2174
- const { value, name, message } = options;
2217
+ checkSerializableValue(options);
2218
+ }
2219
+ // TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
2220
+ // TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
2221
+ // Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
2222
+ /**
2223
+ * Checks one value and dispatches to the appropriate specialized validator.
2224
+ *
2225
+ * @private function of `checkSerializableAsJson`
2226
+ */
2227
+ function checkSerializableValue(options) {
2228
+ const { value } = options;
2229
+ if (isSerializablePrimitive(value)) {
2230
+ return;
2231
+ }
2175
2232
  if (value === undefined) {
2176
- throw new UnexpectedError(`${name} is undefined`);
2233
+ throw new UnexpectedError(`${options.name} is undefined`);
2177
2234
  }
2178
- else if (value === null) {
2179
- return;
2235
+ if (typeof value === 'symbol') {
2236
+ throw new UnexpectedError(`${options.name} is symbol`);
2180
2237
  }
2181
- else if (typeof value === 'boolean') {
2182
- return;
2238
+ if (typeof value === 'function') {
2239
+ throw new UnexpectedError(`${options.name} is function`);
2183
2240
  }
2184
- else if (typeof value === 'number' && !isNaN(value)) {
2241
+ if (Array.isArray(value)) {
2242
+ checkSerializableArray(options, value);
2185
2243
  return;
2186
2244
  }
2187
- else if (typeof value === 'string') {
2245
+ if (value !== null && typeof value === 'object') {
2246
+ checkSerializableObject(options, value);
2188
2247
  return;
2189
2248
  }
2190
- else if (typeof value === 'symbol') {
2191
- throw new UnexpectedError(`${name} is symbol`);
2192
- }
2193
- else if (typeof value === 'function') {
2194
- throw new UnexpectedError(`${name} is function`);
2195
- }
2196
- else if (typeof value === 'object' && Array.isArray(value)) {
2197
- for (let i = 0; i < value.length; i++) {
2198
- checkSerializableAsJson({ name: `${name}[${i}]`, value: value[i], message });
2199
- }
2249
+ throwUnknownTypeError(options);
2250
+ }
2251
+ /**
2252
+ * Checks the primitive values that are directly JSON serializable.
2253
+ *
2254
+ * @private function of `checkSerializableAsJson`
2255
+ */
2256
+ function isSerializablePrimitive(value) {
2257
+ return (value === null ||
2258
+ typeof value === 'boolean' ||
2259
+ (typeof value === 'number' && !isNaN(value)) ||
2260
+ typeof value === 'string');
2261
+ }
2262
+ /**
2263
+ * Recursively checks JSON array items.
2264
+ *
2265
+ * @private function of `checkSerializableAsJson`
2266
+ */
2267
+ function checkSerializableArray(context, arrayValue) {
2268
+ for (let index = 0; index < arrayValue.length; index++) {
2269
+ checkSerializableAsJson({
2270
+ ...context,
2271
+ name: `${context.name}[${index}]`,
2272
+ value: arrayValue[index],
2273
+ });
2200
2274
  }
2201
- else if (typeof value === 'object') {
2202
- if (value instanceof Date) {
2203
- throw new UnexpectedError(spaceTrim$1((block) => `
2204
- \`${name}\` is Date
2275
+ }
2276
+ /**
2277
+ * Checks object-like values and dispatches special unsupported built-ins.
2278
+ *
2279
+ * @private function of `checkSerializableAsJson`
2280
+ */
2281
+ function checkSerializableObject(context, objectValue) {
2282
+ checkUnsupportedObjectType(context, objectValue);
2283
+ checkSerializableObjectEntries(context, objectValue);
2284
+ assertJsonStringificationSucceeds(context, objectValue);
2285
+ }
2286
+ /**
2287
+ * Rejects built-in objects that must be converted before JSON serialization.
2288
+ *
2289
+ * @private function of `checkSerializableAsJson`
2290
+ */
2291
+ function checkUnsupportedObjectType(context, objectValue) {
2292
+ if (objectValue instanceof Date) {
2293
+ throw new UnexpectedError(spaceTrim$1((block) => `
2294
+ \`${context.name}\` is Date
2205
2295
 
2206
- Use \`string_date_iso8601\` instead
2296
+ Use \`string_date_iso8601\` instead
2207
2297
 
2208
- Additional message for \`${name}\`:
2209
- ${block(message || '(nothing)')}
2210
- `));
2211
- }
2212
- else if (value instanceof Map) {
2213
- throw new UnexpectedError(`${name} is Map`);
2214
- }
2215
- else if (value instanceof Set) {
2216
- throw new UnexpectedError(`${name} is Set`);
2217
- }
2218
- else if (value instanceof RegExp) {
2219
- throw new UnexpectedError(`${name} is RegExp`);
2220
- }
2221
- else if (value instanceof Error) {
2222
- throw new UnexpectedError(spaceTrim$1((block) => `
2223
- \`${name}\` is unserialized Error
2298
+ Additional message for \`${context.name}\`:
2299
+ ${block(context.message || '(nothing)')}
2300
+ `));
2301
+ }
2302
+ if (objectValue instanceof Map) {
2303
+ throw new UnexpectedError(`${context.name} is Map`);
2304
+ }
2305
+ if (objectValue instanceof Set) {
2306
+ throw new UnexpectedError(`${context.name} is Set`);
2307
+ }
2308
+ if (objectValue instanceof RegExp) {
2309
+ throw new UnexpectedError(`${context.name} is RegExp`);
2310
+ }
2311
+ if (objectValue instanceof Error) {
2312
+ throw new UnexpectedError(spaceTrim$1((block) => `
2313
+ \`${context.name}\` is unserialized Error
2224
2314
 
2225
- Use function \`serializeError\`
2315
+ Use function \`serializeError\`
2226
2316
 
2227
- Additional message for \`${name}\`:
2228
- ${block(message || '(nothing)')}
2317
+ Additional message for \`${context.name}\`:
2318
+ ${block(context.message || '(nothing)')}
2229
2319
 
2230
- `));
2320
+ `));
2321
+ }
2322
+ }
2323
+ /**
2324
+ * Recursively checks object properties while preserving omitted `undefined` keys.
2325
+ *
2326
+ * @private function of `checkSerializableAsJson`
2327
+ */
2328
+ function checkSerializableObjectEntries(context, objectValue) {
2329
+ for (const [subName, subValue] of Object.entries(objectValue)) {
2330
+ if (subValue === undefined) {
2331
+ // Note: undefined in object is serializable - it is just omitted
2332
+ continue;
2231
2333
  }
2232
- else {
2233
- for (const [subName, subValue] of Object.entries(value)) {
2234
- if (subValue === undefined) {
2235
- // Note: undefined in object is serializable - it is just omitted
2236
- continue;
2237
- }
2238
- checkSerializableAsJson({ name: `${name}.${subName}`, value: subValue, message });
2239
- }
2240
- try {
2241
- JSON.stringify(value); // <- TODO: [0]
2242
- }
2243
- catch (error) {
2244
- assertsError(error);
2245
- throw new UnexpectedError(spaceTrim$1((block) => `
2246
- \`${name}\` is not serializable
2334
+ checkSerializableAsJson({
2335
+ ...context,
2336
+ name: `${context.name}.${subName}`,
2337
+ value: subValue,
2338
+ });
2339
+ }
2340
+ }
2341
+ /**
2342
+ * Uses `JSON.stringify` as the final guard for cases like circular references.
2343
+ *
2344
+ * @private function of `checkSerializableAsJson`
2345
+ */
2346
+ function assertJsonStringificationSucceeds(context, objectValue) {
2347
+ try {
2348
+ JSON.stringify(objectValue); // <- TODO: [0]
2349
+ }
2350
+ catch (error) {
2351
+ assertsError(error);
2352
+ throw new UnexpectedError(spaceTrim$1((block) => `
2353
+ \`${context.name}\` is not serializable
2247
2354
 
2248
- ${block(error.stack || error.message)}
2355
+ ${block(error.stack || error.message)}
2249
2356
 
2250
- Additional message for \`${name}\`:
2251
- ${block(message || '(nothing)')}
2252
- `));
2357
+ Additional message for \`${context.name}\`:
2358
+ ${block(context.message || '(nothing)')}
2359
+ `));
2360
+ }
2361
+ /*
2362
+ TODO: [0] Is there some more elegant way to check circular references?
2363
+ const seen = new Set();
2364
+ const stack = [{ value }];
2365
+ while (stack.length > 0) {
2366
+ const { value } = stack.pop()!;
2367
+ if (typeof value === 'object' && value !== null) {
2368
+ if (seen.has(value)) {
2369
+ throw new UnexpectedError(`${name} has circular reference`);
2253
2370
  }
2254
- /*
2255
- TODO: [0] Is there some more elegant way to check circular references?
2256
- const seen = new Set();
2257
- const stack = [{ value }];
2258
- while (stack.length > 0) {
2259
- const { value } = stack.pop()!;
2260
- if (typeof value === 'object' && value !== null) {
2261
- if (seen.has(value)) {
2262
- throw new UnexpectedError(`${name} has circular reference`);
2263
- }
2264
- seen.add(value);
2265
- if (Array.isArray(value)) {
2266
- stack.push(...value.map((value) => ({ value })));
2267
- } else {
2268
- stack.push(...Object.values(value).map((value) => ({ value })));
2269
- }
2270
- }
2371
+ seen.add(value);
2372
+ if (Array.isArray(value)) {
2373
+ stack.push(...value.map((value) => ({ value })));
2374
+ } else {
2375
+ stack.push(...Object.values(value).map((value) => ({ value })));
2271
2376
  }
2272
- */
2273
- return;
2274
2377
  }
2275
2378
  }
2276
- else {
2277
- throw new UnexpectedError(spaceTrim$1((block) => `
2278
- \`${name}\` is unknown type
2379
+ */
2380
+ }
2381
+ /**
2382
+ * Throws the fallback error for unsupported value types like `bigint` and `NaN`.
2383
+ *
2384
+ * @private function of `checkSerializableAsJson`
2385
+ */
2386
+ function throwUnknownTypeError(context) {
2387
+ throw new UnexpectedError(spaceTrim$1((block) => `
2388
+ \`${context.name}\` is unknown type
2279
2389
 
2280
- Additional message for \`${name}\`:
2281
- ${block(message || '(nothing)')}
2282
- `));
2283
- }
2390
+ Additional message for \`${context.name}\`:
2391
+ ${block(context.message || '(nothing)')}
2392
+ `));
2284
2393
  }
2285
- // TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
2286
- // TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
2287
- // Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
2288
2394
 
2289
2395
  /**
2290
2396
  * Creates a deep clone of the given object
@@ -2917,8 +3023,7 @@ function hasTaskJokers(task) {
2917
3023
  * @private internal utility of `validatePipeline`
2918
3024
  */
2919
3025
  function validateTaskSupportsJokers(task, pipelineIdentification) {
2920
- if (task.format ||
2921
- task.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
3026
+ if (task.format || task.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
2922
3027
  return;
2923
3028
  }
2924
3029
  throw new PipelineLogicError(spaceTrim$1((block) => `
@@ -4035,7 +4140,7 @@ function createJokerCommands(task) {
4035
4140
  */
4036
4141
  function createPostprocessingCommands(task) {
4037
4142
  var _a;
4038
- return ((_a = task.postprocessingFunctionNames) === null || _a === void 0 ? void 0 : _a.map((postprocessingFunctionName) => `POSTPROCESSING \`${postprocessingFunctionName}\``)) || [];
4143
+ return (((_a = task.postprocessingFunctionNames) === null || _a === void 0 ? void 0 : _a.map((postprocessingFunctionName) => `POSTPROCESSING \`${postprocessingFunctionName}\``)) || []);
4039
4144
  }
4040
4145
  /**
4041
4146
  * Collects expectation commands.
@@ -7466,9 +7571,7 @@ function createFailuresSummary($failedResults) {
7466
7571
  ${block(quoteMultilineText(((_b = failure.error) === null || _b === void 0 ? void 0 : _b.message) || ''))}
7467
7572
 
7468
7573
  Result:
7469
- ${block(failure.result === null
7470
- ? 'null'
7471
- : quoteMultilineText(spaceTrim$1(failure.result)))}
7574
+ ${block(failure.result === null ? 'null' : quoteMultilineText(spaceTrim$1(failure.result)))}
7472
7575
  `;
7473
7576
  }))
7474
7577
  .join('\n\n---\n\n');
@@ -9050,10 +9153,7 @@ function createAvailableProviderMessage(llmToolStatus, index, env) {
9050
9153
  * @private internal function of `$registeredLlmToolsMessage`
9051
9154
  */
9052
9155
  function createProviderStatusMessages(llmToolStatus, env) {
9053
- return [
9054
- createInstallationStatusMessage(llmToolStatus),
9055
- createConfigurationStatusMessage(llmToolStatus, env),
9056
- ];
9156
+ return [createInstallationStatusMessage(llmToolStatus), createConfigurationStatusMessage(llmToolStatus, env)];
9057
9157
  }
9058
9158
  /**
9059
9159
  * Creates the installation-status sentence for one provider.
@@ -14616,7 +14716,7 @@ function fillTextureRect(texture, x, y, width, height, color) {
14616
14716
  *
14617
14717
  * @private helper of `minecraft2AvatarVisual`
14618
14718
  */
14619
- const LIGHT_DIRECTION$1 = normalizeVector3({
14719
+ const LIGHT_DIRECTION$2 = normalizeVector3({
14620
14720
  x: 0.4,
14621
14721
  y: -0.65,
14622
14722
  z: 0.92,
@@ -14828,7 +14928,7 @@ function resolveVisibleCuboidFaces(cuboid, size, sceneCenterX, sceneCenterY) {
14828
14928
  corners: projectedCorners,
14829
14929
  texture: faceDefinition.texture,
14830
14930
  averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
14831
- lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$1), -1, 1),
14931
+ lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$2), -1, 1),
14832
14932
  outlineColor: cuboid.outlineColor,
14833
14933
  };
14834
14934
  });
@@ -15888,13 +15988,138 @@ function drawSeededEye(context, centerX, centerY, radiusX, radiusY, rotation, pa
15888
15988
  context.restore();
15889
15989
  }
15890
15990
 
15991
+ /* eslint-disable no-magic-numbers */
15992
+ /**
15993
+ * Draws one projected eye on a rotated octopus surface.
15994
+ *
15995
+ * @private helper of the 3D octopus avatar visuals
15996
+ */
15997
+ function drawProjectedOrganicEye(context, localCenter, radiusX, radiusY, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, phase, interaction, eyeStyle) {
15998
+ const centerScenePoint = transformScenePoint(localCenter, center, rotationX, rotationY);
15999
+ if (centerScenePoint.z <= center.z) {
16000
+ return;
16001
+ }
16002
+ const horizontalScenePoint = transformScenePoint({ x: localCenter.x + radiusX, y: localCenter.y, z: localCenter.z }, center, rotationX, rotationY);
16003
+ const verticalScenePoint = transformScenePoint({ x: localCenter.x, y: localCenter.y + radiusY, z: localCenter.z }, center, rotationX, rotationY);
16004
+ const projectedCenterPoint = projectScenePoint(centerScenePoint, size, sceneCenterX, sceneCenterY);
16005
+ const projectedHorizontalPoint = projectScenePoint(horizontalScenePoint, size, sceneCenterX, sceneCenterY);
16006
+ const projectedVerticalPoint = projectScenePoint(verticalScenePoint, size, sceneCenterX, sceneCenterY);
16007
+ const projectedRadiusX = Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y);
16008
+ const projectedRadiusY = Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y);
16009
+ if (projectedRadiusX < size * 0.008 || projectedRadiusY < size * 0.008) {
16010
+ return;
16011
+ }
16012
+ const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
16013
+ radiusX: projectedRadiusX,
16014
+ radiusY: projectedRadiusY,
16015
+ timeMs,
16016
+ phase,
16017
+ interaction,
16018
+ });
16019
+ const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
16020
+ context.save();
16021
+ context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
16022
+ context.rotate(rotation);
16023
+ context.beginPath();
16024
+ context.ellipse(0, 0, projectedRadiusX, projectedRadiusY, 0, 0, Math.PI * 2);
16025
+ context.fillStyle = '#f8fbff';
16026
+ context.fill();
16027
+ context.clip();
16028
+ const irisGradient = context.createRadialGradient(-projectedRadiusX * 0.2, -projectedRadiusY * 0.26, projectedRadiusX * 0.05, 0, 0, projectedRadiusX * 0.92);
16029
+ irisGradient.addColorStop(0, palette.highlight);
16030
+ irisGradient.addColorStop(0.56, palette.secondary);
16031
+ irisGradient.addColorStop(1, palette.shadow);
16032
+ context.beginPath();
16033
+ context.ellipse(pupilOffsetX, pupilOffsetY, projectedRadiusX * 0.62 * eyeStyle.irisScale, projectedRadiusY * 0.72 * eyeStyle.irisScale, 0, 0, Math.PI * 2);
16034
+ context.fillStyle = irisGradient;
16035
+ context.fill();
16036
+ context.beginPath();
16037
+ context.ellipse(pupilOffsetX, pupilOffsetY, projectedRadiusX * 0.15 * eyeStyle.pupilWidthScale, projectedRadiusY * 0.48 * eyeStyle.pupilHeightScale, 0, 0, Math.PI * 2);
16038
+ context.fillStyle = palette.ink;
16039
+ context.fill();
16040
+ context.beginPath();
16041
+ context.ellipse(pupilOffsetX - projectedRadiusX * 0.22, pupilOffsetY - projectedRadiusY * 0.24, projectedRadiusX * 0.12, projectedRadiusY * 0.14, 0, 0, Math.PI * 2);
16042
+ context.fillStyle = '#ffffff';
16043
+ context.fill();
16044
+ context.restore();
16045
+ context.save();
16046
+ context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
16047
+ context.rotate(rotation);
16048
+ context.beginPath();
16049
+ context.ellipse(0, 0, projectedRadiusX, projectedRadiusY, 0, 0, Math.PI * 2);
16050
+ context.strokeStyle = `${palette.shadow}cc`;
16051
+ context.lineWidth = projectedRadiusX * 0.16;
16052
+ context.stroke();
16053
+ context.beginPath();
16054
+ context.moveTo(-projectedRadiusX * 0.88, -projectedRadiusY * eyeStyle.upperLidInsetRatio);
16055
+ context.quadraticCurveTo(0, -projectedRadiusY * (eyeStyle.upperLidArchRatio - interaction.gazeY * 0.16 + interaction.intensity * 0.08), projectedRadiusX * 0.88, -projectedRadiusY * eyeStyle.upperLidInsetRatio);
16056
+ context.strokeStyle = `${palette.shadow}73`;
16057
+ context.lineWidth = projectedRadiusX * 0.14;
16058
+ context.lineCap = 'round';
16059
+ context.stroke();
16060
+ if (eyeStyle.lowerLidOpacity > 0) {
16061
+ context.beginPath();
16062
+ context.moveTo(-projectedRadiusX * 0.74, projectedRadiusY * 0.2);
16063
+ context.quadraticCurveTo(0, projectedRadiusY * 0.38, projectedRadiusX * 0.74, projectedRadiusY * 0.2);
16064
+ context.strokeStyle = `${palette.highlight}${formatAlphaHex(eyeStyle.lowerLidOpacity)}`;
16065
+ context.lineWidth = projectedRadiusX * 0.08;
16066
+ context.lineCap = 'round';
16067
+ context.stroke();
16068
+ }
16069
+ context.restore();
16070
+ }
16071
+ /**
16072
+ * Draws a subtle projected mouth arc across the front of a rotated octopus surface.
16073
+ *
16074
+ * @private helper of the 3D octopus avatar visuals
16075
+ */
16076
+ function drawProjectedOrganicMouth(context, localPoints, center, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size) {
16077
+ const scenePoints = localPoints.map((localPoint) => transformScenePoint(localPoint, center, rotationX, rotationY));
16078
+ if (scenePoints.some((scenePoint) => scenePoint.z <= center.z)) {
16079
+ return;
16080
+ }
16081
+ const projectedPoints = scenePoints.map((scenePoint) => projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
16082
+ context.beginPath();
16083
+ context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
16084
+ context.quadraticCurveTo(projectedPoints[1].x, projectedPoints[1].y, projectedPoints[2].x, projectedPoints[2].y);
16085
+ context.strokeStyle = `${palette.ink}b8`;
16086
+ context.lineWidth = Math.max(1.1, size * 0.009);
16087
+ context.lineCap = 'round';
16088
+ context.stroke();
16089
+ }
16090
+ /**
16091
+ * Draws one filled projected quad.
16092
+ *
16093
+ * @private helper of the 3D octopus avatar visuals
16094
+ */
16095
+ function drawProjectedQuad(context, corners, fillStyle) {
16096
+ context.beginPath();
16097
+ context.moveTo(corners[0].x, corners[0].y);
16098
+ context.lineTo(corners[1].x, corners[1].y);
16099
+ context.lineTo(corners[2].x, corners[2].y);
16100
+ context.lineTo(corners[3].x, corners[3].y);
16101
+ context.closePath();
16102
+ context.fillStyle = fillStyle;
16103
+ context.fill();
16104
+ }
16105
+ /**
16106
+ * Converts an opacity ratio into a two-digit hexadecimal alpha suffix.
16107
+ *
16108
+ * @private helper of the 3D octopus avatar visuals
16109
+ */
16110
+ function formatAlphaHex(opacity) {
16111
+ return Math.round(clampNumber$1(opacity, 0, 1) * 255)
16112
+ .toString(16)
16113
+ .padStart(2, '0');
16114
+ }
16115
+
15891
16116
  /* eslint-disable no-magic-numbers */
15892
16117
  /**
15893
16118
  * Light direction used by the organic 3D octopus shading.
15894
16119
  *
15895
16120
  * @private helper of `octopus3dAvatarVisual`
15896
16121
  */
15897
- const LIGHT_DIRECTION = normalizeVector3({
16122
+ const LIGHT_DIRECTION$1 = normalizeVector3({
15898
16123
  x: 0.48,
15899
16124
  y: -0.62,
15900
16125
  z: 0.94,
@@ -16007,17 +16232,17 @@ const octopus3dAvatarVisual = {
16007
16232
  for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => candidateTentacleStroke.isFrontFacing)) {
16008
16233
  drawTentacleStroke(context, tentacleStroke, palette);
16009
16234
  }
16010
- drawProjectedEye(context, {
16235
+ drawProjectedOrganicEye(context, {
16011
16236
  x: -faceEyeSpacing,
16012
16237
  y: faceEyeYOffset,
16013
16238
  z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
16014
16239
  }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
16015
- drawProjectedEye(context, {
16240
+ drawProjectedOrganicEye(context, {
16016
16241
  x: faceEyeSpacing,
16017
16242
  y: faceEyeYOffset,
16018
16243
  z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
16019
16244
  }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
16020
- drawProjectedMouth(context, [
16245
+ drawProjectedOrganicMouth(context, [
16021
16246
  {
16022
16247
  x: -mouthHalfWidth,
16023
16248
  y: mouthY,
@@ -16104,7 +16329,7 @@ function resolveVisibleEllipsoidPatches(options) {
16104
16329
  corners: projectedCorners,
16105
16330
  averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
16106
16331
  transformedCorners.length,
16107
- lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
16332
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
16108
16333
  fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
16109
16334
  outlineColor,
16110
16335
  });
@@ -16286,128 +16511,260 @@ function resolveEllipsoidSurfaceDepth(radiusX, radiusY, radiusZ, x, y) {
16286
16511
  const remainingDepthRatio = Math.max(0, 1 - normalizedX * normalizedX - normalizedY * normalizedY);
16287
16512
  return Math.sqrt(remainingDepthRatio) * radiusZ;
16288
16513
  }
16514
+
16515
+ /* eslint-disable no-magic-numbers */
16289
16516
  /**
16290
- * Draws one projected eye on the turned octopus mantle.
16517
+ * Light direction used by the single-mesh octopus shading.
16291
16518
  *
16292
- * @private helper of `octopus3dAvatarVisual`
16519
+ * @private helper of `octopus3d2AvatarVisual`
16293
16520
  */
16294
- function drawProjectedEye(context, localCenter, radiusX, radiusY, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, phase, interaction, eyeStyle) {
16295
- const centerScenePoint = transformScenePoint(localCenter, center, rotationX, rotationY);
16296
- if (centerScenePoint.z <= center.z) {
16297
- return;
16298
- }
16299
- const horizontalScenePoint = transformScenePoint({ x: localCenter.x + radiusX, y: localCenter.y, z: localCenter.z }, center, rotationX, rotationY);
16300
- const verticalScenePoint = transformScenePoint({ x: localCenter.x, y: localCenter.y + radiusY, z: localCenter.z }, center, rotationX, rotationY);
16301
- const projectedCenterPoint = projectScenePoint(centerScenePoint, size, sceneCenterX, sceneCenterY);
16302
- const projectedHorizontalPoint = projectScenePoint(horizontalScenePoint, size, sceneCenterX, sceneCenterY);
16303
- const projectedVerticalPoint = projectScenePoint(verticalScenePoint, size, sceneCenterX, sceneCenterY);
16304
- const projectedRadiusX = Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y);
16305
- const projectedRadiusY = Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y);
16306
- if (projectedRadiusX < size * 0.008 || projectedRadiusY < size * 0.008) {
16307
- return;
16308
- }
16309
- const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
16310
- radiusX: projectedRadiusX,
16311
- radiusY: projectedRadiusY,
16312
- timeMs,
16313
- phase,
16314
- interaction,
16315
- });
16316
- const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
16521
+ const LIGHT_DIRECTION = normalizeVector3({
16522
+ x: 0.38,
16523
+ y: -0.6,
16524
+ z: 0.98,
16525
+ });
16526
+ /**
16527
+ * Octopus 3D 2 avatar visual.
16528
+ *
16529
+ * @private built-in avatar visual
16530
+ */
16531
+ const octopus3d2AvatarVisual = {
16532
+ id: 'octopus3d2',
16533
+ title: 'Octopus 3D 2',
16534
+ description: 'Continuous blobby 3D octopus portrait with one soft mesh, turning silhouette, and cursor-aware eyes.',
16535
+ isAnimated: true,
16536
+ supportsPointerTracking: true,
16537
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
16538
+ const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
16539
+ const animationRandom = createRandom('octopus3d2-animation-profile');
16540
+ const eyeRandom = createRandom('octopus3d2-eye-profile');
16541
+ const animationPhase = animationRandom() * Math.PI * 2;
16542
+ const sceneCenterX = size * 0.5;
16543
+ const sceneCenterY = size * 0.575;
16544
+ const bob = Math.sin(timeMs / 940 + animationPhase) * size * 0.013;
16545
+ const meshCenter = {
16546
+ x: interaction.bodyOffsetX * size * 0.044 + size * morphologyProfile.body.centerXJitterRatio * 0.5,
16547
+ y: -size * 0.03 + interaction.bodyOffsetY * size * 0.026 + bob,
16548
+ z: interaction.intensity * size * 0.018,
16549
+ };
16550
+ const rotationY = -0.14 +
16551
+ Math.sin(timeMs / 2600 + animationPhase) * 0.04 +
16552
+ interaction.bodyOffsetX * 0.2 +
16553
+ interaction.gazeX * 0.78;
16554
+ const rotationX = -0.06 +
16555
+ Math.cos(timeMs / 3000 + animationPhase * 0.7) * 0.02 -
16556
+ interaction.bodyOffsetY * 0.08 -
16557
+ interaction.gazeY * 0.34;
16558
+ const surfaceOptions = {
16559
+ radiusX: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch * 1.02,
16560
+ radiusY: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.22,
16561
+ radiusZ: size *
16562
+ morphologyProfile.body.bodyRadiusRatio *
16563
+ (0.98 + (morphologyProfile.body.horizontalStretch - 1) * 0.2),
16564
+ morphologyProfile,
16565
+ timeMs,
16566
+ animationPhase,
16567
+ };
16568
+ const surfacePatches = resolveVisibleBlobbyOctopusPatches({
16569
+ ...surfaceOptions,
16570
+ center: meshCenter,
16571
+ rotationX,
16572
+ rotationY,
16573
+ sceneCenterX,
16574
+ sceneCenterY,
16575
+ size,
16576
+ palette,
16577
+ });
16578
+ const eyeLatitude = clampNumber$1(morphologyProfile.face.eyeCenterYOffsetRatio * 4.4, -0.16, 0.11);
16579
+ const eyeLongitude = clampNumber$1(morphologyProfile.face.eyeSpacingRatio * 3.25, 0.2, 0.34);
16580
+ const mouthLatitude = clampNumber$1(eyeLatitude + 0.19 + morphologyProfile.face.mouthYOffsetRatio * 1.08, 0.08, 0.34);
16581
+ const mouthCenterLongitude = clampNumber$1(morphologyProfile.face.mouthCenterOffsetRatio * 5.8, -0.08, 0.08);
16582
+ const mouthHalfLongitude = clampNumber$1(eyeLongitude * 0.82, 0.16, 0.29);
16583
+ const mouthCurveLatitude = clampNumber$1(mouthLatitude + morphologyProfile.face.mouthCurveDepthRatio * 0.85, mouthLatitude + 0.03, 0.42);
16584
+ drawAvatarFrame(context, size, palette);
16585
+ drawBlobbyOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
16586
+ drawBlobbyOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile);
16587
+ for (const surfacePatch of surfacePatches.sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
16588
+ drawBlobbySurfacePatch(context, surfacePatch);
16589
+ }
16590
+ const leftEyeLocalCenter = sampleBlobbyOctopusSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude);
16591
+ const rightEyeLocalCenter = sampleBlobbyOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude);
16592
+ const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.78;
16593
+ const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.92;
16594
+ drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
16595
+ drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
16596
+ drawProjectedOrganicMouth(context, [
16597
+ sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
16598
+ sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
16599
+ sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude + mouthHalfLongitude),
16600
+ ], meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size);
16601
+ },
16602
+ };
16603
+ /**
16604
+ * Draws the deep-water glow behind the continuous octopus mesh.
16605
+ *
16606
+ * @private helper of `octopus3d2AvatarVisual`
16607
+ */
16608
+ function drawBlobbyOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
16609
+ const glowGradient = context.createRadialGradient(sceneCenterX + interaction.gazeX * size * 0.11, sceneCenterY - size * 0.17 + interaction.gazeY * size * 0.05, size * 0.05, sceneCenterX, sceneCenterY - size * 0.03, size * 0.66);
16610
+ glowGradient.addColorStop(0, `${palette.highlight}5e`);
16611
+ glowGradient.addColorStop(0.38, `${palette.accent}26`);
16612
+ glowGradient.addColorStop(1, `${palette.highlight}00`);
16613
+ context.fillStyle = glowGradient;
16614
+ context.fillRect(0, 0, size, size);
16615
+ const lowerGradient = context.createRadialGradient(sceneCenterX + Math.sin(timeMs / 1650) * size * 0.045, sceneCenterY + size * 0.28, size * 0.06, sceneCenterX, sceneCenterY + size * 0.28, size * 0.52);
16616
+ lowerGradient.addColorStop(0, `${palette.secondary}22`);
16617
+ lowerGradient.addColorStop(1, `${palette.secondary}00`);
16618
+ context.fillStyle = lowerGradient;
16619
+ context.fillRect(0, 0, size, size);
16620
+ }
16621
+ /**
16622
+ * Draws the soft floor shadow that anchors the single mesh in the frame.
16623
+ *
16624
+ * @private helper of `octopus3d2AvatarVisual`
16625
+ */
16626
+ function drawBlobbyOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
16317
16627
  context.save();
16318
- context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
16319
- context.rotate(rotation);
16320
- context.beginPath();
16321
- context.ellipse(0, 0, projectedRadiusX, projectedRadiusY, 0, 0, Math.PI * 2);
16322
- context.fillStyle = '#f8fbff';
16323
- context.fill();
16324
- context.clip();
16325
- const irisGradient = context.createRadialGradient(-projectedRadiusX * 0.2, -projectedRadiusY * 0.26, projectedRadiusX * 0.05, 0, 0, projectedRadiusX * 0.92);
16326
- irisGradient.addColorStop(0, palette.highlight);
16327
- irisGradient.addColorStop(0.56, palette.secondary);
16328
- irisGradient.addColorStop(1, palette.shadow);
16329
- context.beginPath();
16330
- context.ellipse(pupilOffsetX, pupilOffsetY, projectedRadiusX * 0.62 * eyeStyle.irisScale, projectedRadiusY * 0.72 * eyeStyle.irisScale, 0, 0, Math.PI * 2);
16331
- context.fillStyle = irisGradient;
16332
- context.fill();
16628
+ context.fillStyle = `${palette.shadow}66`;
16629
+ context.filter = `blur(${size * 0.024}px)`;
16333
16630
  context.beginPath();
16334
- context.ellipse(pupilOffsetX, pupilOffsetY, projectedRadiusX * 0.15 * eyeStyle.pupilWidthScale, projectedRadiusY * 0.48 * eyeStyle.pupilHeightScale, 0, 0, Math.PI * 2);
16335
- context.fillStyle = palette.ink;
16336
- context.fill();
16337
- context.beginPath();
16338
- context.ellipse(pupilOffsetX - projectedRadiusX * 0.22, pupilOffsetY - projectedRadiusY * 0.24, projectedRadiusX * 0.12, projectedRadiusY * 0.14, 0, 0, Math.PI * 2);
16339
- context.fillStyle = '#ffffff';
16631
+ context.ellipse(size * 0.5 + interaction.gazeX * size * 0.045, size * 0.88 + Math.sin(timeMs / 940) * size * 0.008, size * (0.18 + (morphologyProfile.body.horizontalStretch - 1) * 0.04 + interaction.intensity * 0.018), size * 0.062, 0, 0, Math.PI * 2);
16340
16632
  context.fill();
16341
16633
  context.restore();
16342
- context.save();
16343
- context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
16344
- context.rotate(rotation);
16345
- context.beginPath();
16346
- context.ellipse(0, 0, projectedRadiusX, projectedRadiusY, 0, 0, Math.PI * 2);
16347
- context.strokeStyle = `${palette.shadow}cc`;
16348
- context.lineWidth = projectedRadiusX * 0.16;
16349
- context.stroke();
16350
- context.beginPath();
16351
- context.moveTo(-projectedRadiusX * 0.88, -projectedRadiusY * eyeStyle.upperLidInsetRatio);
16352
- context.quadraticCurveTo(0, -projectedRadiusY * (eyeStyle.upperLidArchRatio - interaction.gazeY * 0.16 + interaction.intensity * 0.08), projectedRadiusX * 0.88, -projectedRadiusY * eyeStyle.upperLidInsetRatio);
16353
- context.strokeStyle = `${palette.shadow}73`;
16354
- context.lineWidth = projectedRadiusX * 0.14;
16355
- context.lineCap = 'round';
16356
- context.stroke();
16357
- if (eyeStyle.lowerLidOpacity > 0) {
16358
- context.beginPath();
16359
- context.moveTo(-projectedRadiusX * 0.74, projectedRadiusY * 0.2);
16360
- context.quadraticCurveTo(0, projectedRadiusY * 0.38, projectedRadiusX * 0.74, projectedRadiusY * 0.2);
16361
- context.strokeStyle = `${palette.highlight}${formatAlphaHex(eyeStyle.lowerLidOpacity)}`;
16362
- context.lineWidth = projectedRadiusX * 0.08;
16363
- context.lineCap = 'round';
16364
- context.stroke();
16365
- }
16366
- context.restore();
16367
16634
  }
16368
16635
  /**
16369
- * Draws a subtle projected mouth arc across the front of the mantle.
16636
+ * Resolves all visible projected patches for the single blobby octopus mesh.
16370
16637
  *
16371
- * @private helper of `octopus3dAvatarVisual`
16638
+ * @private helper of `octopus3d2AvatarVisual`
16372
16639
  */
16373
- function drawProjectedMouth(context, localPoints, center, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size) {
16374
- const scenePoints = localPoints.map((localPoint) => transformScenePoint(localPoint, center, rotationX, rotationY));
16375
- if (scenePoints.some((scenePoint) => scenePoint.z <= center.z)) {
16376
- return;
16640
+ function resolveVisibleBlobbyOctopusPatches(options) {
16641
+ const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, animationPhase, timeMs, } = options;
16642
+ const latitudePatchCount = 12;
16643
+ const longitudePatchCount = 24;
16644
+ const surfacePatches = [];
16645
+ for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
16646
+ const startLatitude = -Math.PI / 2 + (latitudeIndex / latitudePatchCount) * Math.PI;
16647
+ const endLatitude = -Math.PI / 2 + ((latitudeIndex + 1) / latitudePatchCount) * Math.PI;
16648
+ const centerLatitude = (startLatitude + endLatitude) / 2;
16649
+ const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
16650
+ for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
16651
+ const startLongitude = -Math.PI + (longitudeIndex / longitudePatchCount) * Math.PI * 2;
16652
+ const endLongitude = -Math.PI + ((longitudeIndex + 1) / longitudePatchCount) * Math.PI * 2;
16653
+ const centerLongitude = (startLongitude + endLongitude) / 2;
16654
+ const localCorners = [
16655
+ sampleBlobbyOctopusSurfacePoint(options, startLatitude, startLongitude),
16656
+ sampleBlobbyOctopusSurfacePoint(options, startLatitude, endLongitude),
16657
+ sampleBlobbyOctopusSurfacePoint(options, endLatitude, endLongitude),
16658
+ sampleBlobbyOctopusSurfacePoint(options, endLatitude, startLongitude),
16659
+ ];
16660
+ const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
16661
+ const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
16662
+ if (surfaceNormal.z <= 0.01) {
16663
+ continue;
16664
+ }
16665
+ const projectedCorners = transformedCorners.map((transformedCorner) => projectScenePoint(transformedCorner, size, sceneCenterX, sceneCenterY));
16666
+ surfacePatches.push({
16667
+ corners: projectedCorners,
16668
+ averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
16669
+ transformedCorners.length,
16670
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
16671
+ fillStyle: resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), resolveLowerLobeWave(centerLongitude, morphologyProfile, animationPhase, timeMs)),
16672
+ outlineColor: verticalProgress < 0.58 ? `${palette.highlight}73` : `${palette.shadow}8a`,
16673
+ });
16674
+ }
16377
16675
  }
16378
- const projectedPoints = scenePoints.map((scenePoint) => projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
16379
- context.beginPath();
16380
- context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
16381
- context.quadraticCurveTo(projectedPoints[1].x, projectedPoints[1].y, projectedPoints[2].x, projectedPoints[2].y);
16382
- context.strokeStyle = `${palette.ink}b8`;
16383
- context.lineWidth = Math.max(1.1, size * 0.009);
16384
- context.lineCap = 'round';
16385
- context.stroke();
16676
+ return surfacePatches;
16386
16677
  }
16387
16678
  /**
16388
- * Draws one filled projected quad.
16679
+ * Samples one point on the continuous Octopus 3D 2 surface.
16680
+ *
16681
+ * The lower hemisphere widens and falls into soft lobe waves so the octopus stays one connected mesh
16682
+ * instead of switching to separately rendered tentacles.
16683
+ *
16684
+ * @private helper of `octopus3d2AvatarVisual`
16685
+ */
16686
+ function sampleBlobbyOctopusSurfacePoint(options, latitude, longitude) {
16687
+ const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
16688
+ const cosineLatitude = Math.max(0, Math.cos(latitude));
16689
+ const verticalProgress = (Math.sin(latitude) + 1) / 2;
16690
+ const upperBlend = Math.pow(1 - verticalProgress, 1.2);
16691
+ const lowerBlend = Math.pow(verticalProgress, 1.42);
16692
+ const lowerLobeWave = resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, timeMs);
16693
+ const skirtEnvelope = Math.pow(cosineLatitude, 0.5) * lowerBlend;
16694
+ const horizontalScale = 1.02 +
16695
+ skirtEnvelope * (0.34 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.22 + lowerLobeWave * 0.22) -
16696
+ upperBlend * 0.08;
16697
+ const depthScale = 1.04 +
16698
+ upperBlend * 0.16 +
16699
+ Math.max(0, Math.cos(longitude)) * 0.1 +
16700
+ skirtEnvelope * (0.08 + lowerLobeWave * 0.06) -
16701
+ Math.max(0, -Math.cos(longitude)) * 0.04;
16702
+ const lowerDrop = skirtEnvelope *
16703
+ radiusY *
16704
+ (0.28 + lowerLobeWave * 0.14 + (morphologyProfile.tentacles.flowLengthScale - 1) * 0.12);
16705
+ const swayX = Math.sin(timeMs / 1250 + longitude * 1.8 + animationPhase) * skirtEnvelope * radiusX * 0.05;
16706
+ const swayZ = Math.cos(timeMs / 1480 + longitude * 1.2 - animationPhase * 0.7) * skirtEnvelope * radiusZ * 0.03;
16707
+ return {
16708
+ x: Math.sin(longitude) * cosineLatitude * radiusX * horizontalScale + swayX,
16709
+ y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.14) -
16710
+ upperBlend * radiusY * 0.1 +
16711
+ lowerDrop +
16712
+ Math.sin(timeMs / 1780 + animationPhase + latitude * 1.4) * skirtEnvelope * radiusY * 0.02,
16713
+ z: Math.cos(longitude) * cosineLatitude * radiusZ * depthScale + swayZ,
16714
+ };
16715
+ }
16716
+ /**
16717
+ * Resolves the soft lower-lobe wave that makes the silhouette read more like a real octopus.
16389
16718
  *
16390
- * @private helper of `octopus3dAvatarVisual`
16719
+ * @private helper of `octopus3d2AvatarVisual`
16391
16720
  */
16392
- function drawProjectedQuad(context, corners, fillStyle) {
16393
- context.beginPath();
16394
- context.moveTo(corners[0].x, corners[0].y);
16395
- context.lineTo(corners[1].x, corners[1].y);
16396
- context.lineTo(corners[2].x, corners[2].y);
16397
- context.lineTo(corners[3].x, corners[3].y);
16398
- context.closePath();
16399
- context.fillStyle = fillStyle;
16400
- context.fill();
16721
+ function resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, timeMs) {
16722
+ const lobeCount = Math.max(4, Math.round((morphologyProfile.body.lobeCount + morphologyProfile.tentacles.count) / 2));
16723
+ return (Math.cos(longitude * lobeCount + animationPhase + timeMs / 1040) + 1) / 2;
16401
16724
  }
16402
16725
  /**
16403
- * Converts an opacity ratio into a two-digit hexadecimal alpha suffix.
16726
+ * Resolves one base fill tone for a surface patch on the single octopus mesh.
16404
16727
  *
16405
- * @private helper of `octopus3dAvatarVisual`
16728
+ * @private helper of `octopus3d2AvatarVisual`
16406
16729
  */
16407
- function formatAlphaHex(opacity) {
16408
- return Math.round(clampNumber$1(opacity, 0, 1) * 255)
16409
- .toString(16)
16410
- .padStart(2, '0');
16730
+ function resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, forwardness, lowerLobeWave) {
16731
+ const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.12 - forwardness * 0.07, 0, 1);
16732
+ if (tonalProgress < 0.16) {
16733
+ return palette.highlight;
16734
+ }
16735
+ if (tonalProgress < 0.34) {
16736
+ return palette.secondary;
16737
+ }
16738
+ if (tonalProgress < 0.72) {
16739
+ return forwardness > 0.58 ? palette.secondary : palette.primary;
16740
+ }
16741
+ return `${palette.shadow}f2`;
16742
+ }
16743
+ /**
16744
+ * Draws one projected patch with soft octopus shading.
16745
+ *
16746
+ * @private helper of `octopus3d2AvatarVisual`
16747
+ */
16748
+ function drawBlobbySurfacePatch(context, surfacePatch) {
16749
+ drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
16750
+ if (surfacePatch.lightIntensity > 0) {
16751
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.16 * surfacePatch.lightIntensity})`);
16752
+ }
16753
+ else if (surfacePatch.lightIntensity < 0) {
16754
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.24 * Math.abs(surfacePatch.lightIntensity)})`);
16755
+ }
16756
+ context.save();
16757
+ context.beginPath();
16758
+ context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
16759
+ for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
16760
+ context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
16761
+ }
16762
+ context.closePath();
16763
+ context.strokeStyle = surfacePatch.outlineColor;
16764
+ context.lineWidth = Math.max(1, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0042);
16765
+ context.lineJoin = 'round';
16766
+ context.stroke();
16767
+ context.restore();
16411
16768
  }
16412
16769
 
16413
16770
  /* eslint-disable no-magic-numbers */
@@ -17179,6 +17536,7 @@ const AVATAR_VISUALS = [
17179
17536
  octopus2AvatarVisual,
17180
17537
  octopus3AvatarVisual,
17181
17538
  octopus3dAvatarVisual,
17539
+ octopus3d2AvatarVisual,
17182
17540
  asciiOctopusAvatarVisual,
17183
17541
  minecraftAvatarVisual,
17184
17542
  minecraft2AvatarVisual,
@@ -22754,11 +23112,11 @@ function buildCandidateEncodings(options) {
22754
23112
  });
22755
23113
  }
22756
23114
  /**
22757
- * Best-effort decoder for uploaded or remote file bytes whose extension or encoding may be unknown.
23115
+ * Prepares one attachment for best-effort text decoding.
22758
23116
  *
22759
- * @private internal utility for shared text decoding
23117
+ * @private function of decodeAttachmentAsText
22760
23118
  */
22761
- function decodeAttachmentAsText(input, options = {}) {
23119
+ function createDecodeAttachmentPreparation(input, options) {
22762
23120
  var _a;
22763
23121
  const maxBytes = Math.max(1, Math.floor((_a = options.maxBytes) !== null && _a !== void 0 ? _a : DEFAULT_ATTACHMENT_TEXT_DECODE_BYTES));
22764
23122
  const forceText = options.forceText === true;
@@ -22772,54 +23130,102 @@ function decodeAttachmentAsText(input, options = {}) {
22772
23130
  const inspection = inspectBytes(truncatedBytes);
22773
23131
  const trustedTextMime = isTrustedTextMimeType(mimeType);
22774
23132
  const trustedBinaryMime = isTrustedBinaryMimeType(mimeType);
23133
+ const shouldTreatAsBinary = (trustedBinaryMime || inspection.looksBinary) && !trustedTextMime;
22775
23134
  if (isTruncated) {
22776
23135
  warnings.push(`Decoded only the first ${maxBytes} bytes of \`${input.filename}\` because the attachment exceeded the text preview limit.`);
22777
23136
  }
22778
- const shouldTreatAsBinary = (trustedBinaryMime || inspection.looksBinary) && !trustedTextMime;
22779
- if (shouldTreatAsBinary && !forceText) {
22780
- warnings.push('File content looks binary, so text decoding was skipped.');
22781
- return {
22782
- text: '',
22783
- encodingUsed: 'binary',
22784
- confidence: 1,
22785
- warnings,
22786
- wasBinary: true,
22787
- isTruncated,
22788
- };
23137
+ return {
23138
+ warnings,
23139
+ charset,
23140
+ bom,
23141
+ inspection,
23142
+ truncatedBytes,
23143
+ isTruncated,
23144
+ forceText,
23145
+ shouldTreatAsBinary,
23146
+ };
23147
+ }
23148
+ /**
23149
+ * Returns an early result when the attachment should stay classified as binary.
23150
+ *
23151
+ * @private function of decodeAttachmentAsText
23152
+ */
23153
+ function createBinaryDecodeResult(preparation) {
23154
+ if (!preparation.shouldTreatAsBinary) {
23155
+ return null;
22789
23156
  }
22790
- if (shouldTreatAsBinary && forceText) {
22791
- warnings.push('File content looks binary, but text decoding was forced with `forceText`.');
23157
+ if (preparation.forceText) {
23158
+ preparation.warnings.push('File content looks binary, but text decoding was forced with `forceText`.');
23159
+ return null;
22792
23160
  }
22793
- if (charset && !isSupportedEncoding(charset)) {
22794
- warnings.push(`Ignored unsupported declared charset \`${charset}\` and used best-effort detection instead.`);
23161
+ preparation.warnings.push('File content looks binary, so text decoding was skipped.');
23162
+ return {
23163
+ text: '',
23164
+ encodingUsed: 'binary',
23165
+ confidence: 1,
23166
+ warnings: preparation.warnings,
23167
+ wasBinary: true,
23168
+ isTruncated: preparation.isTruncated,
23169
+ };
23170
+ }
23171
+ /**
23172
+ * Warns when the declared charset cannot be used by the runtime decoder.
23173
+ *
23174
+ * @private function of decodeAttachmentAsText
23175
+ */
23176
+ function addUnsupportedCharsetWarning(preparation) {
23177
+ if (preparation.charset && !isSupportedEncoding(preparation.charset)) {
23178
+ preparation.warnings.push(`Ignored unsupported declared charset \`${preparation.charset}\` and used best-effort detection instead.`);
22795
23179
  }
22796
- const bytesToDecode = bom ? truncatedBytes.subarray(bom.offset) : truncatedBytes;
22797
- const candidates = buildCandidateEncodings({
22798
- mimeType,
22799
- charset: charset && isSupportedEncoding(charset) ? charset : null,
22800
- bom,
22801
- inspection,
22802
- });
22803
- const decodedCandidates = candidates
23180
+ }
23181
+ /**
23182
+ * Returns the byte slice that should actually be decoded as text.
23183
+ *
23184
+ * @private function of decodeAttachmentAsText
23185
+ */
23186
+ function getBytesToDecode(preparation) {
23187
+ return preparation.bom ? preparation.truncatedBytes.subarray(preparation.bom.offset) : preparation.truncatedBytes;
23188
+ }
23189
+ /**
23190
+ * Decodes all candidate encodings and sorts the successful results by score.
23191
+ *
23192
+ * @private function of decodeAttachmentAsText
23193
+ */
23194
+ function decodeAttachmentCandidates(preparation) {
23195
+ return buildCandidateEncodings({
23196
+ charset: preparation.charset && isSupportedEncoding(preparation.charset) ? preparation.charset : null,
23197
+ bom: preparation.bom,
23198
+ inspection: preparation.inspection,
23199
+ })
22804
23200
  .map(({ encoding, source }) => {
22805
- const decoded = decodeWithEncoding(bytesToDecode, encoding);
23201
+ const decoded = decodeWithEncoding(getBytesToDecode(preparation), encoding);
22806
23202
  return decoded ? { ...decoded, source } : null;
22807
23203
  })
22808
23204
  .filter((candidate) => candidate !== null)
22809
23205
  .sort((left, right) => left.score - right.score);
22810
- const bestCandidate = decodedCandidates[0];
22811
- if (!bestCandidate) {
22812
- warnings.push('No supported text decoder was available.');
22813
- return {
22814
- text: '',
22815
- encodingUsed: 'binary',
22816
- confidence: 0,
22817
- warnings,
22818
- wasBinary: true,
22819
- isTruncated,
22820
- };
22821
- }
22822
- const secondBestCandidate = decodedCandidates[1];
23206
+ }
23207
+ /**
23208
+ * Returns the fallback result used when no text decoder could be applied.
23209
+ *
23210
+ * @private function of decodeAttachmentAsText
23211
+ */
23212
+ function createNoDecoderAvailableResult(preparation) {
23213
+ preparation.warnings.push('No supported text decoder was available.');
23214
+ return {
23215
+ text: '',
23216
+ encodingUsed: 'binary',
23217
+ confidence: 0,
23218
+ warnings: preparation.warnings,
23219
+ wasBinary: true,
23220
+ isTruncated: preparation.isTruncated,
23221
+ };
23222
+ }
23223
+ /**
23224
+ * Estimates confidence for the winning decoded text candidate.
23225
+ *
23226
+ * @private function of decodeAttachmentAsText
23227
+ */
23228
+ function computeDecodeConfidence(bestCandidate, secondBestCandidate, preparation) {
22823
23229
  const baseConfidence = bestCandidate.source === 'bom'
22824
23230
  ? 1
22825
23231
  : bestCandidate.source === 'charset'
@@ -22832,27 +23238,62 @@ function decodeAttachmentAsText(input, options = {}) {
22832
23238
  ? 0.82
22833
23239
  : 0.62;
22834
23240
  const scoreMargin = secondBestCandidate ? Math.max(0, secondBestCandidate.score - bestCandidate.score) : 0.2;
22835
- const confidence = Math.max(0.2, Math.min(shouldTreatAsBinary && forceText ? 0.45 : 1, baseConfidence + Math.min(0.18, scoreMargin / 2)));
23241
+ return Math.max(0.2, Math.min(preparation.shouldTreatAsBinary && preparation.forceText ? 0.45 : 1, baseConfidence + Math.min(0.18, scoreMargin / 2)));
23242
+ }
23243
+ /**
23244
+ * Appends user-facing warnings derived from the chosen decoded text candidate.
23245
+ *
23246
+ * @private function of decodeAttachmentAsText
23247
+ */
23248
+ function addDecodeWarnings(bestCandidate, confidence, preparation) {
22836
23249
  if (bestCandidate.source === 'heuristic' && bestCandidate.encoding !== 'utf-8') {
22837
- warnings.push(`Encoding was guessed as \`${bestCandidate.encoding}\`.`);
23250
+ preparation.warnings.push(`Encoding was guessed as \`${bestCandidate.encoding}\`.`);
22838
23251
  }
22839
23252
  if (bestCandidate.source === 'heuristic' &&
22840
23253
  bestCandidate.encoding === 'utf-8' &&
22841
23254
  bestCandidate.replacementCount > 0) {
22842
- warnings.push('UTF-8 decoding produced replacement characters, so the extracted text may contain errors.');
23255
+ preparation.warnings.push('UTF-8 decoding produced replacement characters, so the extracted text may contain errors.');
22843
23256
  }
22844
23257
  if (confidence < 0.6) {
22845
- warnings.push('Decoding confidence is low, so the extracted text may contain errors.');
23258
+ preparation.warnings.push('Decoding confidence is low, so the extracted text may contain errors.');
22846
23259
  }
23260
+ }
23261
+ /**
23262
+ * Creates the final decoded-text result from the chosen candidate and accumulated metadata.
23263
+ *
23264
+ * @private function of decodeAttachmentAsText
23265
+ */
23266
+ function createDecodedTextResult(bestCandidate, confidence, preparation) {
22847
23267
  return {
22848
- text: isTruncated ? appendTruncatedMarker(bestCandidate.text) : bestCandidate.text,
23268
+ text: preparation.isTruncated ? appendTruncatedMarker(bestCandidate.text) : bestCandidate.text,
22849
23269
  encodingUsed: bestCandidate.encoding,
22850
23270
  confidence,
22851
- warnings,
23271
+ warnings: preparation.warnings,
22852
23272
  wasBinary: false,
22853
- isTruncated,
23273
+ isTruncated: preparation.isTruncated,
22854
23274
  };
22855
23275
  }
23276
+ /**
23277
+ * Best-effort decoder for uploaded or remote file bytes whose extension or encoding may be unknown.
23278
+ *
23279
+ * @private internal utility for shared text decoding
23280
+ */
23281
+ function decodeAttachmentAsText(input, options = {}) {
23282
+ const preparation = createDecodeAttachmentPreparation(input, options);
23283
+ const binaryResult = createBinaryDecodeResult(preparation);
23284
+ if (binaryResult) {
23285
+ return binaryResult;
23286
+ }
23287
+ addUnsupportedCharsetWarning(preparation);
23288
+ const decodedCandidates = decodeAttachmentCandidates(preparation);
23289
+ const bestCandidate = decodedCandidates[0];
23290
+ if (!bestCandidate) {
23291
+ return createNoDecoderAvailableResult(preparation);
23292
+ }
23293
+ const confidence = computeDecodeConfidence(bestCandidate, decodedCandidates[1], preparation);
23294
+ addDecodeWarnings(bestCandidate, confidence, preparation);
23295
+ return createDecodedTextResult(bestCandidate, confidence, preparation);
23296
+ }
22856
23297
 
22857
23298
  /**
22858
23299
  * Base GitHub API URL.
@@ -34751,7 +35192,10 @@ class OpenAiCompatibleModelCatalog {
34751
35192
  Cannot find model in ${this.options.getTitle()} models with name "${defaultModelName}" which should be used as default.
34752
35193
 
34753
35194
  Available models:
34754
- ${block(this.options.getHardcodedModels().map(({ modelName }) => `- "${modelName}"`).join('\n'))}
35195
+ ${block(this.options
35196
+ .getHardcodedModels()
35197
+ .map(({ modelName }) => `- "${modelName}"`)
35198
+ .join('\n'))}
34755
35199
 
34756
35200
  Model "${defaultModelName}" is probably not available anymore, not installed, inaccessible or misconfigured.
34757
35201
 
@@ -35105,7 +35549,8 @@ class OpenAiCompatibleNonChatPromptCaller {
35105
35549
  };
35106
35550
  let rawPromptContent = templateParameters(content, { ...parameters, modelName });
35107
35551
  if ('attachments' in prompt && Array.isArray(prompt.attachments) && prompt.attachments.length > 0) {
35108
- rawPromptContent += '\n\n' + prompt.attachments.map((attachment) => `Image attachment: ${attachment.url}`).join('\n');
35552
+ rawPromptContent +=
35553
+ '\n\n' + prompt.attachments.map((attachment) => `Image attachment: ${attachment.url}`).join('\n');
35109
35554
  }
35110
35555
  const rawRequest = {
35111
35556
  ...modelSettings,
@@ -35210,7 +35655,9 @@ class OpenAiCompatibleRequestManager {
35210
35655
  * Schedules one request through the shared limiter and retry policy.
35211
35656
  */
35212
35657
  async executeRateLimitedRequest(requestFn) {
35213
- return this.limiter.schedule(() => this.makeRequestWithNetworkRetry(requestFn)).catch((error) => {
35658
+ return this.limiter
35659
+ .schedule(() => this.makeRequestWithNetworkRetry(requestFn))
35660
+ .catch((error) => {
35214
35661
  assertsError(error);
35215
35662
  if (this.options.isVerbose) {
35216
35663
  console.info(colors.bgRed('error'), error);
@@ -36417,7 +36864,9 @@ class OpenAiVectorStoreFileBatchPoller {
36417
36864
  pollingState.lastProgressAtMs = nowMs;
36418
36865
  pollingState.lastProgressKey = progressKey;
36419
36866
  }
36420
- if (this.options.isVerbose && (statusCountsKey !== pollingState.lastCountsKey || nowMs - pollingState.lastLogAtMs >= progressLogIntervalMs)) {
36867
+ if (this.options.isVerbose &&
36868
+ (statusCountsKey !== pollingState.lastCountsKey ||
36869
+ nowMs - pollingState.lastLogAtMs >= progressLogIntervalMs)) {
36421
36870
  console.info('[🤰]', 'Vector store file batch status', {
36422
36871
  vectorStoreId,
36423
36872
  batchId,