@promptbook/node 0.112.0-69 → 0.112.0-70

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 (27) hide show
  1. package/esm/index.es.js +753 -221
  2. package/esm/index.es.js.map +1 -1
  3. package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
  4. package/esm/src/avatars/visuals/avatar3dProjectionShared.d.ts +141 -0
  5. package/esm/src/avatars/visuals/octopus3dAvatarVisual.d.ts +7 -0
  6. package/esm/src/cli/cli-commands/agent/agentProjectPaths.d.ts +2 -2
  7. package/esm/src/cli/cli-commands/agent/initializeAgentRunnerCommand.d.ts +22 -0
  8. package/esm/src/cli/cli-commands/agent/runMultiple.d.ts +10 -0
  9. package/esm/src/cli/cli-commands/agent.d.ts +3 -2
  10. package/esm/src/cli/other/install.test.d.ts +1 -0
  11. package/esm/src/commitments/USE_TIMEOUT/USE_TIMEOUT.d.ts +1 -1
  12. package/esm/src/commitments/WALLET/WALLET.d.ts +1 -1
  13. package/esm/src/version.d.ts +1 -1
  14. package/package.json +2 -2
  15. package/umd/index.umd.js +753 -221
  16. package/umd/index.umd.js.map +1 -1
  17. package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
  18. package/umd/src/avatars/visuals/avatar3dProjectionShared.d.ts +141 -0
  19. package/umd/src/avatars/visuals/octopus3dAvatarVisual.d.ts +7 -0
  20. package/umd/src/cli/cli-commands/agent/agentProjectPaths.d.ts +2 -2
  21. package/umd/src/cli/cli-commands/agent/initializeAgentRunnerCommand.d.ts +22 -0
  22. package/umd/src/cli/cli-commands/agent/runMultiple.d.ts +10 -0
  23. package/umd/src/cli/cli-commands/agent.d.ts +3 -2
  24. package/umd/src/cli/other/install.test.d.ts +1 -0
  25. package/umd/src/commitments/USE_TIMEOUT/USE_TIMEOUT.d.ts +1 -1
  26. package/umd/src/commitments/WALLET/WALLET.d.ts +1 -1
  27. package/umd/src/version.d.ts +1 -1
package/esm/index.es.js CHANGED
@@ -35,7 +35,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
35
35
  * @generated
36
36
  * @see https://github.com/webgptorg/promptbook
37
37
  */
38
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-69';
38
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-70';
39
39
  /**
40
40
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
41
41
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -2389,11 +2389,11 @@ function capitalize(word) {
2389
2389
  */
2390
2390
  function pipelineJsonToString(pipelineJson) {
2391
2391
  const { title, pipelineUrl, bookVersion, description, parameters, tasks } = pipelineJson;
2392
- let pipelineString = `# ${title}`;
2393
- if (description) {
2394
- pipelineString += '\n\n';
2395
- pipelineString += description;
2396
- }
2392
+ let pipelineString = spaceTrim$1((block) => `
2393
+ # ${title}
2394
+
2395
+ ${block(description || '')}
2396
+ `);
2397
2397
  const commands = [];
2398
2398
  if (pipelineUrl) {
2399
2399
  commands.push(`PIPELINE URL ${pipelineUrl}`);
@@ -2409,20 +2409,17 @@ function pipelineJsonToString(pipelineJson) {
2409
2409
  for (const parameter of parameters.filter(({ isOutput }) => isOutput)) {
2410
2410
  commands.push(`OUTPUT PARAMETER ${taskParameterJsonToString(parameter)}`);
2411
2411
  }
2412
- pipelineString += '\n\n';
2413
- pipelineString += commands.map((command) => `- ${command}`).join('\n');
2412
+ pipelineString = spaceTrim$1((block) => `
2413
+ ${block(pipelineString)}
2414
+
2415
+ ${block(commands.map((command) => `- ${command}`).join('\n'))}
2416
+ `);
2414
2417
  for (const task of tasks) {
2415
2418
  const {
2416
2419
  /* Note: Not using:> name, */
2417
2420
  title, description,
2418
2421
  /* Note: dependentParameterNames, */
2419
2422
  jokerParameterNames: jokers, taskType, content, postprocessingFunctionNames: postprocessing, expectations, format, resultingParameterName, } = task;
2420
- pipelineString += '\n\n';
2421
- pipelineString += `## ${title}`;
2422
- if (description) {
2423
- pipelineString += '\n\n';
2424
- pipelineString += description;
2425
- }
2426
2423
  const commands = [];
2427
2424
  let contentLanguage = 'text';
2428
2425
  if (taskType === 'PROMPT_TASK') {
@@ -2485,18 +2482,23 @@ function pipelineJsonToString(pipelineJson) {
2485
2482
  commands.push(`FORMAT JSON`);
2486
2483
  }
2487
2484
  } /* not else */
2488
- pipelineString += '\n\n';
2489
- pipelineString += commands.map((command) => `- ${command}`).join('\n');
2490
- pipelineString += '\n\n';
2491
- pipelineString += '```' + contentLanguage;
2492
- pipelineString += '\n';
2493
- pipelineString += spaceTrim$1(content);
2494
- // <- TODO: [main] !!3 Escape
2495
- // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
2496
- pipelineString += '\n';
2497
- pipelineString += '```';
2498
- pipelineString += '\n\n';
2499
- pipelineString += `\`-> {${resultingParameterName}}\``; // <- TODO: [main] !!3 If the parameter here has description, add it and use taskParameterJsonToString
2485
+ pipelineString = spaceTrim$1((block) => `
2486
+ ${block(pipelineString)}
2487
+
2488
+ ## ${title}
2489
+
2490
+ ${block(description || '')}
2491
+
2492
+ ${block(commands.map((command) => `- ${command}`).join('\n'))}
2493
+
2494
+ \`\`\`${contentLanguage}
2495
+ ${block(spaceTrim$1(content))}
2496
+ \`\`\`
2497
+
2498
+ \`-> {${resultingParameterName}}\`
2499
+ `); // <- TODO: [main] !!3 If the parameter here has description, add it and use taskParameterJsonToString
2500
+ // <- TODO: [main] !!3 Escape
2501
+ // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
2500
2502
  }
2501
2503
  return validatePipelineString(pipelineString);
2502
2504
  }
@@ -16457,6 +16459,184 @@ function drawFractalCore(context, size, palette, timeMs, corePhase) {
16457
16459
  context.restore();
16458
16460
  }
16459
16461
 
16462
+ /* eslint-disable no-magic-numbers */
16463
+ /**
16464
+ * Default camera distance ratio shared by the proper-3D avatar visuals.
16465
+ *
16466
+ * @private helper of the 3D avatar visuals
16467
+ */
16468
+ const DEFAULT_3D_CAMERA_DISTANCE_RATIO = 1.4;
16469
+ /**
16470
+ * Clamps one number into the provided range.
16471
+ *
16472
+ * @param value Input value.
16473
+ * @param minimumValue Inclusive lower bound.
16474
+ * @param maximumValue Inclusive upper bound.
16475
+ * @returns Clamped value.
16476
+ *
16477
+ * @private helper of the 3D avatar visuals
16478
+ */
16479
+ function clampNumber$1(value, minimumValue, maximumValue) {
16480
+ return Math.min(maximumValue, Math.max(minimumValue, value));
16481
+ }
16482
+ /**
16483
+ * Rotates one point around the local Y axis.
16484
+ *
16485
+ * @param point Source point.
16486
+ * @param angle Rotation angle in radians.
16487
+ * @returns Rotated point.
16488
+ *
16489
+ * @private helper of the 3D avatar visuals
16490
+ */
16491
+ function rotatePointAroundY(point, angle) {
16492
+ const cosine = Math.cos(angle);
16493
+ const sine = Math.sin(angle);
16494
+ return {
16495
+ x: point.x * cosine + point.z * sine,
16496
+ y: point.y,
16497
+ z: -point.x * sine + point.z * cosine,
16498
+ };
16499
+ }
16500
+ /**
16501
+ * Rotates one point around the local X axis.
16502
+ *
16503
+ * @param point Source point.
16504
+ * @param angle Rotation angle in radians.
16505
+ * @returns Rotated point.
16506
+ *
16507
+ * @private helper of the 3D avatar visuals
16508
+ */
16509
+ function rotatePointAroundX(point, angle) {
16510
+ const cosine = Math.cos(angle);
16511
+ const sine = Math.sin(angle);
16512
+ return {
16513
+ x: point.x,
16514
+ y: point.y * cosine - point.z * sine,
16515
+ z: point.y * sine + point.z * cosine,
16516
+ };
16517
+ }
16518
+ /**
16519
+ * Applies the local rotations and translation to one scene point.
16520
+ *
16521
+ * @param localPoint Point in local object space.
16522
+ * @param center Object center in scene space.
16523
+ * @param rotationX Object pitch in radians.
16524
+ * @param rotationY Object yaw in radians.
16525
+ * @returns Transformed scene-space point.
16526
+ *
16527
+ * @private helper of the 3D avatar visuals
16528
+ */
16529
+ function transformScenePoint(localPoint, center, rotationX, rotationY) {
16530
+ const yawedPoint = rotatePointAroundY(localPoint, rotationY);
16531
+ const pitchedPoint = rotatePointAroundX(yawedPoint, rotationX);
16532
+ return {
16533
+ x: center.x + pitchedPoint.x,
16534
+ y: center.y + pitchedPoint.y,
16535
+ z: center.z + pitchedPoint.z,
16536
+ };
16537
+ }
16538
+ /**
16539
+ * Projects one scene point into canvas coordinates.
16540
+ *
16541
+ * @param point Scene-space point.
16542
+ * @param size Canvas size in CSS pixels.
16543
+ * @param sceneCenterX Horizontal scene center.
16544
+ * @param sceneCenterY Vertical scene center.
16545
+ * @param cameraDistanceRatio Optional camera distance ratio.
16546
+ * @returns Projected point.
16547
+ *
16548
+ * @private helper of the 3D avatar visuals
16549
+ */
16550
+ function projectScenePoint(point, size, sceneCenterX, sceneCenterY, cameraDistanceRatio = DEFAULT_3D_CAMERA_DISTANCE_RATIO) {
16551
+ const cameraDistance = size * cameraDistanceRatio;
16552
+ const perspectiveScale = cameraDistance / Math.max(cameraDistance - point.z, cameraDistance * 0.35);
16553
+ return {
16554
+ x: sceneCenterX + point.x * perspectiveScale,
16555
+ y: sceneCenterY + point.y * perspectiveScale,
16556
+ z: point.z,
16557
+ };
16558
+ }
16559
+ /**
16560
+ * Subtracts one 3D point from another.
16561
+ *
16562
+ * @param leftPoint Left point.
16563
+ * @param rightPoint Right point.
16564
+ * @returns Difference vector.
16565
+ *
16566
+ * @private helper of the 3D avatar visuals
16567
+ */
16568
+ function subtractPoint3D(leftPoint, rightPoint) {
16569
+ return {
16570
+ x: leftPoint.x - rightPoint.x,
16571
+ y: leftPoint.y - rightPoint.y,
16572
+ z: leftPoint.z - rightPoint.z,
16573
+ };
16574
+ }
16575
+ /**
16576
+ * Computes the 3D cross product of two vectors.
16577
+ *
16578
+ * @param leftVector Left vector.
16579
+ * @param rightVector Right vector.
16580
+ * @returns Cross product.
16581
+ *
16582
+ * @private helper of the 3D avatar visuals
16583
+ */
16584
+ function crossProduct3D(leftVector, rightVector) {
16585
+ return {
16586
+ x: leftVector.y * rightVector.z - leftVector.z * rightVector.y,
16587
+ y: leftVector.z * rightVector.x - leftVector.x * rightVector.z,
16588
+ z: leftVector.x * rightVector.y - leftVector.y * rightVector.x,
16589
+ };
16590
+ }
16591
+ /**
16592
+ * Computes the 3D dot product of two vectors.
16593
+ *
16594
+ * @param leftVector Left vector.
16595
+ * @param rightVector Right vector.
16596
+ * @returns Dot product.
16597
+ *
16598
+ * @private helper of the 3D avatar visuals
16599
+ */
16600
+ function dotProduct3D(leftVector, rightVector) {
16601
+ return leftVector.x * rightVector.x + leftVector.y * rightVector.y + leftVector.z * rightVector.z;
16602
+ }
16603
+ /**
16604
+ * Normalizes one 3D vector while keeping zero vectors stable.
16605
+ *
16606
+ * @param vector Source vector.
16607
+ * @returns Normalized vector.
16608
+ *
16609
+ * @private helper of the 3D avatar visuals
16610
+ */
16611
+ function normalizeVector3(vector) {
16612
+ const length = Math.hypot(vector.x, vector.y, vector.z);
16613
+ if (length === 0) {
16614
+ return vector;
16615
+ }
16616
+ return {
16617
+ x: vector.x / length,
16618
+ y: vector.y / length,
16619
+ z: vector.z / length,
16620
+ };
16621
+ }
16622
+ /**
16623
+ * Measures the perimeter of one projected quad.
16624
+ *
16625
+ * @param corners Quad corners.
16626
+ * @returns Perimeter length.
16627
+ *
16628
+ * @private helper of the 3D avatar visuals
16629
+ */
16630
+ function getProjectedQuadPerimeter(corners) {
16631
+ let perimeter = 0;
16632
+ for (let cornerIndex = 0; cornerIndex < corners.length; cornerIndex++) {
16633
+ const currentCorner = corners[cornerIndex];
16634
+ const nextCorner = corners[(cornerIndex + 1) % corners.length];
16635
+ perimeter += Math.hypot(nextCorner.x - currentCorner.x, nextCorner.y - currentCorner.y);
16636
+ }
16637
+ return perimeter;
16638
+ }
16639
+
16460
16640
  /* eslint-disable no-magic-numbers */
16461
16641
  /**
16462
16642
  * Builds the seeded six-face texture pack used by the Minecraft-style head cuboid.
@@ -16629,18 +16809,12 @@ function fillTextureRect(texture, x, y, width, height, color) {
16629
16809
  }
16630
16810
 
16631
16811
  /* eslint-disable no-magic-numbers */
16632
- /**
16633
- * Fixed scene camera distance used for the proper-3D projection.
16634
- *
16635
- * @private helper of `minecraft2AvatarVisual`
16636
- */
16637
- const CAMERA_DISTANCE_RATIO = 1.4;
16638
16812
  /**
16639
16813
  * Shared light direction used to shade projected cuboid faces.
16640
16814
  *
16641
16815
  * @private helper of `minecraft2AvatarVisual`
16642
16816
  */
16643
- const LIGHT_DIRECTION = normalizeVector3({
16817
+ const LIGHT_DIRECTION$1 = normalizeVector3({
16644
16818
  x: 0.4,
16645
16819
  y: -0.65,
16646
16820
  z: 0.92,
@@ -16852,7 +17026,7 @@ function resolveVisibleCuboidFaces(cuboid, size, sceneCenterX, sceneCenterY) {
16852
17026
  corners: projectedCorners,
16853
17027
  texture: faceDefinition.texture,
16854
17028
  averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
16855
- lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION), -1, 1),
17029
+ lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$1), -1, 1),
16856
17030
  outlineColor: cuboid.outlineColor,
16857
17031
  };
16858
17032
  });
@@ -16879,7 +17053,7 @@ function drawTexturedProjectedFace(context, face) {
16879
17053
  const endX = (columnIndex + 1) / columns;
16880
17054
  const startY = rowIndex / rows;
16881
17055
  const endY = (rowIndex + 1) / rows;
16882
- drawProjectedQuad(context, [
17056
+ drawProjectedQuad$1(context, [
16883
17057
  interpolateProjectedQuad(face.corners, startX, startY),
16884
17058
  interpolateProjectedQuad(face.corners, endX, startY),
16885
17059
  interpolateProjectedQuad(face.corners, endX, endY),
@@ -16888,10 +17062,10 @@ function drawTexturedProjectedFace(context, face) {
16888
17062
  }
16889
17063
  }
16890
17064
  if (face.lightIntensity > 0) {
16891
- drawProjectedQuad(context, face.corners, `rgba(255, 255, 255, ${0.15 * face.lightIntensity})`);
17065
+ drawProjectedQuad$1(context, face.corners, `rgba(255, 255, 255, ${0.15 * face.lightIntensity})`);
16892
17066
  }
16893
17067
  else if (face.lightIntensity < 0) {
16894
- drawProjectedQuad(context, face.corners, `rgba(0, 0, 0, ${0.22 * Math.abs(face.lightIntensity)})`);
17068
+ drawProjectedQuad$1(context, face.corners, `rgba(0, 0, 0, ${0.22 * Math.abs(face.lightIntensity)})`);
16895
17069
  }
16896
17070
  context.save();
16897
17071
  context.beginPath();
@@ -16915,7 +17089,7 @@ function drawTexturedProjectedFace(context, face) {
16915
17089
  *
16916
17090
  * @private helper of `minecraft2AvatarVisual`
16917
17091
  */
16918
- function drawProjectedQuad(context, corners, fillStyle) {
17092
+ function drawProjectedQuad$1(context, corners, fillStyle) {
16919
17093
  context.beginPath();
16920
17094
  context.moveTo(corners[0].x, corners[0].y);
16921
17095
  context.lineTo(corners[1].x, corners[1].y);
@@ -16957,175 +17131,6 @@ function interpolateProjectedPoint(startPoint, endPoint, ratio) {
16957
17131
  z: startPoint.z + (endPoint.z - startPoint.z) * ratio,
16958
17132
  };
16959
17133
  }
16960
- /**
16961
- * Projects one rotated scene point into canvas coordinates.
16962
- *
16963
- * @param point Scene point.
16964
- * @param size Canvas size in CSS pixels.
16965
- * @param sceneCenterX Horizontal scene center.
16966
- * @param sceneCenterY Vertical scene center.
16967
- * @returns Projected point.
16968
- *
16969
- * @private helper of `minecraft2AvatarVisual`
16970
- */
16971
- function projectScenePoint(point, size, sceneCenterX, sceneCenterY) {
16972
- const cameraDistance = size * CAMERA_DISTANCE_RATIO;
16973
- const perspectiveScale = cameraDistance / Math.max(cameraDistance - point.z, cameraDistance * 0.35);
16974
- return {
16975
- x: sceneCenterX + point.x * perspectiveScale,
16976
- y: sceneCenterY + point.y * perspectiveScale,
16977
- z: point.z,
16978
- };
16979
- }
16980
- /**
16981
- * Applies the local cuboid rotations and translation to one scene point.
16982
- *
16983
- * @param localPoint Point in cuboid-local space.
16984
- * @param center Cuboid center in scene space.
16985
- * @param rotationX Cuboid pitch in radians.
16986
- * @param rotationY Cuboid yaw in radians.
16987
- * @returns Transformed scene-space point.
16988
- *
16989
- * @private helper of `minecraft2AvatarVisual`
16990
- */
16991
- function transformScenePoint(localPoint, center, rotationX, rotationY) {
16992
- const yawedPoint = rotatePointAroundY(localPoint, rotationY);
16993
- const pitchedPoint = rotatePointAroundX(yawedPoint, rotationX);
16994
- return {
16995
- x: center.x + pitchedPoint.x,
16996
- y: center.y + pitchedPoint.y,
16997
- z: center.z + pitchedPoint.z,
16998
- };
16999
- }
17000
- /**
17001
- * Rotates one point around the local Y axis.
17002
- *
17003
- * @param point Source point.
17004
- * @param angle Rotation angle in radians.
17005
- * @returns Rotated point.
17006
- *
17007
- * @private helper of `minecraft2AvatarVisual`
17008
- */
17009
- function rotatePointAroundY(point, angle) {
17010
- const cosine = Math.cos(angle);
17011
- const sine = Math.sin(angle);
17012
- return {
17013
- x: point.x * cosine + point.z * sine,
17014
- y: point.y,
17015
- z: -point.x * sine + point.z * cosine,
17016
- };
17017
- }
17018
- /**
17019
- * Rotates one point around the local X axis.
17020
- *
17021
- * @param point Source point.
17022
- * @param angle Rotation angle in radians.
17023
- * @returns Rotated point.
17024
- *
17025
- * @private helper of `minecraft2AvatarVisual`
17026
- */
17027
- function rotatePointAroundX(point, angle) {
17028
- const cosine = Math.cos(angle);
17029
- const sine = Math.sin(angle);
17030
- return {
17031
- x: point.x,
17032
- y: point.y * cosine - point.z * sine,
17033
- z: point.y * sine + point.z * cosine,
17034
- };
17035
- }
17036
- /**
17037
- * Subtracts one 3D point from another.
17038
- *
17039
- * @param leftPoint Left point.
17040
- * @param rightPoint Right point.
17041
- * @returns Difference vector.
17042
- *
17043
- * @private helper of `minecraft2AvatarVisual`
17044
- */
17045
- function subtractPoint3D(leftPoint, rightPoint) {
17046
- return {
17047
- x: leftPoint.x - rightPoint.x,
17048
- y: leftPoint.y - rightPoint.y,
17049
- z: leftPoint.z - rightPoint.z,
17050
- };
17051
- }
17052
- /**
17053
- * Computes the 3D cross product of two vectors.
17054
- *
17055
- * @param leftVector Left vector.
17056
- * @param rightVector Right vector.
17057
- * @returns Cross product.
17058
- *
17059
- * @private helper of `minecraft2AvatarVisual`
17060
- */
17061
- function crossProduct3D(leftVector, rightVector) {
17062
- return {
17063
- x: leftVector.y * rightVector.z - leftVector.z * rightVector.y,
17064
- y: leftVector.z * rightVector.x - leftVector.x * rightVector.z,
17065
- z: leftVector.x * rightVector.y - leftVector.y * rightVector.x,
17066
- };
17067
- }
17068
- /**
17069
- * Computes the 3D dot product of two vectors.
17070
- *
17071
- * @param leftVector Left vector.
17072
- * @param rightVector Right vector.
17073
- * @returns Dot product.
17074
- *
17075
- * @private helper of `minecraft2AvatarVisual`
17076
- */
17077
- function dotProduct3D(leftVector, rightVector) {
17078
- return leftVector.x * rightVector.x + leftVector.y * rightVector.y + leftVector.z * rightVector.z;
17079
- }
17080
- /**
17081
- * Normalizes one 3D vector while keeping zero vectors stable.
17082
- *
17083
- * @param vector Source vector.
17084
- * @returns Normalized vector.
17085
- *
17086
- * @private helper of `minecraft2AvatarVisual`
17087
- */
17088
- function normalizeVector3(vector) {
17089
- const length = Math.hypot(vector.x, vector.y, vector.z);
17090
- if (length === 0) {
17091
- return vector;
17092
- }
17093
- return {
17094
- x: vector.x / length,
17095
- y: vector.y / length,
17096
- z: vector.z / length,
17097
- };
17098
- }
17099
- /**
17100
- * Clamps one number into the provided range.
17101
- *
17102
- * @param value Input value.
17103
- * @param minimumValue Inclusive lower bound.
17104
- * @param maximumValue Inclusive upper bound.
17105
- * @returns Clamped value.
17106
- *
17107
- * @private helper of `minecraft2AvatarVisual`
17108
- */
17109
- function clampNumber$1(value, minimumValue, maximumValue) {
17110
- return Math.min(maximumValue, Math.max(minimumValue, value));
17111
- }
17112
- /**
17113
- * Measures the perimeter of one projected quad.
17114
- *
17115
- * @param corners Quad corners.
17116
- * @returns Perimeter length.
17117
- *
17118
- * @private helper of `minecraft2AvatarVisual`
17119
- */
17120
- function getProjectedQuadPerimeter(corners) {
17121
- let perimeter = 0;
17122
- for (let cornerIndex = 0; cornerIndex < corners.length; cornerIndex++) {
17123
- const currentCorner = corners[cornerIndex];
17124
- const nextCorner = corners[(cornerIndex + 1) % corners.length];
17125
- perimeter += Math.hypot(nextCorner.x - currentCorner.x, nextCorner.y - currentCorner.y);
17126
- }
17127
- return perimeter;
17128
- }
17129
17134
 
17130
17135
  /* eslint-disable no-magic-numbers */
17131
17136
  /**
@@ -17694,7 +17699,7 @@ function resolveSeededIntegerRange(random, minimumValue, maximumValue) {
17694
17699
  *
17695
17700
  * @private helper of `octopus3AvatarVisual`
17696
17701
  */
17697
- function formatAlphaHex(opacity) {
17702
+ function formatAlphaHex$1(opacity) {
17698
17703
  return Math.round(Math.min(1, Math.max(0, opacity)) * 255)
17699
17704
  .toString(16)
17700
17705
  .padStart(2, '0');
@@ -18073,7 +18078,7 @@ function drawSeededEye(context, centerX, centerY, radiusX, radiusY, rotation, pa
18073
18078
  context.beginPath();
18074
18079
  context.moveTo(-radiusX * 0.74, radiusY * 0.2);
18075
18080
  context.quadraticCurveTo(0, radiusY * 0.38, radiusX * 0.74, radiusY * 0.2);
18076
- context.strokeStyle = `${palette.highlight}${formatAlphaHex(eyeStyle.lowerLidOpacity)}`;
18081
+ context.strokeStyle = `${palette.highlight}${formatAlphaHex$1(eyeStyle.lowerLidOpacity)}`;
18077
18082
  context.lineWidth = radiusX * 0.08;
18078
18083
  context.lineCap = 'round';
18079
18084
  context.stroke();
@@ -18081,6 +18086,518 @@ function drawSeededEye(context, centerX, centerY, radiusX, radiusY, rotation, pa
18081
18086
  context.restore();
18082
18087
  }
18083
18088
 
18089
+ /* eslint-disable no-magic-numbers */
18090
+ /**
18091
+ * Light direction used by the organic 3D octopus shading.
18092
+ *
18093
+ * @private helper of `octopus3dAvatarVisual`
18094
+ */
18095
+ const LIGHT_DIRECTION = normalizeVector3({
18096
+ x: 0.48,
18097
+ y: -0.62,
18098
+ z: 0.94,
18099
+ });
18100
+ /**
18101
+ * Proper 3D Octopus visual built from projected organic meshes and tentacles.
18102
+ *
18103
+ * @private built-in avatar visual
18104
+ */
18105
+ const octopus3dAvatarVisual = {
18106
+ id: 'octopus3d',
18107
+ title: 'Octopus 3D',
18108
+ description: 'Proper 3D octopus portrait with a turning silhouette, expressive eyes, and depth-sorted tentacles.',
18109
+ isAnimated: true,
18110
+ supportsPointerTracking: true,
18111
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
18112
+ const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
18113
+ const animationRandom = createRandom('octopus3d-animation-profile');
18114
+ const eyeRandom = createRandom('octopus3d-eye-profile');
18115
+ const animationPhase = animationRandom() * Math.PI * 2;
18116
+ const sceneCenterX = size * 0.5;
18117
+ const sceneCenterY = size * 0.56;
18118
+ const bob = Math.sin(timeMs / 920 + animationPhase) * size * 0.014;
18119
+ const mantleCenter = {
18120
+ x: interaction.bodyOffsetX * size * 0.042 + size * morphologyProfile.body.centerXJitterRatio * 0.65,
18121
+ y: -size * 0.09 + interaction.bodyOffsetY * size * 0.028 + bob,
18122
+ z: interaction.intensity * size * 0.012,
18123
+ };
18124
+ const underbodyCenter = {
18125
+ x: mantleCenter.x * 0.86,
18126
+ y: mantleCenter.y + size * 0.168,
18127
+ z: mantleCenter.z - size * 0.018,
18128
+ };
18129
+ const mantleRadiusX = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch;
18130
+ const mantleRadiusY = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.1;
18131
+ const mantleRadiusZ = size * morphologyProfile.body.bodyRadiusRatio * (0.9 + (morphologyProfile.body.horizontalStretch - 1) * 0.3);
18132
+ const underbodyRadiusX = mantleRadiusX * (0.9 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.08);
18133
+ const underbodyRadiusY = mantleRadiusY * (0.44 + morphologyProfile.body.lowerDropRatio * 3.1);
18134
+ const underbodyRadiusZ = mantleRadiusZ * 0.78;
18135
+ const bodyYaw = -0.18 +
18136
+ Math.sin(timeMs / 2400 + animationPhase) * 0.05 +
18137
+ interaction.bodyOffsetX * 0.18 +
18138
+ interaction.gazeX * 0.22;
18139
+ const bodyPitch = -0.08 +
18140
+ Math.cos(timeMs / 2700 + animationPhase * 0.6) * 0.025 -
18141
+ interaction.bodyOffsetY * 0.08 -
18142
+ interaction.gazeY * 0.08;
18143
+ const headYaw = bodyYaw - 0.04 + interaction.gazeX * 0.56;
18144
+ const headPitch = bodyPitch - 0.02 - interaction.gazeY * 0.32;
18145
+ const mantlePatches = resolveVisibleEllipsoidPatches({
18146
+ center: mantleCenter,
18147
+ radiusX: mantleRadiusX,
18148
+ radiusY: mantleRadiusY,
18149
+ radiusZ: mantleRadiusZ,
18150
+ rotationX: headPitch,
18151
+ rotationY: headYaw,
18152
+ sceneCenterX,
18153
+ sceneCenterY,
18154
+ size,
18155
+ palette,
18156
+ verticalColorBias: 0,
18157
+ outlineColor: `${palette.highlight}7a`,
18158
+ });
18159
+ const underbodyPatches = resolveVisibleEllipsoidPatches({
18160
+ center: underbodyCenter,
18161
+ radiusX: underbodyRadiusX,
18162
+ radiusY: underbodyRadiusY,
18163
+ radiusZ: underbodyRadiusZ,
18164
+ rotationX: bodyPitch,
18165
+ rotationY: bodyYaw,
18166
+ sceneCenterX,
18167
+ sceneCenterY,
18168
+ size,
18169
+ palette,
18170
+ verticalColorBias: 0.18,
18171
+ outlineColor: `${palette.shadow}8f`,
18172
+ });
18173
+ const tentacleStrokes = createOctopusTentacleStrokes({
18174
+ createRandom,
18175
+ morphologyProfile,
18176
+ timeMs,
18177
+ size,
18178
+ center: underbodyCenter,
18179
+ radiusX: underbodyRadiusX,
18180
+ radiusY: underbodyRadiusY,
18181
+ radiusZ: underbodyRadiusZ,
18182
+ rotationX: bodyPitch,
18183
+ rotationY: bodyYaw,
18184
+ sceneCenterX,
18185
+ sceneCenterY,
18186
+ animationPhase,
18187
+ });
18188
+ const faceEyeSpacing = size * morphologyProfile.face.eyeSpacingRatio * 0.92;
18189
+ const faceEyeYOffset = size * morphologyProfile.face.eyeCenterYOffsetRatio - mantleRadiusY * 0.02;
18190
+ const faceEyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.82;
18191
+ const faceEyeRadiusY = faceEyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.96;
18192
+ const mouthHalfWidth = size * morphologyProfile.face.mouthWidthRatio * 0.92;
18193
+ const mouthY = size * morphologyProfile.face.mouthYOffsetRatio + mantleRadiusY * 0.08;
18194
+ drawAvatarFrame(context, size, palette);
18195
+ drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
18196
+ drawOctopus3dShadow(context, size, palette, interaction, timeMs);
18197
+ for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => !candidateTentacleStroke.isFrontFacing)) {
18198
+ drawTentacleStroke(context, tentacleStroke, palette);
18199
+ }
18200
+ for (const surfacePatch of [...mantlePatches, ...underbodyPatches].sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
18201
+ drawSurfacePatch(context, surfacePatch);
18202
+ }
18203
+ for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => candidateTentacleStroke.isFrontFacing)) {
18204
+ drawTentacleStroke(context, tentacleStroke, palette);
18205
+ }
18206
+ drawProjectedEye(context, {
18207
+ x: -faceEyeSpacing,
18208
+ y: faceEyeYOffset,
18209
+ z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
18210
+ }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
18211
+ drawProjectedEye(context, {
18212
+ x: faceEyeSpacing,
18213
+ y: faceEyeYOffset,
18214
+ z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
18215
+ }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
18216
+ drawProjectedMouth(context, [
18217
+ { x: -mouthHalfWidth, y: mouthY, z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -mouthHalfWidth, mouthY) },
18218
+ {
18219
+ x: size * morphologyProfile.face.mouthCenterOffsetRatio,
18220
+ y: mouthY +
18221
+ size * morphologyProfile.face.mouthCurveDepthRatio * 0.38 +
18222
+ Math.sin(timeMs / 760 + animationPhase) * size * 0.01 +
18223
+ interaction.gazeY * size * 0.01,
18224
+ z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, size * morphologyProfile.face.mouthCenterOffsetRatio, mouthY),
18225
+ },
18226
+ { x: mouthHalfWidth, y: mouthY, z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, mouthHalfWidth, mouthY) },
18227
+ ], mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, palette, size);
18228
+ },
18229
+ };
18230
+ /**
18231
+ * Draws the atmospheric underwater glow behind the octopus mesh.
18232
+ *
18233
+ * @private helper of `octopus3dAvatarVisual`
18234
+ */
18235
+ function drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
18236
+ const glowGradient = context.createRadialGradient(sceneCenterX + interaction.gazeX * size * 0.1, sceneCenterY - size * 0.2 + interaction.gazeY * size * 0.05, size * 0.04, sceneCenterX, sceneCenterY - size * 0.04, size * 0.62);
18237
+ glowGradient.addColorStop(0, `${palette.highlight}5c`);
18238
+ glowGradient.addColorStop(0.36, `${palette.accent}24`);
18239
+ glowGradient.addColorStop(1, `${palette.highlight}00`);
18240
+ context.fillStyle = glowGradient;
18241
+ context.fillRect(0, 0, size, size);
18242
+ const lowerGradient = context.createRadialGradient(sceneCenterX + Math.sin(timeMs / 1700) * size * 0.05, sceneCenterY + size * 0.23, size * 0.08, sceneCenterX, sceneCenterY + size * 0.28, size * 0.5);
18243
+ lowerGradient.addColorStop(0, `${palette.secondary}1d`);
18244
+ lowerGradient.addColorStop(1, `${palette.secondary}00`);
18245
+ context.fillStyle = lowerGradient;
18246
+ context.fillRect(0, 0, size, size);
18247
+ }
18248
+ /**
18249
+ * Draws the soft ground shadow below the octopus.
18250
+ *
18251
+ * @private helper of `octopus3dAvatarVisual`
18252
+ */
18253
+ function drawOctopus3dShadow(context, size, palette, interaction, timeMs) {
18254
+ context.save();
18255
+ context.fillStyle = `${palette.shadow}66`;
18256
+ context.filter = `blur(${size * 0.022}px)`;
18257
+ context.beginPath();
18258
+ context.ellipse(size * 0.5 + interaction.gazeX * size * 0.04, size * 0.87 + Math.sin(timeMs / 920) * size * 0.008, size * (0.18 + interaction.intensity * 0.02), size * 0.06, 0, 0, Math.PI * 2);
18259
+ context.fill();
18260
+ context.restore();
18261
+ }
18262
+ /**
18263
+ * Resolves visible projected patches for one rotated ellipsoid mesh.
18264
+ *
18265
+ * @private helper of `octopus3dAvatarVisual`
18266
+ */
18267
+ function resolveVisibleEllipsoidPatches(options) {
18268
+ const { center, radiusX, radiusY, radiusZ, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, verticalColorBias, outlineColor, } = options;
18269
+ const latitudePatchCount = 10;
18270
+ const longitudePatchCount = 18;
18271
+ const surfacePatches = [];
18272
+ for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
18273
+ const startLatitude = -Math.PI / 2 + (latitudeIndex / latitudePatchCount) * Math.PI;
18274
+ const endLatitude = -Math.PI / 2 + ((latitudeIndex + 1) / latitudePatchCount) * Math.PI;
18275
+ const verticalProgress = (latitudeIndex + 0.5) / latitudePatchCount;
18276
+ for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
18277
+ const startLongitude = -Math.PI + (longitudeIndex / longitudePatchCount) * Math.PI * 2;
18278
+ const endLongitude = -Math.PI + ((longitudeIndex + 1) / longitudePatchCount) * Math.PI * 2;
18279
+ const localCorners = [
18280
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, startLatitude, startLongitude),
18281
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, startLatitude, endLongitude),
18282
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, endLatitude, endLongitude),
18283
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, endLatitude, startLongitude),
18284
+ ];
18285
+ const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
18286
+ const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
18287
+ if (surfaceNormal.z <= 0.01) {
18288
+ continue;
18289
+ }
18290
+ const projectedCorners = transformedCorners.map((transformedCorner) => projectScenePoint(transformedCorner, size, sceneCenterX, sceneCenterY));
18291
+ surfacePatches.push({
18292
+ corners: projectedCorners,
18293
+ averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
18294
+ transformedCorners.length,
18295
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
18296
+ fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
18297
+ outlineColor,
18298
+ });
18299
+ }
18300
+ }
18301
+ return surfacePatches;
18302
+ }
18303
+ /**
18304
+ * Samples one point on an ellipsoid aligned to the local axes.
18305
+ *
18306
+ * @private helper of `octopus3dAvatarVisual`
18307
+ */
18308
+ function sampleEllipsoidPoint(radiusX, radiusY, radiusZ, latitude, longitude) {
18309
+ const cosineLatitude = Math.cos(latitude);
18310
+ return {
18311
+ x: Math.sin(longitude) * cosineLatitude * radiusX,
18312
+ y: Math.sin(latitude) * radiusY,
18313
+ z: Math.cos(longitude) * cosineLatitude * radiusZ,
18314
+ };
18315
+ }
18316
+ /**
18317
+ * Resolves one base fill tone for a surface patch across the octopus body.
18318
+ *
18319
+ * @private helper of `octopus3dAvatarVisual`
18320
+ */
18321
+ function resolveSurfacePatchFillStyle(palette, verticalProgress) {
18322
+ const clampedVerticalProgress = clampNumber$1(verticalProgress, 0, 1);
18323
+ if (clampedVerticalProgress < 0.2) {
18324
+ return palette.highlight;
18325
+ }
18326
+ if (clampedVerticalProgress < 0.45) {
18327
+ return palette.secondary;
18328
+ }
18329
+ if (clampedVerticalProgress < 0.8) {
18330
+ return palette.primary;
18331
+ }
18332
+ return `${palette.shadow}f2`;
18333
+ }
18334
+ /**
18335
+ * Draws one projected mesh patch with organic shading.
18336
+ *
18337
+ * @private helper of `octopus3dAvatarVisual`
18338
+ */
18339
+ function drawSurfacePatch(context, surfacePatch) {
18340
+ drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
18341
+ if (surfacePatch.lightIntensity > 0) {
18342
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.15 * surfacePatch.lightIntensity})`);
18343
+ }
18344
+ else if (surfacePatch.lightIntensity < 0) {
18345
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.24 * Math.abs(surfacePatch.lightIntensity)})`);
18346
+ }
18347
+ context.save();
18348
+ context.beginPath();
18349
+ context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
18350
+ for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
18351
+ context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
18352
+ }
18353
+ context.closePath();
18354
+ context.strokeStyle = surfacePatch.outlineColor;
18355
+ context.lineWidth = Math.max(1, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0044);
18356
+ context.lineJoin = 'round';
18357
+ context.stroke();
18358
+ context.restore();
18359
+ }
18360
+ /**
18361
+ * Creates the projected 3D tentacle strokes orbiting around the lower octopus body.
18362
+ *
18363
+ * @private helper of `octopus3dAvatarVisual`
18364
+ */
18365
+ function createOctopusTentacleStrokes(options) {
18366
+ const { createRandom, morphologyProfile, timeMs, size, center, radiusX, radiusY, radiusZ, rotationX, rotationY, sceneCenterX, sceneCenterY, animationPhase, } = options;
18367
+ return Array.from({ length: morphologyProfile.tentacles.count }, (_, tentacleIndex) => {
18368
+ const tentacleRandom = createRandom(`octopus3d-tentacle-${tentacleIndex}`);
18369
+ const spreadProgress = morphologyProfile.tentacles.count === 1 ? 0.5 : tentacleIndex / (morphologyProfile.tentacles.count - 1);
18370
+ const orbitAngle = -Math.PI * 0.92 + spreadProgress * Math.PI * 1.84 + (tentacleRandom() - 0.5) * 0.16;
18371
+ const flowLength = size * (0.19 + morphologyProfile.tentacles.flowLengthScale * 0.075 + tentacleRandom() * 0.018);
18372
+ const lateralReach = size *
18373
+ (0.08 + morphologyProfile.tentacles.lateralReachScale * 0.05 + Math.abs(Math.sin(orbitAngle)) * 0.018);
18374
+ const depthReach = size * (0.028 + morphologyProfile.tentacles.tipReachScale * 0.032);
18375
+ const sway = Math.sin(timeMs / (760 + tentacleIndex * 36) + animationPhase + tentacleRandom() * Math.PI * 2);
18376
+ const anchorPoint = {
18377
+ x: Math.sin(orbitAngle) * radiusX * (0.84 + tentacleRandom() * 0.08),
18378
+ y: radiusY * (0.22 + tentacleRandom() * 0.18),
18379
+ z: Math.cos(orbitAngle) * radiusZ * (0.72 + tentacleRandom() * 0.12),
18380
+ };
18381
+ const controlPointOne = {
18382
+ x: anchorPoint.x + Math.sin(orbitAngle) * lateralReach * 0.44,
18383
+ y: anchorPoint.y + flowLength * 0.26,
18384
+ z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.3 + sway * size * 0.012,
18385
+ };
18386
+ const controlPointTwo = {
18387
+ x: anchorPoint.x + Math.sin(orbitAngle) * lateralReach * (0.82 + morphologyProfile.tentacles.swayScale * 0.12),
18388
+ y: anchorPoint.y + flowLength * 0.66,
18389
+ z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.72 + sway * size * 0.02,
18390
+ };
18391
+ const endPoint = {
18392
+ x: anchorPoint.x +
18393
+ Math.sin(orbitAngle) * lateralReach * (1.02 + morphologyProfile.tentacles.tipWidthScale * 0.12) +
18394
+ sway * size * 0.028,
18395
+ y: anchorPoint.y + flowLength,
18396
+ z: anchorPoint.z + Math.cos(orbitAngle) * depthReach + sway * size * 0.016,
18397
+ };
18398
+ const scenePoints = Array.from({ length: 12 }, (_, sampleIndex) => transformScenePoint(sampleCubicBezierPoint3D(anchorPoint, controlPointOne, controlPointTwo, endPoint, sampleIndex / 11), center, rotationX, rotationY));
18399
+ const projectedPoints = scenePoints.map((scenePoint) => projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
18400
+ const averageDepth = scenePoints.reduce((depthSum, scenePoint) => depthSum + scenePoint.z, 0) / scenePoints.length;
18401
+ return {
18402
+ projectedPoints,
18403
+ averageDepth,
18404
+ isFrontFacing: averageDepth >= center.z - size * 0.006,
18405
+ baseWidth: size *
18406
+ (0.019 +
18407
+ morphologyProfile.tentacles.baseWidthScale * 0.007 +
18408
+ tentacleRandom() * 0.003 +
18409
+ Math.abs(Math.sin(orbitAngle)) * 0.002),
18410
+ tipWidth: size * (0.0046 + morphologyProfile.tentacles.tipWidthScale * 0.0018),
18411
+ colorBias: tentacleRandom(),
18412
+ };
18413
+ });
18414
+ }
18415
+ /**
18416
+ * Samples one point on a cubic Bezier curve in 3D.
18417
+ *
18418
+ * @private helper of `octopus3dAvatarVisual`
18419
+ */
18420
+ function sampleCubicBezierPoint3D(startPoint, controlPointOne, controlPointTwo, endPoint, progress) {
18421
+ const inverseProgress = 1 - progress;
18422
+ return {
18423
+ x: inverseProgress * inverseProgress * inverseProgress * startPoint.x +
18424
+ 3 * inverseProgress * inverseProgress * progress * controlPointOne.x +
18425
+ 3 * inverseProgress * progress * progress * controlPointTwo.x +
18426
+ progress * progress * progress * endPoint.x,
18427
+ y: inverseProgress * inverseProgress * inverseProgress * startPoint.y +
18428
+ 3 * inverseProgress * inverseProgress * progress * controlPointOne.y +
18429
+ 3 * inverseProgress * progress * progress * controlPointTwo.y +
18430
+ progress * progress * progress * endPoint.y,
18431
+ z: inverseProgress * inverseProgress * inverseProgress * startPoint.z +
18432
+ 3 * inverseProgress * inverseProgress * progress * controlPointOne.z +
18433
+ 3 * inverseProgress * progress * progress * controlPointTwo.z +
18434
+ progress * progress * progress * endPoint.z,
18435
+ };
18436
+ }
18437
+ /**
18438
+ * Draws one projected tentacle stroke with a slim highlight ridge.
18439
+ *
18440
+ * @private helper of `octopus3dAvatarVisual`
18441
+ */
18442
+ function drawTentacleStroke(context, tentacleStroke, palette) {
18443
+ const projectedSegments = tentacleStroke.projectedPoints.length - 1;
18444
+ for (let segmentIndex = 0; segmentIndex < projectedSegments; segmentIndex++) {
18445
+ const startPoint = tentacleStroke.projectedPoints[segmentIndex];
18446
+ const endPoint = tentacleStroke.projectedPoints[segmentIndex + 1];
18447
+ const progress = segmentIndex / projectedSegments;
18448
+ const width = tentacleStroke.baseWidth + (tentacleStroke.tipWidth - tentacleStroke.baseWidth) * progress;
18449
+ context.beginPath();
18450
+ context.moveTo(startPoint.x, startPoint.y);
18451
+ context.lineTo(endPoint.x, endPoint.y);
18452
+ context.strokeStyle =
18453
+ tentacleStroke.colorBias > 0.6 ? `${palette.secondary}f0` : `${palette.primary}f0`;
18454
+ context.lineWidth = width;
18455
+ context.lineCap = 'round';
18456
+ context.stroke();
18457
+ context.beginPath();
18458
+ context.moveTo(startPoint.x, startPoint.y);
18459
+ context.lineTo(endPoint.x, endPoint.y);
18460
+ context.strokeStyle = tentacleStroke.isFrontFacing ? `${palette.highlight}80` : `${palette.highlight}40`;
18461
+ context.lineWidth = Math.max(1, width * 0.34);
18462
+ context.lineCap = 'round';
18463
+ context.stroke();
18464
+ }
18465
+ }
18466
+ /**
18467
+ * Resolves the front surface depth on an ellipsoid for one local face point.
18468
+ *
18469
+ * @private helper of `octopus3dAvatarVisual`
18470
+ */
18471
+ function resolveEllipsoidSurfaceDepth(radiusX, radiusY, radiusZ, x, y) {
18472
+ const normalizedX = x / radiusX;
18473
+ const normalizedY = y / radiusY;
18474
+ const remainingDepthRatio = Math.max(0, 1 - normalizedX * normalizedX - normalizedY * normalizedY);
18475
+ return Math.sqrt(remainingDepthRatio) * radiusZ;
18476
+ }
18477
+ /**
18478
+ * Draws one projected eye on the turned octopus mantle.
18479
+ *
18480
+ * @private helper of `octopus3dAvatarVisual`
18481
+ */
18482
+ function drawProjectedEye(context, localCenter, radiusX, radiusY, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, phase, interaction, eyeStyle) {
18483
+ const centerScenePoint = transformScenePoint(localCenter, center, rotationX, rotationY);
18484
+ if (centerScenePoint.z <= center.z) {
18485
+ return;
18486
+ }
18487
+ const horizontalScenePoint = transformScenePoint({ x: localCenter.x + radiusX, y: localCenter.y, z: localCenter.z }, center, rotationX, rotationY);
18488
+ const verticalScenePoint = transformScenePoint({ x: localCenter.x, y: localCenter.y + radiusY, z: localCenter.z }, center, rotationX, rotationY);
18489
+ const projectedCenterPoint = projectScenePoint(centerScenePoint, size, sceneCenterX, sceneCenterY);
18490
+ const projectedHorizontalPoint = projectScenePoint(horizontalScenePoint, size, sceneCenterX, sceneCenterY);
18491
+ const projectedVerticalPoint = projectScenePoint(verticalScenePoint, size, sceneCenterX, sceneCenterY);
18492
+ const projectedRadiusX = Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y);
18493
+ const projectedRadiusY = Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y);
18494
+ if (projectedRadiusX < size * 0.008 || projectedRadiusY < size * 0.008) {
18495
+ return;
18496
+ }
18497
+ const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
18498
+ radiusX: projectedRadiusX,
18499
+ radiusY: projectedRadiusY,
18500
+ timeMs,
18501
+ phase,
18502
+ interaction,
18503
+ });
18504
+ const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
18505
+ context.save();
18506
+ context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
18507
+ context.rotate(rotation);
18508
+ context.beginPath();
18509
+ context.ellipse(0, 0, projectedRadiusX, projectedRadiusY, 0, 0, Math.PI * 2);
18510
+ context.fillStyle = '#f8fbff';
18511
+ context.fill();
18512
+ context.clip();
18513
+ const irisGradient = context.createRadialGradient(-projectedRadiusX * 0.2, -projectedRadiusY * 0.26, projectedRadiusX * 0.05, 0, 0, projectedRadiusX * 0.92);
18514
+ irisGradient.addColorStop(0, palette.highlight);
18515
+ irisGradient.addColorStop(0.56, palette.secondary);
18516
+ irisGradient.addColorStop(1, palette.shadow);
18517
+ context.beginPath();
18518
+ context.ellipse(pupilOffsetX, pupilOffsetY, projectedRadiusX * 0.62 * eyeStyle.irisScale, projectedRadiusY * 0.72 * eyeStyle.irisScale, 0, 0, Math.PI * 2);
18519
+ context.fillStyle = irisGradient;
18520
+ context.fill();
18521
+ context.beginPath();
18522
+ context.ellipse(pupilOffsetX, pupilOffsetY, projectedRadiusX * 0.15 * eyeStyle.pupilWidthScale, projectedRadiusY * 0.48 * eyeStyle.pupilHeightScale, 0, 0, Math.PI * 2);
18523
+ context.fillStyle = palette.ink;
18524
+ context.fill();
18525
+ context.beginPath();
18526
+ context.ellipse(pupilOffsetX - projectedRadiusX * 0.22, pupilOffsetY - projectedRadiusY * 0.24, projectedRadiusX * 0.12, projectedRadiusY * 0.14, 0, 0, Math.PI * 2);
18527
+ context.fillStyle = '#ffffff';
18528
+ context.fill();
18529
+ context.restore();
18530
+ context.save();
18531
+ context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
18532
+ context.rotate(rotation);
18533
+ context.beginPath();
18534
+ context.ellipse(0, 0, projectedRadiusX, projectedRadiusY, 0, 0, Math.PI * 2);
18535
+ context.strokeStyle = `${palette.shadow}cc`;
18536
+ context.lineWidth = projectedRadiusX * 0.16;
18537
+ context.stroke();
18538
+ context.beginPath();
18539
+ context.moveTo(-projectedRadiusX * 0.88, -projectedRadiusY * eyeStyle.upperLidInsetRatio);
18540
+ context.quadraticCurveTo(0, -projectedRadiusY * (eyeStyle.upperLidArchRatio - interaction.gazeY * 0.16 + interaction.intensity * 0.08), projectedRadiusX * 0.88, -projectedRadiusY * eyeStyle.upperLidInsetRatio);
18541
+ context.strokeStyle = `${palette.shadow}73`;
18542
+ context.lineWidth = projectedRadiusX * 0.14;
18543
+ context.lineCap = 'round';
18544
+ context.stroke();
18545
+ if (eyeStyle.lowerLidOpacity > 0) {
18546
+ context.beginPath();
18547
+ context.moveTo(-projectedRadiusX * 0.74, projectedRadiusY * 0.2);
18548
+ context.quadraticCurveTo(0, projectedRadiusY * 0.38, projectedRadiusX * 0.74, projectedRadiusY * 0.2);
18549
+ context.strokeStyle = `${palette.highlight}${formatAlphaHex(eyeStyle.lowerLidOpacity)}`;
18550
+ context.lineWidth = projectedRadiusX * 0.08;
18551
+ context.lineCap = 'round';
18552
+ context.stroke();
18553
+ }
18554
+ context.restore();
18555
+ }
18556
+ /**
18557
+ * Draws a subtle projected mouth arc across the front of the mantle.
18558
+ *
18559
+ * @private helper of `octopus3dAvatarVisual`
18560
+ */
18561
+ function drawProjectedMouth(context, localPoints, center, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size) {
18562
+ const scenePoints = localPoints.map((localPoint) => transformScenePoint(localPoint, center, rotationX, rotationY));
18563
+ if (scenePoints.some((scenePoint) => scenePoint.z <= center.z)) {
18564
+ return;
18565
+ }
18566
+ const projectedPoints = scenePoints.map((scenePoint) => projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
18567
+ context.beginPath();
18568
+ context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
18569
+ context.quadraticCurveTo(projectedPoints[1].x, projectedPoints[1].y, projectedPoints[2].x, projectedPoints[2].y);
18570
+ context.strokeStyle = `${palette.ink}b8`;
18571
+ context.lineWidth = Math.max(1.1, size * 0.009);
18572
+ context.lineCap = 'round';
18573
+ context.stroke();
18574
+ }
18575
+ /**
18576
+ * Draws one filled projected quad.
18577
+ *
18578
+ * @private helper of `octopus3dAvatarVisual`
18579
+ */
18580
+ function drawProjectedQuad(context, corners, fillStyle) {
18581
+ context.beginPath();
18582
+ context.moveTo(corners[0].x, corners[0].y);
18583
+ context.lineTo(corners[1].x, corners[1].y);
18584
+ context.lineTo(corners[2].x, corners[2].y);
18585
+ context.lineTo(corners[3].x, corners[3].y);
18586
+ context.closePath();
18587
+ context.fillStyle = fillStyle;
18588
+ context.fill();
18589
+ }
18590
+ /**
18591
+ * Converts an opacity ratio into a two-digit hexadecimal alpha suffix.
18592
+ *
18593
+ * @private helper of `octopus3dAvatarVisual`
18594
+ */
18595
+ function formatAlphaHex(opacity) {
18596
+ return Math.round(clampNumber$1(opacity, 0, 1) * 255)
18597
+ .toString(16)
18598
+ .padStart(2, '0');
18599
+ }
18600
+
18084
18601
  /* eslint-disable no-magic-numbers */
18085
18602
  /**
18086
18603
  * Octopus avatar visual.
@@ -18849,6 +19366,7 @@ const AVATAR_VISUALS = [
18849
19366
  octopusAvatarVisual,
18850
19367
  octopus2AvatarVisual,
18851
19368
  octopus3AvatarVisual,
19369
+ octopus3dAvatarVisual,
18852
19370
  asciiOctopusAvatarVisual,
18853
19371
  minecraftAvatarVisual,
18854
19372
  minecraft2AvatarVisual,
@@ -21042,16 +21560,22 @@ function resolveTeamTeammateLabels(teamContent, teammates) {
21042
21560
  * Each teammate is listed with its tool name, TEAM instructions, and optional profile hints.
21043
21561
  */
21044
21562
  function buildTeamSystemMessageBody(teamEntries) {
21045
- const lines = [
21046
- ...TEAM_SYSTEM_MESSAGE_GUIDANCE_LINES,
21047
- '',
21048
- ...teamEntries.map((entry, index) => {
21049
- const toolLine = `${index + 1}) ${entry.teammate.label} tool \`${entry.toolName}\``;
21050
- const detailLines = collectTeamEntryDetails(entry).map(formatTeamEntryDetailLine);
21051
- return [toolLine, ...detailLines].join('\n');
21052
- }),
21053
- ];
21054
- return lines.join('\n');
21563
+ const teammateSections = teamEntries.map((entry, index) => {
21564
+ const toolLine = `${index + 1}) ${entry.teammate.label} tool \`${entry.toolName}\``;
21565
+ const detailLines = collectTeamEntryDetails(entry).map(formatTeamEntryDetailLine);
21566
+ if (detailLines.length === 0) {
21567
+ return toolLine;
21568
+ }
21569
+ return spaceTrim$1((block) => `
21570
+ ${toolLine}
21571
+ ${block(detailLines.join('\n'))}
21572
+ `);
21573
+ });
21574
+ return spaceTrim$1((block) => `
21575
+ ${block(TEAM_SYSTEM_MESSAGE_GUIDANCE_LINES.join('\n'))}
21576
+
21577
+ ${block(teammateSections.join('\n\n'))}
21578
+ `);
21055
21579
  }
21056
21580
  /**
21057
21581
  * Builds the model-visible description for one teammate tool.
@@ -34044,7 +34568,11 @@ function createExampleInteractionsContent(parseResult, samples) {
34044
34568
  if (examples.length === 0) {
34045
34569
  return null;
34046
34570
  }
34047
- return `## Sample of communication with the agent:\n\n${examples.join('\n\n')}`;
34571
+ return spaceTrim$1((block) => `
34572
+ ## Sample of communication with the agent:
34573
+
34574
+ ${block(examples.join('\n\n'))}
34575
+ `);
34048
34576
  }
34049
34577
  /**
34050
34578
  * Collects the individual lines used in the example interaction section.
@@ -34081,7 +34609,11 @@ function collectExampleInteractionLines(parseResult, samples) {
34081
34609
  function appendSystemMessageSection(requirements, section) {
34082
34610
  return {
34083
34611
  ...requirements,
34084
- systemMessage: requirements.systemMessage + '\n\n' + section,
34612
+ systemMessage: spaceTrim$1((block) => `
34613
+ ${block(requirements.systemMessage)}
34614
+
34615
+ ${block(section)}
34616
+ `),
34085
34617
  };
34086
34618
  }
34087
34619
  /**