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

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/umd/index.umd.js CHANGED
@@ -50,7 +50,7 @@
50
50
  * @generated
51
51
  * @see https://github.com/webgptorg/promptbook
52
52
  */
53
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-73';
53
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-79';
54
54
  /**
55
55
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
56
56
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -300,6 +300,111 @@
300
300
  }
301
301
  }
302
302
 
303
+ /**
304
+ * Shared immutable channel storage and serialization helpers for `Color`.
305
+ *
306
+ * @private base class of Color
307
+ */
308
+ class ColorValue {
309
+ constructor(red, green, blue, alpha = 255) {
310
+ this.red = red;
311
+ this.green = green;
312
+ this.blue = blue;
313
+ this.alpha = alpha;
314
+ checkChannelValue('Red', red);
315
+ checkChannelValue('Green', green);
316
+ checkChannelValue('Blue', blue);
317
+ checkChannelValue('Alpha', alpha);
318
+ }
319
+ /**
320
+ * Shortcut for `red` property
321
+ * Number from 0 to 255
322
+ * @alias red
323
+ */
324
+ get r() {
325
+ return this.red;
326
+ }
327
+ /**
328
+ * Shortcut for `green` property
329
+ * Number from 0 to 255
330
+ * @alias green
331
+ */
332
+ get g() {
333
+ return this.green;
334
+ }
335
+ /**
336
+ * Shortcut for `blue` property
337
+ * Number from 0 to 255
338
+ * @alias blue
339
+ */
340
+ get b() {
341
+ return this.blue;
342
+ }
343
+ /**
344
+ * Shortcut for `alpha` property
345
+ * Number from 0 (transparent) to 255 (opaque)
346
+ * @alias alpha
347
+ */
348
+ get a() {
349
+ return this.alpha;
350
+ }
351
+ /**
352
+ * Shortcut for `alpha` property
353
+ * Number from 0 (transparent) to 255 (opaque)
354
+ * @alias alpha
355
+ */
356
+ get opacity() {
357
+ return this.alpha;
358
+ }
359
+ /**
360
+ * Shortcut for 1-`alpha` property
361
+ */
362
+ get transparency() {
363
+ return 255 - this.alpha;
364
+ }
365
+ clone() {
366
+ return take(this.createColor(this.red, this.green, this.blue, this.alpha));
367
+ }
368
+ toString() {
369
+ return this.toHex();
370
+ }
371
+ toHex() {
372
+ if (this.alpha === 255) {
373
+ return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
374
+ .toString(16)
375
+ .padStart(2, '0')}`;
376
+ }
377
+ else {
378
+ return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
379
+ .toString(16)
380
+ .padStart(2, '0')}${this.alpha.toString(16).padStart(2, '0')}`;
381
+ }
382
+ }
383
+ toRgb() {
384
+ if (this.alpha === 255) {
385
+ return `rgb(${this.red}, ${this.green}, ${this.blue})`;
386
+ }
387
+ else {
388
+ return `rgba(${this.red}, ${this.green}, ${this.blue}, ${Math.round((this.alpha / 255) * 100)}%)`;
389
+ }
390
+ }
391
+ toHsl() {
392
+ throw new Error(`Getting HSL is not implemented`);
393
+ }
394
+ }
395
+
396
+ /**
397
+ * Checks if the given value is a valid hex color string
398
+ *
399
+ * @param value - value to check
400
+ * @returns true if the value is a valid hex color string (e.g., `#009edd`, `#fff`, etc.)
401
+ *
402
+ * @private function of Color
403
+ */
404
+ function isHexColorString(value) {
405
+ 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));
406
+ }
407
+
303
408
  /**
304
409
  * Constant for short hex lengths.
305
410
  */
@@ -511,16 +616,53 @@
511
616
 
512
617
  /**
513
618
  * Pattern matching hsl regex.
619
+ *
620
+ * @private function of Color
514
621
  */
515
622
  const HSL_REGEX_PATTERN = /^hsl\(\s*([0-9.]+)\s*,\s*([0-9.]+)%\s*,\s*([0-9.]+)%\s*\)$/;
516
623
  /**
517
624
  * Pattern matching RGB regex.
625
+ *
626
+ * @private function of Color
518
627
  */
519
628
  const RGB_REGEX_PATTERN = /^rgb\(\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*\)$/;
520
629
  /**
521
630
  * Pattern matching rgba regex.
631
+ *
632
+ * @private function of Color
522
633
  */
523
634
  const RGBA_REGEX_PATTERN = /^rgba\(\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*\)$/;
635
+ /**
636
+ * Parses a supported color string into RGBA channels.
637
+ *
638
+ * @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`,...
639
+ * @returns RGBA channel values.
640
+ *
641
+ * @private function of Color
642
+ */
643
+ function parseColorString(color) {
644
+ const trimmed = color.trim();
645
+ const cssColor = CSS_COLORS[trimmed];
646
+ if (cssColor) {
647
+ return parseColorString(cssColor);
648
+ }
649
+ else if (isHexColorString(trimmed)) {
650
+ return parseHexColor(trimmed);
651
+ }
652
+ if (HSL_REGEX_PATTERN.test(trimmed)) {
653
+ return parseHslColor(trimmed);
654
+ }
655
+ else if (RGB_REGEX_PATTERN.test(trimmed)) {
656
+ return parseRgbColor(trimmed);
657
+ }
658
+ else if (RGBA_REGEX_PATTERN.test(trimmed)) {
659
+ return parseRgbaColor(trimmed);
660
+ }
661
+ else {
662
+ throw new Error(`Can not create a new Color instance from string "${trimmed}".`);
663
+ }
664
+ }
665
+
524
666
  /**
525
667
  * Color object represents an RGB color with alpha channel
526
668
  *
@@ -528,7 +670,7 @@
528
670
  *
529
671
  * @public exported from `@promptbook/color`
530
672
  */
531
- class Color {
673
+ class Color extends ColorValue {
532
674
  /**
533
675
  * Creates a new Color instance from miscellaneous formats
534
676
  * - It can receive Color instance and just return the same instance
@@ -601,25 +743,7 @@
601
743
  * @returns Color object
602
744
  */
603
745
  static fromString(color) {
604
- const trimmed = color.trim();
605
- if (CSS_COLORS[trimmed]) {
606
- return Color.fromString(CSS_COLORS[trimmed]);
607
- }
608
- else if (Color.isHexColorString(trimmed)) {
609
- return Color.fromHex(trimmed);
610
- }
611
- if (HSL_REGEX_PATTERN.test(trimmed)) {
612
- return Color.fromHsl(trimmed);
613
- }
614
- else if (RGB_REGEX_PATTERN.test(trimmed)) {
615
- return Color.fromRgbString(trimmed);
616
- }
617
- else if (RGBA_REGEX_PATTERN.test(trimmed)) {
618
- return Color.fromRgbaString(trimmed);
619
- }
620
- else {
621
- throw new Error(`Can not create a new Color instance from string "${trimmed}".`);
622
- }
746
+ return Color.fromColorChannels(parseColorString(color));
623
747
  }
624
748
  /**
625
749
  * Gets common color
@@ -649,8 +773,7 @@
649
773
  * @returns Color object
650
774
  */
651
775
  static fromHex(hex) {
652
- const { red, green, blue, alpha } = parseHexColor(hex);
653
- return take(new Color(red, green, blue, alpha));
776
+ return Color.fromColorChannels(parseHexColor(hex));
654
777
  }
655
778
  /**
656
779
  * Creates a new Color instance from color in hsl format
@@ -659,8 +782,7 @@
659
782
  * @returns Color object
660
783
  */
661
784
  static fromHsl(hsl) {
662
- const { red, green, blue, alpha } = parseHslColor(hsl);
663
- return take(new Color(red, green, blue, alpha));
785
+ return Color.fromColorChannels(parseHslColor(hsl));
664
786
  }
665
787
  /**
666
788
  * Creates a new Color instance from color in rgb format
@@ -669,8 +791,7 @@
669
791
  * @returns Color object
670
792
  */
671
793
  static fromRgbString(rgb) {
672
- const { red, green, blue, alpha } = parseRgbColor(rgb);
673
- return take(new Color(red, green, blue, alpha));
794
+ return Color.fromColorChannels(parseRgbColor(rgb));
674
795
  }
675
796
  /**
676
797
  * Creates a new Color instance from color in rbga format
@@ -679,8 +800,7 @@
679
800
  * @returns Color object
680
801
  */
681
802
  static fromRgbaString(rgba) {
682
- const { red, green, blue, alpha } = parseRgbaColor(rgba);
683
- return take(new Color(red, green, blue, alpha));
803
+ return Color.fromColorChannels(parseRgbaColor(rgba));
684
804
  }
685
805
  /**
686
806
  * Creates a new Color for color channels values
@@ -692,7 +812,7 @@
692
812
  * @returns Color object
693
813
  */
694
814
  static fromValues(red, green, blue, alpha = 255) {
695
- return take(new Color(red, green, blue, alpha));
815
+ return Color.fromColorChannels({ red, green, blue, alpha });
696
816
  }
697
817
  /**
698
818
  * Checks if the given value is a valid Color object.
@@ -725,8 +845,7 @@
725
845
  * @returns true if the value is a valid hex color string (e.g., `#009edd`, `#fff`, etc.)
726
846
  */
727
847
  static isHexColorString(value) {
728
- return (typeof value === 'string' &&
729
- /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value));
848
+ return isHexColorString(value);
730
849
  }
731
850
  /**
732
851
  * Creates new Color object
@@ -739,89 +858,13 @@
739
858
  * @param alpha number from 0 (transparent) to 255 (opaque)
740
859
  */
741
860
  constructor(red, green, blue, alpha = 255) {
742
- this.red = red;
743
- this.green = green;
744
- this.blue = blue;
745
- this.alpha = alpha;
746
- checkChannelValue('Red', red);
747
- checkChannelValue('Green', green);
748
- checkChannelValue('Blue', blue);
749
- checkChannelValue('Alpha', alpha);
750
- }
751
- /**
752
- * Shortcut for `red` property
753
- * Number from 0 to 255
754
- * @alias red
755
- */
756
- get r() {
757
- return this.red;
758
- }
759
- /**
760
- * Shortcut for `green` property
761
- * Number from 0 to 255
762
- * @alias green
763
- */
764
- get g() {
765
- return this.green;
766
- }
767
- /**
768
- * Shortcut for `blue` property
769
- * Number from 0 to 255
770
- * @alias blue
771
- */
772
- get b() {
773
- return this.blue;
774
- }
775
- /**
776
- * Shortcut for `alpha` property
777
- * Number from 0 (transparent) to 255 (opaque)
778
- * @alias alpha
779
- */
780
- get a() {
781
- return this.alpha;
782
- }
783
- /**
784
- * Shortcut for `alpha` property
785
- * Number from 0 (transparent) to 255 (opaque)
786
- * @alias alpha
787
- */
788
- get opacity() {
789
- return this.alpha;
790
- }
791
- /**
792
- * Shortcut for 1-`alpha` property
793
- */
794
- get transparency() {
795
- return 255 - this.alpha;
796
- }
797
- clone() {
798
- return take(new Color(this.red, this.green, this.blue, this.alpha));
799
- }
800
- toString() {
801
- return this.toHex();
802
- }
803
- toHex() {
804
- if (this.alpha === 255) {
805
- return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
806
- .toString(16)
807
- .padStart(2, '0')}`;
808
- }
809
- else {
810
- return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
811
- .toString(16)
812
- .padStart(2, '0')}${this.alpha.toString(16).padStart(2, '0')}`;
813
- }
861
+ super(red, green, blue, alpha);
814
862
  }
815
- toRgb() {
816
- if (this.alpha === 255) {
817
- return `rgb(${this.red}, ${this.green}, ${this.blue})`;
818
- }
819
- else {
820
- return `rgba(${this.red}, ${this.green}, ${this.blue}, ${Math.round((this.alpha / 255) * 100)}%)`;
821
- }
863
+ createColor(red, green, blue, alpha) {
864
+ return new Color(red, green, blue, alpha);
822
865
  }
823
- toHsl() {
824
- throw new Error(`Getting HSL is not implemented`);
866
+ static fromColorChannels({ red, green, blue, alpha }) {
867
+ return take(new Color(red, green, blue, alpha));
825
868
  }
826
869
  }
827
870
 
@@ -2181,120 +2224,183 @@
2181
2224
  * @public exported from `@promptbook/utils`
2182
2225
  */
2183
2226
  function checkSerializableAsJson(options) {
2184
- const { value, name, message } = options;
2227
+ checkSerializableValue(options);
2228
+ }
2229
+ // TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
2230
+ // TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
2231
+ // Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
2232
+ /**
2233
+ * Checks one value and dispatches to the appropriate specialized validator.
2234
+ *
2235
+ * @private function of `checkSerializableAsJson`
2236
+ */
2237
+ function checkSerializableValue(options) {
2238
+ const { value } = options;
2239
+ if (isSerializablePrimitive(value)) {
2240
+ return;
2241
+ }
2185
2242
  if (value === undefined) {
2186
- throw new UnexpectedError(`${name} is undefined`);
2243
+ throw new UnexpectedError(`${options.name} is undefined`);
2187
2244
  }
2188
- else if (value === null) {
2189
- return;
2245
+ if (typeof value === 'symbol') {
2246
+ throw new UnexpectedError(`${options.name} is symbol`);
2190
2247
  }
2191
- else if (typeof value === 'boolean') {
2192
- return;
2248
+ if (typeof value === 'function') {
2249
+ throw new UnexpectedError(`${options.name} is function`);
2193
2250
  }
2194
- else if (typeof value === 'number' && !isNaN(value)) {
2251
+ if (Array.isArray(value)) {
2252
+ checkSerializableArray(options, value);
2195
2253
  return;
2196
2254
  }
2197
- else if (typeof value === 'string') {
2255
+ if (value !== null && typeof value === 'object') {
2256
+ checkSerializableObject(options, value);
2198
2257
  return;
2199
2258
  }
2200
- else if (typeof value === 'symbol') {
2201
- throw new UnexpectedError(`${name} is symbol`);
2202
- }
2203
- else if (typeof value === 'function') {
2204
- throw new UnexpectedError(`${name} is function`);
2205
- }
2206
- else if (typeof value === 'object' && Array.isArray(value)) {
2207
- for (let i = 0; i < value.length; i++) {
2208
- checkSerializableAsJson({ name: `${name}[${i}]`, value: value[i], message });
2209
- }
2259
+ throwUnknownTypeError(options);
2260
+ }
2261
+ /**
2262
+ * Checks the primitive values that are directly JSON serializable.
2263
+ *
2264
+ * @private function of `checkSerializableAsJson`
2265
+ */
2266
+ function isSerializablePrimitive(value) {
2267
+ return (value === null ||
2268
+ typeof value === 'boolean' ||
2269
+ (typeof value === 'number' && !isNaN(value)) ||
2270
+ typeof value === 'string');
2271
+ }
2272
+ /**
2273
+ * Recursively checks JSON array items.
2274
+ *
2275
+ * @private function of `checkSerializableAsJson`
2276
+ */
2277
+ function checkSerializableArray(context, arrayValue) {
2278
+ for (let index = 0; index < arrayValue.length; index++) {
2279
+ checkSerializableAsJson({
2280
+ ...context,
2281
+ name: `${context.name}[${index}]`,
2282
+ value: arrayValue[index],
2283
+ });
2210
2284
  }
2211
- else if (typeof value === 'object') {
2212
- if (value instanceof Date) {
2213
- throw new UnexpectedError(_spaceTrim.spaceTrim((block) => `
2214
- \`${name}\` is Date
2285
+ }
2286
+ /**
2287
+ * Checks object-like values and dispatches special unsupported built-ins.
2288
+ *
2289
+ * @private function of `checkSerializableAsJson`
2290
+ */
2291
+ function checkSerializableObject(context, objectValue) {
2292
+ checkUnsupportedObjectType(context, objectValue);
2293
+ checkSerializableObjectEntries(context, objectValue);
2294
+ assertJsonStringificationSucceeds(context, objectValue);
2295
+ }
2296
+ /**
2297
+ * Rejects built-in objects that must be converted before JSON serialization.
2298
+ *
2299
+ * @private function of `checkSerializableAsJson`
2300
+ */
2301
+ function checkUnsupportedObjectType(context, objectValue) {
2302
+ if (objectValue instanceof Date) {
2303
+ throw new UnexpectedError(_spaceTrim.spaceTrim((block) => `
2304
+ \`${context.name}\` is Date
2215
2305
 
2216
- Use \`string_date_iso8601\` instead
2306
+ Use \`string_date_iso8601\` instead
2217
2307
 
2218
- Additional message for \`${name}\`:
2219
- ${block(message || '(nothing)')}
2220
- `));
2221
- }
2222
- else if (value instanceof Map) {
2223
- throw new UnexpectedError(`${name} is Map`);
2224
- }
2225
- else if (value instanceof Set) {
2226
- throw new UnexpectedError(`${name} is Set`);
2227
- }
2228
- else if (value instanceof RegExp) {
2229
- throw new UnexpectedError(`${name} is RegExp`);
2230
- }
2231
- else if (value instanceof Error) {
2232
- throw new UnexpectedError(_spaceTrim.spaceTrim((block) => `
2233
- \`${name}\` is unserialized Error
2308
+ Additional message for \`${context.name}\`:
2309
+ ${block(context.message || '(nothing)')}
2310
+ `));
2311
+ }
2312
+ if (objectValue instanceof Map) {
2313
+ throw new UnexpectedError(`${context.name} is Map`);
2314
+ }
2315
+ if (objectValue instanceof Set) {
2316
+ throw new UnexpectedError(`${context.name} is Set`);
2317
+ }
2318
+ if (objectValue instanceof RegExp) {
2319
+ throw new UnexpectedError(`${context.name} is RegExp`);
2320
+ }
2321
+ if (objectValue instanceof Error) {
2322
+ throw new UnexpectedError(_spaceTrim.spaceTrim((block) => `
2323
+ \`${context.name}\` is unserialized Error
2234
2324
 
2235
- Use function \`serializeError\`
2325
+ Use function \`serializeError\`
2236
2326
 
2237
- Additional message for \`${name}\`:
2238
- ${block(message || '(nothing)')}
2327
+ Additional message for \`${context.name}\`:
2328
+ ${block(context.message || '(nothing)')}
2239
2329
 
2240
- `));
2330
+ `));
2331
+ }
2332
+ }
2333
+ /**
2334
+ * Recursively checks object properties while preserving omitted `undefined` keys.
2335
+ *
2336
+ * @private function of `checkSerializableAsJson`
2337
+ */
2338
+ function checkSerializableObjectEntries(context, objectValue) {
2339
+ for (const [subName, subValue] of Object.entries(objectValue)) {
2340
+ if (subValue === undefined) {
2341
+ // Note: undefined in object is serializable - it is just omitted
2342
+ continue;
2241
2343
  }
2242
- else {
2243
- for (const [subName, subValue] of Object.entries(value)) {
2244
- if (subValue === undefined) {
2245
- // Note: undefined in object is serializable - it is just omitted
2246
- continue;
2247
- }
2248
- checkSerializableAsJson({ name: `${name}.${subName}`, value: subValue, message });
2249
- }
2250
- try {
2251
- JSON.stringify(value); // <- TODO: [0]
2252
- }
2253
- catch (error) {
2254
- assertsError(error);
2255
- throw new UnexpectedError(_spaceTrim.spaceTrim((block) => `
2256
- \`${name}\` is not serializable
2344
+ checkSerializableAsJson({
2345
+ ...context,
2346
+ name: `${context.name}.${subName}`,
2347
+ value: subValue,
2348
+ });
2349
+ }
2350
+ }
2351
+ /**
2352
+ * Uses `JSON.stringify` as the final guard for cases like circular references.
2353
+ *
2354
+ * @private function of `checkSerializableAsJson`
2355
+ */
2356
+ function assertJsonStringificationSucceeds(context, objectValue) {
2357
+ try {
2358
+ JSON.stringify(objectValue); // <- TODO: [0]
2359
+ }
2360
+ catch (error) {
2361
+ assertsError(error);
2362
+ throw new UnexpectedError(_spaceTrim.spaceTrim((block) => `
2363
+ \`${context.name}\` is not serializable
2257
2364
 
2258
- ${block(error.stack || error.message)}
2365
+ ${block(error.stack || error.message)}
2259
2366
 
2260
- Additional message for \`${name}\`:
2261
- ${block(message || '(nothing)')}
2262
- `));
2367
+ Additional message for \`${context.name}\`:
2368
+ ${block(context.message || '(nothing)')}
2369
+ `));
2370
+ }
2371
+ /*
2372
+ TODO: [0] Is there some more elegant way to check circular references?
2373
+ const seen = new Set();
2374
+ const stack = [{ value }];
2375
+ while (stack.length > 0) {
2376
+ const { value } = stack.pop()!;
2377
+ if (typeof value === 'object' && value !== null) {
2378
+ if (seen.has(value)) {
2379
+ throw new UnexpectedError(`${name} has circular reference`);
2263
2380
  }
2264
- /*
2265
- TODO: [0] Is there some more elegant way to check circular references?
2266
- const seen = new Set();
2267
- const stack = [{ value }];
2268
- while (stack.length > 0) {
2269
- const { value } = stack.pop()!;
2270
- if (typeof value === 'object' && value !== null) {
2271
- if (seen.has(value)) {
2272
- throw new UnexpectedError(`${name} has circular reference`);
2273
- }
2274
- seen.add(value);
2275
- if (Array.isArray(value)) {
2276
- stack.push(...value.map((value) => ({ value })));
2277
- } else {
2278
- stack.push(...Object.values(value).map((value) => ({ value })));
2279
- }
2280
- }
2381
+ seen.add(value);
2382
+ if (Array.isArray(value)) {
2383
+ stack.push(...value.map((value) => ({ value })));
2384
+ } else {
2385
+ stack.push(...Object.values(value).map((value) => ({ value })));
2281
2386
  }
2282
- */
2283
- return;
2284
2387
  }
2285
2388
  }
2286
- else {
2287
- throw new UnexpectedError(_spaceTrim.spaceTrim((block) => `
2288
- \`${name}\` is unknown type
2389
+ */
2390
+ }
2391
+ /**
2392
+ * Throws the fallback error for unsupported value types like `bigint` and `NaN`.
2393
+ *
2394
+ * @private function of `checkSerializableAsJson`
2395
+ */
2396
+ function throwUnknownTypeError(context) {
2397
+ throw new UnexpectedError(_spaceTrim.spaceTrim((block) => `
2398
+ \`${context.name}\` is unknown type
2289
2399
 
2290
- Additional message for \`${name}\`:
2291
- ${block(message || '(nothing)')}
2292
- `));
2293
- }
2400
+ Additional message for \`${context.name}\`:
2401
+ ${block(context.message || '(nothing)')}
2402
+ `));
2294
2403
  }
2295
- // TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
2296
- // TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
2297
- // Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
2298
2404
 
2299
2405
  /**
2300
2406
  * Creates a deep clone of the given object
@@ -2927,8 +3033,7 @@
2927
3033
  * @private internal utility of `validatePipeline`
2928
3034
  */
2929
3035
  function validateTaskSupportsJokers(task, pipelineIdentification) {
2930
- if (task.format ||
2931
- task.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
3036
+ if (task.format || task.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
2932
3037
  return;
2933
3038
  }
2934
3039
  throw new PipelineLogicError(_spaceTrim.spaceTrim((block) => `
@@ -4045,7 +4150,7 @@
4045
4150
  */
4046
4151
  function createPostprocessingCommands(task) {
4047
4152
  var _a;
4048
- return ((_a = task.postprocessingFunctionNames) === null || _a === void 0 ? void 0 : _a.map((postprocessingFunctionName) => `POSTPROCESSING \`${postprocessingFunctionName}\``)) || [];
4153
+ return (((_a = task.postprocessingFunctionNames) === null || _a === void 0 ? void 0 : _a.map((postprocessingFunctionName) => `POSTPROCESSING \`${postprocessingFunctionName}\``)) || []);
4049
4154
  }
4050
4155
  /**
4051
4156
  * Collects expectation commands.
@@ -7476,9 +7581,7 @@
7476
7581
  ${block(quoteMultilineText(((_b = failure.error) === null || _b === void 0 ? void 0 : _b.message) || ''))}
7477
7582
 
7478
7583
  Result:
7479
- ${block(failure.result === null
7480
- ? 'null'
7481
- : quoteMultilineText(_spaceTrim.spaceTrim(failure.result)))}
7584
+ ${block(failure.result === null ? 'null' : quoteMultilineText(_spaceTrim.spaceTrim(failure.result)))}
7482
7585
  `;
7483
7586
  }))
7484
7587
  .join('\n\n---\n\n');
@@ -9060,10 +9163,7 @@
9060
9163
  * @private internal function of `$registeredLlmToolsMessage`
9061
9164
  */
9062
9165
  function createProviderStatusMessages(llmToolStatus, env) {
9063
- return [
9064
- createInstallationStatusMessage(llmToolStatus),
9065
- createConfigurationStatusMessage(llmToolStatus, env),
9066
- ];
9166
+ return [createInstallationStatusMessage(llmToolStatus), createConfigurationStatusMessage(llmToolStatus, env)];
9067
9167
  }
9068
9168
  /**
9069
9169
  * Creates the installation-status sentence for one provider.
@@ -14626,7 +14726,7 @@
14626
14726
  *
14627
14727
  * @private helper of `minecraft2AvatarVisual`
14628
14728
  */
14629
- const LIGHT_DIRECTION$1 = normalizeVector3({
14729
+ const LIGHT_DIRECTION$2 = normalizeVector3({
14630
14730
  x: 0.4,
14631
14731
  y: -0.65,
14632
14732
  z: 0.92,
@@ -14838,7 +14938,7 @@
14838
14938
  corners: projectedCorners,
14839
14939
  texture: faceDefinition.texture,
14840
14940
  averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
14841
- lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$1), -1, 1),
14941
+ lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$2), -1, 1),
14842
14942
  outlineColor: cuboid.outlineColor,
14843
14943
  };
14844
14944
  });
@@ -15898,13 +15998,138 @@
15898
15998
  context.restore();
15899
15999
  }
15900
16000
 
16001
+ /* eslint-disable no-magic-numbers */
16002
+ /**
16003
+ * Draws one projected eye on a rotated octopus surface.
16004
+ *
16005
+ * @private helper of the 3D octopus avatar visuals
16006
+ */
16007
+ function drawProjectedOrganicEye(context, localCenter, radiusX, radiusY, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, phase, interaction, eyeStyle) {
16008
+ const centerScenePoint = transformScenePoint(localCenter, center, rotationX, rotationY);
16009
+ if (centerScenePoint.z <= center.z) {
16010
+ return;
16011
+ }
16012
+ const horizontalScenePoint = transformScenePoint({ x: localCenter.x + radiusX, y: localCenter.y, z: localCenter.z }, center, rotationX, rotationY);
16013
+ const verticalScenePoint = transformScenePoint({ x: localCenter.x, y: localCenter.y + radiusY, z: localCenter.z }, center, rotationX, rotationY);
16014
+ const projectedCenterPoint = projectScenePoint(centerScenePoint, size, sceneCenterX, sceneCenterY);
16015
+ const projectedHorizontalPoint = projectScenePoint(horizontalScenePoint, size, sceneCenterX, sceneCenterY);
16016
+ const projectedVerticalPoint = projectScenePoint(verticalScenePoint, size, sceneCenterX, sceneCenterY);
16017
+ const projectedRadiusX = Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y);
16018
+ const projectedRadiusY = Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y);
16019
+ if (projectedRadiusX < size * 0.008 || projectedRadiusY < size * 0.008) {
16020
+ return;
16021
+ }
16022
+ const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
16023
+ radiusX: projectedRadiusX,
16024
+ radiusY: projectedRadiusY,
16025
+ timeMs,
16026
+ phase,
16027
+ interaction,
16028
+ });
16029
+ const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
16030
+ context.save();
16031
+ context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
16032
+ context.rotate(rotation);
16033
+ context.beginPath();
16034
+ context.ellipse(0, 0, projectedRadiusX, projectedRadiusY, 0, 0, Math.PI * 2);
16035
+ context.fillStyle = '#f8fbff';
16036
+ context.fill();
16037
+ context.clip();
16038
+ const irisGradient = context.createRadialGradient(-projectedRadiusX * 0.2, -projectedRadiusY * 0.26, projectedRadiusX * 0.05, 0, 0, projectedRadiusX * 0.92);
16039
+ irisGradient.addColorStop(0, palette.highlight);
16040
+ irisGradient.addColorStop(0.56, palette.secondary);
16041
+ irisGradient.addColorStop(1, palette.shadow);
16042
+ context.beginPath();
16043
+ context.ellipse(pupilOffsetX, pupilOffsetY, projectedRadiusX * 0.62 * eyeStyle.irisScale, projectedRadiusY * 0.72 * eyeStyle.irisScale, 0, 0, Math.PI * 2);
16044
+ context.fillStyle = irisGradient;
16045
+ context.fill();
16046
+ context.beginPath();
16047
+ context.ellipse(pupilOffsetX, pupilOffsetY, projectedRadiusX * 0.15 * eyeStyle.pupilWidthScale, projectedRadiusY * 0.48 * eyeStyle.pupilHeightScale, 0, 0, Math.PI * 2);
16048
+ context.fillStyle = palette.ink;
16049
+ context.fill();
16050
+ context.beginPath();
16051
+ context.ellipse(pupilOffsetX - projectedRadiusX * 0.22, pupilOffsetY - projectedRadiusY * 0.24, projectedRadiusX * 0.12, projectedRadiusY * 0.14, 0, 0, Math.PI * 2);
16052
+ context.fillStyle = '#ffffff';
16053
+ context.fill();
16054
+ context.restore();
16055
+ context.save();
16056
+ context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
16057
+ context.rotate(rotation);
16058
+ context.beginPath();
16059
+ context.ellipse(0, 0, projectedRadiusX, projectedRadiusY, 0, 0, Math.PI * 2);
16060
+ context.strokeStyle = `${palette.shadow}cc`;
16061
+ context.lineWidth = projectedRadiusX * 0.16;
16062
+ context.stroke();
16063
+ context.beginPath();
16064
+ context.moveTo(-projectedRadiusX * 0.88, -projectedRadiusY * eyeStyle.upperLidInsetRatio);
16065
+ context.quadraticCurveTo(0, -projectedRadiusY * (eyeStyle.upperLidArchRatio - interaction.gazeY * 0.16 + interaction.intensity * 0.08), projectedRadiusX * 0.88, -projectedRadiusY * eyeStyle.upperLidInsetRatio);
16066
+ context.strokeStyle = `${palette.shadow}73`;
16067
+ context.lineWidth = projectedRadiusX * 0.14;
16068
+ context.lineCap = 'round';
16069
+ context.stroke();
16070
+ if (eyeStyle.lowerLidOpacity > 0) {
16071
+ context.beginPath();
16072
+ context.moveTo(-projectedRadiusX * 0.74, projectedRadiusY * 0.2);
16073
+ context.quadraticCurveTo(0, projectedRadiusY * 0.38, projectedRadiusX * 0.74, projectedRadiusY * 0.2);
16074
+ context.strokeStyle = `${palette.highlight}${formatAlphaHex(eyeStyle.lowerLidOpacity)}`;
16075
+ context.lineWidth = projectedRadiusX * 0.08;
16076
+ context.lineCap = 'round';
16077
+ context.stroke();
16078
+ }
16079
+ context.restore();
16080
+ }
16081
+ /**
16082
+ * Draws a subtle projected mouth arc across the front of a rotated octopus surface.
16083
+ *
16084
+ * @private helper of the 3D octopus avatar visuals
16085
+ */
16086
+ function drawProjectedOrganicMouth(context, localPoints, center, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size) {
16087
+ const scenePoints = localPoints.map((localPoint) => transformScenePoint(localPoint, center, rotationX, rotationY));
16088
+ if (scenePoints.some((scenePoint) => scenePoint.z <= center.z)) {
16089
+ return;
16090
+ }
16091
+ const projectedPoints = scenePoints.map((scenePoint) => projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
16092
+ context.beginPath();
16093
+ context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
16094
+ context.quadraticCurveTo(projectedPoints[1].x, projectedPoints[1].y, projectedPoints[2].x, projectedPoints[2].y);
16095
+ context.strokeStyle = `${palette.ink}b8`;
16096
+ context.lineWidth = Math.max(1.1, size * 0.009);
16097
+ context.lineCap = 'round';
16098
+ context.stroke();
16099
+ }
16100
+ /**
16101
+ * Draws one filled projected quad.
16102
+ *
16103
+ * @private helper of the 3D octopus avatar visuals
16104
+ */
16105
+ function drawProjectedQuad(context, corners, fillStyle) {
16106
+ context.beginPath();
16107
+ context.moveTo(corners[0].x, corners[0].y);
16108
+ context.lineTo(corners[1].x, corners[1].y);
16109
+ context.lineTo(corners[2].x, corners[2].y);
16110
+ context.lineTo(corners[3].x, corners[3].y);
16111
+ context.closePath();
16112
+ context.fillStyle = fillStyle;
16113
+ context.fill();
16114
+ }
16115
+ /**
16116
+ * Converts an opacity ratio into a two-digit hexadecimal alpha suffix.
16117
+ *
16118
+ * @private helper of the 3D octopus avatar visuals
16119
+ */
16120
+ function formatAlphaHex(opacity) {
16121
+ return Math.round(clampNumber$1(opacity, 0, 1) * 255)
16122
+ .toString(16)
16123
+ .padStart(2, '0');
16124
+ }
16125
+
15901
16126
  /* eslint-disable no-magic-numbers */
15902
16127
  /**
15903
16128
  * Light direction used by the organic 3D octopus shading.
15904
16129
  *
15905
16130
  * @private helper of `octopus3dAvatarVisual`
15906
16131
  */
15907
- const LIGHT_DIRECTION = normalizeVector3({
16132
+ const LIGHT_DIRECTION$1 = normalizeVector3({
15908
16133
  x: 0.48,
15909
16134
  y: -0.62,
15910
16135
  z: 0.94,
@@ -16017,17 +16242,17 @@
16017
16242
  for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => candidateTentacleStroke.isFrontFacing)) {
16018
16243
  drawTentacleStroke(context, tentacleStroke, palette);
16019
16244
  }
16020
- drawProjectedEye(context, {
16245
+ drawProjectedOrganicEye(context, {
16021
16246
  x: -faceEyeSpacing,
16022
16247
  y: faceEyeYOffset,
16023
16248
  z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
16024
16249
  }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
16025
- drawProjectedEye(context, {
16250
+ drawProjectedOrganicEye(context, {
16026
16251
  x: faceEyeSpacing,
16027
16252
  y: faceEyeYOffset,
16028
16253
  z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
16029
16254
  }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
16030
- drawProjectedMouth(context, [
16255
+ drawProjectedOrganicMouth(context, [
16031
16256
  {
16032
16257
  x: -mouthHalfWidth,
16033
16258
  y: mouthY,
@@ -16114,7 +16339,7 @@
16114
16339
  corners: projectedCorners,
16115
16340
  averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
16116
16341
  transformedCorners.length,
16117
- lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
16342
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
16118
16343
  fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
16119
16344
  outlineColor,
16120
16345
  });
@@ -16296,128 +16521,260 @@
16296
16521
  const remainingDepthRatio = Math.max(0, 1 - normalizedX * normalizedX - normalizedY * normalizedY);
16297
16522
  return Math.sqrt(remainingDepthRatio) * radiusZ;
16298
16523
  }
16524
+
16525
+ /* eslint-disable no-magic-numbers */
16299
16526
  /**
16300
- * Draws one projected eye on the turned octopus mantle.
16527
+ * Light direction used by the single-mesh octopus shading.
16301
16528
  *
16302
- * @private helper of `octopus3dAvatarVisual`
16529
+ * @private helper of `octopus3d2AvatarVisual`
16303
16530
  */
16304
- function drawProjectedEye(context, localCenter, radiusX, radiusY, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, phase, interaction, eyeStyle) {
16305
- const centerScenePoint = transformScenePoint(localCenter, center, rotationX, rotationY);
16306
- if (centerScenePoint.z <= center.z) {
16307
- return;
16308
- }
16309
- const horizontalScenePoint = transformScenePoint({ x: localCenter.x + radiusX, y: localCenter.y, z: localCenter.z }, center, rotationX, rotationY);
16310
- const verticalScenePoint = transformScenePoint({ x: localCenter.x, y: localCenter.y + radiusY, z: localCenter.z }, center, rotationX, rotationY);
16311
- const projectedCenterPoint = projectScenePoint(centerScenePoint, size, sceneCenterX, sceneCenterY);
16312
- const projectedHorizontalPoint = projectScenePoint(horizontalScenePoint, size, sceneCenterX, sceneCenterY);
16313
- const projectedVerticalPoint = projectScenePoint(verticalScenePoint, size, sceneCenterX, sceneCenterY);
16314
- const projectedRadiusX = Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y);
16315
- const projectedRadiusY = Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y);
16316
- if (projectedRadiusX < size * 0.008 || projectedRadiusY < size * 0.008) {
16317
- return;
16318
- }
16319
- const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
16320
- radiusX: projectedRadiusX,
16321
- radiusY: projectedRadiusY,
16322
- timeMs,
16323
- phase,
16324
- interaction,
16325
- });
16326
- const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
16531
+ const LIGHT_DIRECTION = normalizeVector3({
16532
+ x: 0.38,
16533
+ y: -0.6,
16534
+ z: 0.98,
16535
+ });
16536
+ /**
16537
+ * Octopus 3D 2 avatar visual.
16538
+ *
16539
+ * @private built-in avatar visual
16540
+ */
16541
+ const octopus3d2AvatarVisual = {
16542
+ id: 'octopus3d2',
16543
+ title: 'Octopus 3D 2',
16544
+ description: 'Continuous blobby 3D octopus portrait with one soft mesh, turning silhouette, and cursor-aware eyes.',
16545
+ isAnimated: true,
16546
+ supportsPointerTracking: true,
16547
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
16548
+ const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
16549
+ const animationRandom = createRandom('octopus3d2-animation-profile');
16550
+ const eyeRandom = createRandom('octopus3d2-eye-profile');
16551
+ const animationPhase = animationRandom() * Math.PI * 2;
16552
+ const sceneCenterX = size * 0.5;
16553
+ const sceneCenterY = size * 0.575;
16554
+ const bob = Math.sin(timeMs / 940 + animationPhase) * size * 0.013;
16555
+ const meshCenter = {
16556
+ x: interaction.bodyOffsetX * size * 0.044 + size * morphologyProfile.body.centerXJitterRatio * 0.5,
16557
+ y: -size * 0.03 + interaction.bodyOffsetY * size * 0.026 + bob,
16558
+ z: interaction.intensity * size * 0.018,
16559
+ };
16560
+ const rotationY = -0.14 +
16561
+ Math.sin(timeMs / 2600 + animationPhase) * 0.04 +
16562
+ interaction.bodyOffsetX * 0.2 +
16563
+ interaction.gazeX * 0.78;
16564
+ const rotationX = -0.06 +
16565
+ Math.cos(timeMs / 3000 + animationPhase * 0.7) * 0.02 -
16566
+ interaction.bodyOffsetY * 0.08 -
16567
+ interaction.gazeY * 0.34;
16568
+ const surfaceOptions = {
16569
+ radiusX: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch * 1.02,
16570
+ radiusY: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.22,
16571
+ radiusZ: size *
16572
+ morphologyProfile.body.bodyRadiusRatio *
16573
+ (0.98 + (morphologyProfile.body.horizontalStretch - 1) * 0.2),
16574
+ morphologyProfile,
16575
+ timeMs,
16576
+ animationPhase,
16577
+ };
16578
+ const surfacePatches = resolveVisibleBlobbyOctopusPatches({
16579
+ ...surfaceOptions,
16580
+ center: meshCenter,
16581
+ rotationX,
16582
+ rotationY,
16583
+ sceneCenterX,
16584
+ sceneCenterY,
16585
+ size,
16586
+ palette,
16587
+ });
16588
+ const eyeLatitude = clampNumber$1(morphologyProfile.face.eyeCenterYOffsetRatio * 4.4, -0.16, 0.11);
16589
+ const eyeLongitude = clampNumber$1(morphologyProfile.face.eyeSpacingRatio * 3.25, 0.2, 0.34);
16590
+ const mouthLatitude = clampNumber$1(eyeLatitude + 0.19 + morphologyProfile.face.mouthYOffsetRatio * 1.08, 0.08, 0.34);
16591
+ const mouthCenterLongitude = clampNumber$1(morphologyProfile.face.mouthCenterOffsetRatio * 5.8, -0.08, 0.08);
16592
+ const mouthHalfLongitude = clampNumber$1(eyeLongitude * 0.82, 0.16, 0.29);
16593
+ const mouthCurveLatitude = clampNumber$1(mouthLatitude + morphologyProfile.face.mouthCurveDepthRatio * 0.85, mouthLatitude + 0.03, 0.42);
16594
+ drawAvatarFrame(context, size, palette);
16595
+ drawBlobbyOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
16596
+ drawBlobbyOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile);
16597
+ for (const surfacePatch of surfacePatches.sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
16598
+ drawBlobbySurfacePatch(context, surfacePatch);
16599
+ }
16600
+ const leftEyeLocalCenter = sampleBlobbyOctopusSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude);
16601
+ const rightEyeLocalCenter = sampleBlobbyOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude);
16602
+ const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.78;
16603
+ const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.92;
16604
+ drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
16605
+ drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
16606
+ drawProjectedOrganicMouth(context, [
16607
+ sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
16608
+ sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
16609
+ sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude + mouthHalfLongitude),
16610
+ ], meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size);
16611
+ },
16612
+ };
16613
+ /**
16614
+ * Draws the deep-water glow behind the continuous octopus mesh.
16615
+ *
16616
+ * @private helper of `octopus3d2AvatarVisual`
16617
+ */
16618
+ function drawBlobbyOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
16619
+ 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);
16620
+ glowGradient.addColorStop(0, `${palette.highlight}5e`);
16621
+ glowGradient.addColorStop(0.38, `${palette.accent}26`);
16622
+ glowGradient.addColorStop(1, `${palette.highlight}00`);
16623
+ context.fillStyle = glowGradient;
16624
+ context.fillRect(0, 0, size, size);
16625
+ 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);
16626
+ lowerGradient.addColorStop(0, `${palette.secondary}22`);
16627
+ lowerGradient.addColorStop(1, `${palette.secondary}00`);
16628
+ context.fillStyle = lowerGradient;
16629
+ context.fillRect(0, 0, size, size);
16630
+ }
16631
+ /**
16632
+ * Draws the soft floor shadow that anchors the single mesh in the frame.
16633
+ *
16634
+ * @private helper of `octopus3d2AvatarVisual`
16635
+ */
16636
+ function drawBlobbyOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
16327
16637
  context.save();
16328
- context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
16329
- context.rotate(rotation);
16330
- context.beginPath();
16331
- context.ellipse(0, 0, projectedRadiusX, projectedRadiusY, 0, 0, Math.PI * 2);
16332
- context.fillStyle = '#f8fbff';
16333
- context.fill();
16334
- context.clip();
16335
- const irisGradient = context.createRadialGradient(-projectedRadiusX * 0.2, -projectedRadiusY * 0.26, projectedRadiusX * 0.05, 0, 0, projectedRadiusX * 0.92);
16336
- irisGradient.addColorStop(0, palette.highlight);
16337
- irisGradient.addColorStop(0.56, palette.secondary);
16338
- irisGradient.addColorStop(1, palette.shadow);
16339
- context.beginPath();
16340
- context.ellipse(pupilOffsetX, pupilOffsetY, projectedRadiusX * 0.62 * eyeStyle.irisScale, projectedRadiusY * 0.72 * eyeStyle.irisScale, 0, 0, Math.PI * 2);
16341
- context.fillStyle = irisGradient;
16342
- context.fill();
16638
+ context.fillStyle = `${palette.shadow}66`;
16639
+ context.filter = `blur(${size * 0.024}px)`;
16343
16640
  context.beginPath();
16344
- context.ellipse(pupilOffsetX, pupilOffsetY, projectedRadiusX * 0.15 * eyeStyle.pupilWidthScale, projectedRadiusY * 0.48 * eyeStyle.pupilHeightScale, 0, 0, Math.PI * 2);
16345
- context.fillStyle = palette.ink;
16641
+ 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);
16346
16642
  context.fill();
16347
- context.beginPath();
16348
- context.ellipse(pupilOffsetX - projectedRadiusX * 0.22, pupilOffsetY - projectedRadiusY * 0.24, projectedRadiusX * 0.12, projectedRadiusY * 0.14, 0, 0, Math.PI * 2);
16349
- context.fillStyle = '#ffffff';
16350
- context.fill();
16351
- context.restore();
16352
- context.save();
16353
- context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
16354
- context.rotate(rotation);
16355
- context.beginPath();
16356
- context.ellipse(0, 0, projectedRadiusX, projectedRadiusY, 0, 0, Math.PI * 2);
16357
- context.strokeStyle = `${palette.shadow}cc`;
16358
- context.lineWidth = projectedRadiusX * 0.16;
16359
- context.stroke();
16360
- context.beginPath();
16361
- context.moveTo(-projectedRadiusX * 0.88, -projectedRadiusY * eyeStyle.upperLidInsetRatio);
16362
- context.quadraticCurveTo(0, -projectedRadiusY * (eyeStyle.upperLidArchRatio - interaction.gazeY * 0.16 + interaction.intensity * 0.08), projectedRadiusX * 0.88, -projectedRadiusY * eyeStyle.upperLidInsetRatio);
16363
- context.strokeStyle = `${palette.shadow}73`;
16364
- context.lineWidth = projectedRadiusX * 0.14;
16365
- context.lineCap = 'round';
16366
- context.stroke();
16367
- if (eyeStyle.lowerLidOpacity > 0) {
16368
- context.beginPath();
16369
- context.moveTo(-projectedRadiusX * 0.74, projectedRadiusY * 0.2);
16370
- context.quadraticCurveTo(0, projectedRadiusY * 0.38, projectedRadiusX * 0.74, projectedRadiusY * 0.2);
16371
- context.strokeStyle = `${palette.highlight}${formatAlphaHex(eyeStyle.lowerLidOpacity)}`;
16372
- context.lineWidth = projectedRadiusX * 0.08;
16373
- context.lineCap = 'round';
16374
- context.stroke();
16375
- }
16376
16643
  context.restore();
16377
16644
  }
16378
16645
  /**
16379
- * Draws a subtle projected mouth arc across the front of the mantle.
16646
+ * Resolves all visible projected patches for the single blobby octopus mesh.
16380
16647
  *
16381
- * @private helper of `octopus3dAvatarVisual`
16648
+ * @private helper of `octopus3d2AvatarVisual`
16382
16649
  */
16383
- function drawProjectedMouth(context, localPoints, center, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size) {
16384
- const scenePoints = localPoints.map((localPoint) => transformScenePoint(localPoint, center, rotationX, rotationY));
16385
- if (scenePoints.some((scenePoint) => scenePoint.z <= center.z)) {
16386
- return;
16650
+ function resolveVisibleBlobbyOctopusPatches(options) {
16651
+ const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, animationPhase, timeMs, } = options;
16652
+ const latitudePatchCount = 12;
16653
+ const longitudePatchCount = 24;
16654
+ const surfacePatches = [];
16655
+ for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
16656
+ const startLatitude = -Math.PI / 2 + (latitudeIndex / latitudePatchCount) * Math.PI;
16657
+ const endLatitude = -Math.PI / 2 + ((latitudeIndex + 1) / latitudePatchCount) * Math.PI;
16658
+ const centerLatitude = (startLatitude + endLatitude) / 2;
16659
+ const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
16660
+ for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
16661
+ const startLongitude = -Math.PI + (longitudeIndex / longitudePatchCount) * Math.PI * 2;
16662
+ const endLongitude = -Math.PI + ((longitudeIndex + 1) / longitudePatchCount) * Math.PI * 2;
16663
+ const centerLongitude = (startLongitude + endLongitude) / 2;
16664
+ const localCorners = [
16665
+ sampleBlobbyOctopusSurfacePoint(options, startLatitude, startLongitude),
16666
+ sampleBlobbyOctopusSurfacePoint(options, startLatitude, endLongitude),
16667
+ sampleBlobbyOctopusSurfacePoint(options, endLatitude, endLongitude),
16668
+ sampleBlobbyOctopusSurfacePoint(options, endLatitude, startLongitude),
16669
+ ];
16670
+ const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
16671
+ const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
16672
+ if (surfaceNormal.z <= 0.01) {
16673
+ continue;
16674
+ }
16675
+ const projectedCorners = transformedCorners.map((transformedCorner) => projectScenePoint(transformedCorner, size, sceneCenterX, sceneCenterY));
16676
+ surfacePatches.push({
16677
+ corners: projectedCorners,
16678
+ averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
16679
+ transformedCorners.length,
16680
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
16681
+ fillStyle: resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), resolveLowerLobeWave(centerLongitude, morphologyProfile, animationPhase, timeMs)),
16682
+ outlineColor: verticalProgress < 0.58 ? `${palette.highlight}73` : `${palette.shadow}8a`,
16683
+ });
16684
+ }
16387
16685
  }
16388
- const projectedPoints = scenePoints.map((scenePoint) => projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
16389
- context.beginPath();
16390
- context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
16391
- context.quadraticCurveTo(projectedPoints[1].x, projectedPoints[1].y, projectedPoints[2].x, projectedPoints[2].y);
16392
- context.strokeStyle = `${palette.ink}b8`;
16393
- context.lineWidth = Math.max(1.1, size * 0.009);
16394
- context.lineCap = 'round';
16395
- context.stroke();
16686
+ return surfacePatches;
16396
16687
  }
16397
16688
  /**
16398
- * Draws one filled projected quad.
16689
+ * Samples one point on the continuous Octopus 3D 2 surface.
16690
+ *
16691
+ * The lower hemisphere widens and falls into soft lobe waves so the octopus stays one connected mesh
16692
+ * instead of switching to separately rendered tentacles.
16693
+ *
16694
+ * @private helper of `octopus3d2AvatarVisual`
16695
+ */
16696
+ function sampleBlobbyOctopusSurfacePoint(options, latitude, longitude) {
16697
+ const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
16698
+ const cosineLatitude = Math.max(0, Math.cos(latitude));
16699
+ const verticalProgress = (Math.sin(latitude) + 1) / 2;
16700
+ const upperBlend = Math.pow(1 - verticalProgress, 1.2);
16701
+ const lowerBlend = Math.pow(verticalProgress, 1.42);
16702
+ const lowerLobeWave = resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, timeMs);
16703
+ const skirtEnvelope = Math.pow(cosineLatitude, 0.5) * lowerBlend;
16704
+ const horizontalScale = 1.02 +
16705
+ skirtEnvelope * (0.34 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.22 + lowerLobeWave * 0.22) -
16706
+ upperBlend * 0.08;
16707
+ const depthScale = 1.04 +
16708
+ upperBlend * 0.16 +
16709
+ Math.max(0, Math.cos(longitude)) * 0.1 +
16710
+ skirtEnvelope * (0.08 + lowerLobeWave * 0.06) -
16711
+ Math.max(0, -Math.cos(longitude)) * 0.04;
16712
+ const lowerDrop = skirtEnvelope *
16713
+ radiusY *
16714
+ (0.28 + lowerLobeWave * 0.14 + (morphologyProfile.tentacles.flowLengthScale - 1) * 0.12);
16715
+ const swayX = Math.sin(timeMs / 1250 + longitude * 1.8 + animationPhase) * skirtEnvelope * radiusX * 0.05;
16716
+ const swayZ = Math.cos(timeMs / 1480 + longitude * 1.2 - animationPhase * 0.7) * skirtEnvelope * radiusZ * 0.03;
16717
+ return {
16718
+ x: Math.sin(longitude) * cosineLatitude * radiusX * horizontalScale + swayX,
16719
+ y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.14) -
16720
+ upperBlend * radiusY * 0.1 +
16721
+ lowerDrop +
16722
+ Math.sin(timeMs / 1780 + animationPhase + latitude * 1.4) * skirtEnvelope * radiusY * 0.02,
16723
+ z: Math.cos(longitude) * cosineLatitude * radiusZ * depthScale + swayZ,
16724
+ };
16725
+ }
16726
+ /**
16727
+ * Resolves the soft lower-lobe wave that makes the silhouette read more like a real octopus.
16399
16728
  *
16400
- * @private helper of `octopus3dAvatarVisual`
16729
+ * @private helper of `octopus3d2AvatarVisual`
16401
16730
  */
16402
- function drawProjectedQuad(context, corners, fillStyle) {
16403
- context.beginPath();
16404
- context.moveTo(corners[0].x, corners[0].y);
16405
- context.lineTo(corners[1].x, corners[1].y);
16406
- context.lineTo(corners[2].x, corners[2].y);
16407
- context.lineTo(corners[3].x, corners[3].y);
16408
- context.closePath();
16409
- context.fillStyle = fillStyle;
16410
- context.fill();
16731
+ function resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, timeMs) {
16732
+ const lobeCount = Math.max(4, Math.round((morphologyProfile.body.lobeCount + morphologyProfile.tentacles.count) / 2));
16733
+ return (Math.cos(longitude * lobeCount + animationPhase + timeMs / 1040) + 1) / 2;
16411
16734
  }
16412
16735
  /**
16413
- * Converts an opacity ratio into a two-digit hexadecimal alpha suffix.
16736
+ * Resolves one base fill tone for a surface patch on the single octopus mesh.
16414
16737
  *
16415
- * @private helper of `octopus3dAvatarVisual`
16738
+ * @private helper of `octopus3d2AvatarVisual`
16416
16739
  */
16417
- function formatAlphaHex(opacity) {
16418
- return Math.round(clampNumber$1(opacity, 0, 1) * 255)
16419
- .toString(16)
16420
- .padStart(2, '0');
16740
+ function resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, forwardness, lowerLobeWave) {
16741
+ const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.12 - forwardness * 0.07, 0, 1);
16742
+ if (tonalProgress < 0.16) {
16743
+ return palette.highlight;
16744
+ }
16745
+ if (tonalProgress < 0.34) {
16746
+ return palette.secondary;
16747
+ }
16748
+ if (tonalProgress < 0.72) {
16749
+ return forwardness > 0.58 ? palette.secondary : palette.primary;
16750
+ }
16751
+ return `${palette.shadow}f2`;
16752
+ }
16753
+ /**
16754
+ * Draws one projected patch with soft octopus shading.
16755
+ *
16756
+ * @private helper of `octopus3d2AvatarVisual`
16757
+ */
16758
+ function drawBlobbySurfacePatch(context, surfacePatch) {
16759
+ drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
16760
+ if (surfacePatch.lightIntensity > 0) {
16761
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.16 * surfacePatch.lightIntensity})`);
16762
+ }
16763
+ else if (surfacePatch.lightIntensity < 0) {
16764
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.24 * Math.abs(surfacePatch.lightIntensity)})`);
16765
+ }
16766
+ context.save();
16767
+ context.beginPath();
16768
+ context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
16769
+ for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
16770
+ context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
16771
+ }
16772
+ context.closePath();
16773
+ context.strokeStyle = surfacePatch.outlineColor;
16774
+ context.lineWidth = Math.max(1, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0042);
16775
+ context.lineJoin = 'round';
16776
+ context.stroke();
16777
+ context.restore();
16421
16778
  }
16422
16779
 
16423
16780
  /* eslint-disable no-magic-numbers */
@@ -17189,6 +17546,7 @@
17189
17546
  octopus2AvatarVisual,
17190
17547
  octopus3AvatarVisual,
17191
17548
  octopus3dAvatarVisual,
17549
+ octopus3d2AvatarVisual,
17192
17550
  asciiOctopusAvatarVisual,
17193
17551
  minecraftAvatarVisual,
17194
17552
  minecraft2AvatarVisual,
@@ -22764,11 +23122,11 @@
22764
23122
  });
22765
23123
  }
22766
23124
  /**
22767
- * Best-effort decoder for uploaded or remote file bytes whose extension or encoding may be unknown.
23125
+ * Prepares one attachment for best-effort text decoding.
22768
23126
  *
22769
- * @private internal utility for shared text decoding
23127
+ * @private function of decodeAttachmentAsText
22770
23128
  */
22771
- function decodeAttachmentAsText(input, options = {}) {
23129
+ function createDecodeAttachmentPreparation(input, options) {
22772
23130
  var _a;
22773
23131
  const maxBytes = Math.max(1, Math.floor((_a = options.maxBytes) !== null && _a !== void 0 ? _a : DEFAULT_ATTACHMENT_TEXT_DECODE_BYTES));
22774
23132
  const forceText = options.forceText === true;
@@ -22782,54 +23140,102 @@
22782
23140
  const inspection = inspectBytes(truncatedBytes);
22783
23141
  const trustedTextMime = isTrustedTextMimeType(mimeType);
22784
23142
  const trustedBinaryMime = isTrustedBinaryMimeType(mimeType);
23143
+ const shouldTreatAsBinary = (trustedBinaryMime || inspection.looksBinary) && !trustedTextMime;
22785
23144
  if (isTruncated) {
22786
23145
  warnings.push(`Decoded only the first ${maxBytes} bytes of \`${input.filename}\` because the attachment exceeded the text preview limit.`);
22787
23146
  }
22788
- const shouldTreatAsBinary = (trustedBinaryMime || inspection.looksBinary) && !trustedTextMime;
22789
- if (shouldTreatAsBinary && !forceText) {
22790
- warnings.push('File content looks binary, so text decoding was skipped.');
22791
- return {
22792
- text: '',
22793
- encodingUsed: 'binary',
22794
- confidence: 1,
22795
- warnings,
22796
- wasBinary: true,
22797
- isTruncated,
22798
- };
23147
+ return {
23148
+ warnings,
23149
+ charset,
23150
+ bom,
23151
+ inspection,
23152
+ truncatedBytes,
23153
+ isTruncated,
23154
+ forceText,
23155
+ shouldTreatAsBinary,
23156
+ };
23157
+ }
23158
+ /**
23159
+ * Returns an early result when the attachment should stay classified as binary.
23160
+ *
23161
+ * @private function of decodeAttachmentAsText
23162
+ */
23163
+ function createBinaryDecodeResult(preparation) {
23164
+ if (!preparation.shouldTreatAsBinary) {
23165
+ return null;
22799
23166
  }
22800
- if (shouldTreatAsBinary && forceText) {
22801
- warnings.push('File content looks binary, but text decoding was forced with `forceText`.');
23167
+ if (preparation.forceText) {
23168
+ preparation.warnings.push('File content looks binary, but text decoding was forced with `forceText`.');
23169
+ return null;
22802
23170
  }
22803
- if (charset && !isSupportedEncoding(charset)) {
22804
- warnings.push(`Ignored unsupported declared charset \`${charset}\` and used best-effort detection instead.`);
23171
+ preparation.warnings.push('File content looks binary, so text decoding was skipped.');
23172
+ return {
23173
+ text: '',
23174
+ encodingUsed: 'binary',
23175
+ confidence: 1,
23176
+ warnings: preparation.warnings,
23177
+ wasBinary: true,
23178
+ isTruncated: preparation.isTruncated,
23179
+ };
23180
+ }
23181
+ /**
23182
+ * Warns when the declared charset cannot be used by the runtime decoder.
23183
+ *
23184
+ * @private function of decodeAttachmentAsText
23185
+ */
23186
+ function addUnsupportedCharsetWarning(preparation) {
23187
+ if (preparation.charset && !isSupportedEncoding(preparation.charset)) {
23188
+ preparation.warnings.push(`Ignored unsupported declared charset \`${preparation.charset}\` and used best-effort detection instead.`);
22805
23189
  }
22806
- const bytesToDecode = bom ? truncatedBytes.subarray(bom.offset) : truncatedBytes;
22807
- const candidates = buildCandidateEncodings({
22808
- mimeType,
22809
- charset: charset && isSupportedEncoding(charset) ? charset : null,
22810
- bom,
22811
- inspection,
22812
- });
22813
- const decodedCandidates = candidates
23190
+ }
23191
+ /**
23192
+ * Returns the byte slice that should actually be decoded as text.
23193
+ *
23194
+ * @private function of decodeAttachmentAsText
23195
+ */
23196
+ function getBytesToDecode(preparation) {
23197
+ return preparation.bom ? preparation.truncatedBytes.subarray(preparation.bom.offset) : preparation.truncatedBytes;
23198
+ }
23199
+ /**
23200
+ * Decodes all candidate encodings and sorts the successful results by score.
23201
+ *
23202
+ * @private function of decodeAttachmentAsText
23203
+ */
23204
+ function decodeAttachmentCandidates(preparation) {
23205
+ return buildCandidateEncodings({
23206
+ charset: preparation.charset && isSupportedEncoding(preparation.charset) ? preparation.charset : null,
23207
+ bom: preparation.bom,
23208
+ inspection: preparation.inspection,
23209
+ })
22814
23210
  .map(({ encoding, source }) => {
22815
- const decoded = decodeWithEncoding(bytesToDecode, encoding);
23211
+ const decoded = decodeWithEncoding(getBytesToDecode(preparation), encoding);
22816
23212
  return decoded ? { ...decoded, source } : null;
22817
23213
  })
22818
23214
  .filter((candidate) => candidate !== null)
22819
23215
  .sort((left, right) => left.score - right.score);
22820
- const bestCandidate = decodedCandidates[0];
22821
- if (!bestCandidate) {
22822
- warnings.push('No supported text decoder was available.');
22823
- return {
22824
- text: '',
22825
- encodingUsed: 'binary',
22826
- confidence: 0,
22827
- warnings,
22828
- wasBinary: true,
22829
- isTruncated,
22830
- };
22831
- }
22832
- const secondBestCandidate = decodedCandidates[1];
23216
+ }
23217
+ /**
23218
+ * Returns the fallback result used when no text decoder could be applied.
23219
+ *
23220
+ * @private function of decodeAttachmentAsText
23221
+ */
23222
+ function createNoDecoderAvailableResult(preparation) {
23223
+ preparation.warnings.push('No supported text decoder was available.');
23224
+ return {
23225
+ text: '',
23226
+ encodingUsed: 'binary',
23227
+ confidence: 0,
23228
+ warnings: preparation.warnings,
23229
+ wasBinary: true,
23230
+ isTruncated: preparation.isTruncated,
23231
+ };
23232
+ }
23233
+ /**
23234
+ * Estimates confidence for the winning decoded text candidate.
23235
+ *
23236
+ * @private function of decodeAttachmentAsText
23237
+ */
23238
+ function computeDecodeConfidence(bestCandidate, secondBestCandidate, preparation) {
22833
23239
  const baseConfidence = bestCandidate.source === 'bom'
22834
23240
  ? 1
22835
23241
  : bestCandidate.source === 'charset'
@@ -22842,27 +23248,62 @@
22842
23248
  ? 0.82
22843
23249
  : 0.62;
22844
23250
  const scoreMargin = secondBestCandidate ? Math.max(0, secondBestCandidate.score - bestCandidate.score) : 0.2;
22845
- const confidence = Math.max(0.2, Math.min(shouldTreatAsBinary && forceText ? 0.45 : 1, baseConfidence + Math.min(0.18, scoreMargin / 2)));
23251
+ return Math.max(0.2, Math.min(preparation.shouldTreatAsBinary && preparation.forceText ? 0.45 : 1, baseConfidence + Math.min(0.18, scoreMargin / 2)));
23252
+ }
23253
+ /**
23254
+ * Appends user-facing warnings derived from the chosen decoded text candidate.
23255
+ *
23256
+ * @private function of decodeAttachmentAsText
23257
+ */
23258
+ function addDecodeWarnings(bestCandidate, confidence, preparation) {
22846
23259
  if (bestCandidate.source === 'heuristic' && bestCandidate.encoding !== 'utf-8') {
22847
- warnings.push(`Encoding was guessed as \`${bestCandidate.encoding}\`.`);
23260
+ preparation.warnings.push(`Encoding was guessed as \`${bestCandidate.encoding}\`.`);
22848
23261
  }
22849
23262
  if (bestCandidate.source === 'heuristic' &&
22850
23263
  bestCandidate.encoding === 'utf-8' &&
22851
23264
  bestCandidate.replacementCount > 0) {
22852
- warnings.push('UTF-8 decoding produced replacement characters, so the extracted text may contain errors.');
23265
+ preparation.warnings.push('UTF-8 decoding produced replacement characters, so the extracted text may contain errors.');
22853
23266
  }
22854
23267
  if (confidence < 0.6) {
22855
- warnings.push('Decoding confidence is low, so the extracted text may contain errors.');
23268
+ preparation.warnings.push('Decoding confidence is low, so the extracted text may contain errors.');
22856
23269
  }
23270
+ }
23271
+ /**
23272
+ * Creates the final decoded-text result from the chosen candidate and accumulated metadata.
23273
+ *
23274
+ * @private function of decodeAttachmentAsText
23275
+ */
23276
+ function createDecodedTextResult(bestCandidate, confidence, preparation) {
22857
23277
  return {
22858
- text: isTruncated ? appendTruncatedMarker(bestCandidate.text) : bestCandidate.text,
23278
+ text: preparation.isTruncated ? appendTruncatedMarker(bestCandidate.text) : bestCandidate.text,
22859
23279
  encodingUsed: bestCandidate.encoding,
22860
23280
  confidence,
22861
- warnings,
23281
+ warnings: preparation.warnings,
22862
23282
  wasBinary: false,
22863
- isTruncated,
23283
+ isTruncated: preparation.isTruncated,
22864
23284
  };
22865
23285
  }
23286
+ /**
23287
+ * Best-effort decoder for uploaded or remote file bytes whose extension or encoding may be unknown.
23288
+ *
23289
+ * @private internal utility for shared text decoding
23290
+ */
23291
+ function decodeAttachmentAsText(input, options = {}) {
23292
+ const preparation = createDecodeAttachmentPreparation(input, options);
23293
+ const binaryResult = createBinaryDecodeResult(preparation);
23294
+ if (binaryResult) {
23295
+ return binaryResult;
23296
+ }
23297
+ addUnsupportedCharsetWarning(preparation);
23298
+ const decodedCandidates = decodeAttachmentCandidates(preparation);
23299
+ const bestCandidate = decodedCandidates[0];
23300
+ if (!bestCandidate) {
23301
+ return createNoDecoderAvailableResult(preparation);
23302
+ }
23303
+ const confidence = computeDecodeConfidence(bestCandidate, decodedCandidates[1], preparation);
23304
+ addDecodeWarnings(bestCandidate, confidence, preparation);
23305
+ return createDecodedTextResult(bestCandidate, confidence, preparation);
23306
+ }
22866
23307
 
22867
23308
  /**
22868
23309
  * Base GitHub API URL.
@@ -34761,7 +35202,10 @@
34761
35202
  Cannot find model in ${this.options.getTitle()} models with name "${defaultModelName}" which should be used as default.
34762
35203
 
34763
35204
  Available models:
34764
- ${block(this.options.getHardcodedModels().map(({ modelName }) => `- "${modelName}"`).join('\n'))}
35205
+ ${block(this.options
35206
+ .getHardcodedModels()
35207
+ .map(({ modelName }) => `- "${modelName}"`)
35208
+ .join('\n'))}
34765
35209
 
34766
35210
  Model "${defaultModelName}" is probably not available anymore, not installed, inaccessible or misconfigured.
34767
35211
 
@@ -35115,7 +35559,8 @@
35115
35559
  };
35116
35560
  let rawPromptContent = templateParameters(content, { ...parameters, modelName });
35117
35561
  if ('attachments' in prompt && Array.isArray(prompt.attachments) && prompt.attachments.length > 0) {
35118
- rawPromptContent += '\n\n' + prompt.attachments.map((attachment) => `Image attachment: ${attachment.url}`).join('\n');
35562
+ rawPromptContent +=
35563
+ '\n\n' + prompt.attachments.map((attachment) => `Image attachment: ${attachment.url}`).join('\n');
35119
35564
  }
35120
35565
  const rawRequest = {
35121
35566
  ...modelSettings,
@@ -35220,7 +35665,9 @@
35220
35665
  * Schedules one request through the shared limiter and retry policy.
35221
35666
  */
35222
35667
  async executeRateLimitedRequest(requestFn) {
35223
- return this.limiter.schedule(() => this.makeRequestWithNetworkRetry(requestFn)).catch((error) => {
35668
+ return this.limiter
35669
+ .schedule(() => this.makeRequestWithNetworkRetry(requestFn))
35670
+ .catch((error) => {
35224
35671
  assertsError(error);
35225
35672
  if (this.options.isVerbose) {
35226
35673
  console.info(colors__default["default"].bgRed('error'), error);
@@ -36427,7 +36874,9 @@
36427
36874
  pollingState.lastProgressAtMs = nowMs;
36428
36875
  pollingState.lastProgressKey = progressKey;
36429
36876
  }
36430
- if (this.options.isVerbose && (statusCountsKey !== pollingState.lastCountsKey || nowMs - pollingState.lastLogAtMs >= progressLogIntervalMs)) {
36877
+ if (this.options.isVerbose &&
36878
+ (statusCountsKey !== pollingState.lastCountsKey ||
36879
+ nowMs - pollingState.lastLogAtMs >= progressLogIntervalMs)) {
36431
36880
  console.info('[🤰]', 'Vector store file batch status', {
36432
36881
  vectorStoreId,
36433
36882
  batchId,