@promptbook/node 0.112.0-117 → 0.112.0-119

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 (37) hide show
  1. package/esm/index.es.js +351 -64
  2. package/esm/index.es.js.map +1 -1
  3. package/esm/src/_packages/node.index.d.ts +10 -0
  4. package/esm/src/book-3.0/BookNodeAgentSource.d.ts +1 -1
  5. package/esm/src/book-3.0/CliAgent.d.ts +7 -2
  6. package/esm/src/book-3.0/cliAgentEnv.d.ts +33 -0
  7. package/esm/src/book-components/BookEditor/BookEditor.d.ts +1 -1
  8. package/esm/src/book-components/BookEditor/BookEditorForClient.d.ts +1 -1
  9. package/esm/src/book-components/Chat/Chat/CitationIframePreview.d.ts +20 -0
  10. package/esm/src/book-components/_common/Dropdown/Dropdown.d.ts +1 -1
  11. package/esm/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +1 -1
  12. package/esm/src/book-components/_common/Modal/Modal.d.ts +1 -1
  13. package/esm/src/book-components/icons/AboutIcon.d.ts +1 -1
  14. package/esm/src/book-components/icons/DownloadIcon.d.ts +1 -1
  15. package/esm/src/book-components/icons/ExitFullscreenIcon.d.ts +1 -1
  16. package/esm/src/book-components/icons/FullscreenIcon.d.ts +1 -1
  17. package/esm/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +2 -18
  18. package/esm/src/version.d.ts +1 -1
  19. package/package.json +2 -2
  20. package/umd/index.umd.js +355 -63
  21. package/umd/index.umd.js.map +1 -1
  22. package/umd/src/_packages/node.index.d.ts +10 -0
  23. package/umd/src/book-3.0/BookNodeAgentSource.d.ts +1 -1
  24. package/umd/src/book-3.0/CliAgent.d.ts +7 -2
  25. package/umd/src/book-3.0/cliAgentEnv.d.ts +33 -0
  26. package/umd/src/book-components/BookEditor/BookEditor.d.ts +1 -1
  27. package/umd/src/book-components/BookEditor/BookEditorForClient.d.ts +1 -1
  28. package/umd/src/book-components/Chat/Chat/CitationIframePreview.d.ts +20 -0
  29. package/umd/src/book-components/_common/Dropdown/Dropdown.d.ts +1 -1
  30. package/umd/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +1 -1
  31. package/umd/src/book-components/_common/Modal/Modal.d.ts +1 -1
  32. package/umd/src/book-components/icons/AboutIcon.d.ts +1 -1
  33. package/umd/src/book-components/icons/DownloadIcon.d.ts +1 -1
  34. package/umd/src/book-components/icons/ExitFullscreenIcon.d.ts +1 -1
  35. package/umd/src/book-components/icons/FullscreenIcon.d.ts +1 -1
  36. package/umd/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +2 -18
  37. 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-117';
38
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-119';
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
@@ -233,7 +233,9 @@ function parseBookMessageHeader(line) {
233
233
  * @private internal utility of `Book`
234
234
  */
235
235
  function parseCommitmentHeader(line) {
236
- const match = /^([A-Z][A-Z0-9]*(?: [A-Z0-9]+)*)(?:\s+(.*))?$/u.exec(line);
236
+ // Require at least 2 characters in the first keyword word to avoid treating common
237
+ // single-letter words (e.g. "V" in Czech, "I" or "A" in English) as commitment headers.
238
+ const match = /^([A-Z][A-Z0-9]+(?: [A-Z0-9]+)*)(?:\s+(.*))?$/u.exec(line);
237
239
  if (!match) {
238
240
  return null;
239
241
  }
@@ -4498,14 +4500,50 @@ function exitForMissingModel(agentName, availableModels, exampleUsages) {
4498
4500
  * Builds the prompt sent to the selected coding runner for one queued user-thread book.
4499
4501
  */
4500
4502
  function buildAgentMessagePrompt(messageRelativePath, agentSystemMessage) {
4501
- const taskPrompt = spaceTrim$1(`
4502
- Answer 1 user question
4503
+ return spaceTrim$1((block) => `
4504
+ # Answer 1 user question
4503
4505
 
4504
- - Read \`${messageRelativePath}\` and answer the most recent \`MESSAGE @User\`
4505
- - Only change the queued message file by appending one new \`MESSAGE @Agent\` block
4506
- - Do not modify any other file in the repository
4507
- `);
4508
- return `${taskPrompt}\n\n**This is how you should behave:**\n\n${agentSystemMessage.trim()}`;
4506
+ - Read \`${messageRelativePath}\` and answer the most recent \`MESSAGE @User\`
4507
+ - Only change the queued message file by appending one new \`MESSAGE @Agent\` block
4508
+ - Do not modify any other file in the repository
4509
+
4510
+ ## Rules for the answering
4511
+
4512
+ ## Formatting
4513
+
4514
+ - You can use Markdown formatting in the messages like **bold** or *italic*
4515
+
4516
+ ## Sources and citations
4517
+
4518
+ Mark sources and citations like this "【https://example.com/document123.pdf 】"
4519
+
4520
+ At the same time, you can write sources naturally in the text of the answers
4521
+
4522
+ For example:
4523
+
4524
+ > According to paragraph §745b, the fee can be waived for a person over 65 years old. 【https://praha13.cz/2026/paragraph-745.doc】
4525
+
4526
+ - "paragraph §745b" fits naturally in the text.
4527
+ - "【https://praha13.cz/2026/paragraph-745.doc】" The exact format of the quote is important for further processing of the answer.
4528
+ - The "【" and "】" symbols are used to mark the source and will be parsed, inside should be valid URL used as a source for the answer.
4529
+
4530
+ ## Quick buttons
4531
+
4532
+ If there is a meaningful follow-up procedure, use quick buttons at the end of the answer:
4533
+
4534
+ \`\`\`
4535
+ How big is the contract you are posting?
4536
+
4537
+ [Up to 50,000 CZK](?message=We are posting an order up to 50,000 CZK)
4538
+ [Up to 100,000 CZK](?message=We are posting an order up to 100,000 CZK)
4539
+ [Over 100,000 CZK](?message=We are posting an order over 100,000 CZK)
4540
+ \`\`\`
4541
+
4542
+
4543
+ ## This is how you should behave
4544
+
4545
+ ${block(agentSystemMessage)}
4546
+ `);
4509
4547
  }
4510
4548
 
4511
4549
  /**
@@ -15412,21 +15450,22 @@ function getPointBounds(points) {
15412
15450
  * @private helper of `fractalAvatarVisual`
15413
15451
  */
15414
15452
  function drawDragonCurveLayer(context, points, options) {
15415
- const { size, primaryColor, secondaryColor, tertiaryColor, shadowColor, strokeWidth, timeMs, layerIndex } = options;
15453
+ const { primaryColor, secondaryColor, tertiaryColor, shadowColor, strokeWidth, timeMs, layerIndex } = options;
15416
15454
  const firstPoint = points[0];
15417
15455
  const lastPoint = points[points.length - 1];
15418
15456
  const ribbonGradient = context.createLinearGradient(firstPoint.x, firstPoint.y, lastPoint.x, lastPoint.y);
15419
15457
  ribbonGradient.addColorStop(0, `${primaryColor}f2`);
15420
15458
  ribbonGradient.addColorStop(0.5, `${secondaryColor}e6`);
15421
15459
  ribbonGradient.addColorStop(1, `${tertiaryColor}f2`);
15460
+ // Approximate the blurred shadow stroke with a wider semi-transparent stroke instead of
15461
+ // context.filter blur, which triggers a costly software rasterization pass every frame.
15422
15462
  context.save();
15423
15463
  context.beginPath();
15424
15464
  tracePolyline(context, points);
15425
- context.strokeStyle = `${shadowColor}82`;
15426
- context.lineWidth = strokeWidth * 1.8;
15465
+ context.strokeStyle = `${shadowColor}48`;
15466
+ context.lineWidth = strokeWidth * 4.5;
15427
15467
  context.lineJoin = 'round';
15428
15468
  context.lineCap = 'round';
15429
- context.filter = `blur(${size * 0.022}px)`;
15430
15469
  context.stroke();
15431
15470
  context.restore();
15432
15471
  context.beginPath();
@@ -16010,11 +16049,23 @@ function drawMinecraftBackdrop(context, size, palette, sceneCenterX, spotlightY,
16010
16049
  * @private helper of `minecraft2AvatarVisual`
16011
16050
  */
16012
16051
  function drawMinecraftShadow(context, size, palette, interaction, timeMs) {
16052
+ const cx = size * 0.5 + interaction.gazeX * size * 0.03;
16053
+ const cy = size * 0.85 + Math.sin(timeMs / 880) * size * 0.01;
16054
+ const rx = size * (0.16 + interaction.intensity * 0.015);
16055
+ const ry = size * 0.055;
16056
+ // Radial gradient approximates the blurry ellipse shadow without context.filter blur.
16013
16057
  context.save();
16014
- context.fillStyle = `${palette.shadow}66`;
16015
- context.filter = `blur(${size * 0.02}px)`;
16058
+ context.translate(cx, cy);
16059
+ context.scale(1, ry / rx);
16060
+ const blurRadius = rx * 1.4;
16061
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
16062
+ shadowGradient.addColorStop(0, `${palette.shadow}7a`);
16063
+ shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
16064
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
16065
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
16066
+ context.fillStyle = shadowGradient;
16016
16067
  context.beginPath();
16017
- context.ellipse(size * 0.5 + interaction.gazeX * size * 0.03, size * 0.85 + Math.sin(timeMs / 880) * size * 0.01, size * (0.16 + interaction.intensity * 0.015), size * 0.055, 0, 0, Math.PI * 2);
16068
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
16018
16069
  context.fill();
16019
16070
  context.restore();
16020
16071
  }
@@ -16238,13 +16289,27 @@ const minecraftAvatarVisual = {
16238
16289
  spotlight.addColorStop(1, `${palette.highlight}00`);
16239
16290
  context.fillStyle = spotlight;
16240
16291
  context.fillRect(0, 0, size, size);
16241
- context.save();
16242
- context.fillStyle = 'rgba(0, 0, 0, 0.22)';
16243
- context.filter = `blur(${size * 0.018}px)`;
16244
- context.beginPath();
16245
- context.ellipse(size * 0.5, size * 0.86, size * 0.2, size * 0.06, 0, 0, Math.PI * 2);
16246
- context.fill();
16247
- context.restore();
16292
+ {
16293
+ // Radial gradient approximates the blurry ellipse shadow without context.filter blur.
16294
+ const cx = size * 0.5;
16295
+ const cy = size * 0.86;
16296
+ const rx = size * 0.2;
16297
+ const ry = size * 0.06;
16298
+ const blurRadius = rx * 1.4;
16299
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
16300
+ shadowGradient.addColorStop(0, 'rgba(0,0,0,0.28)');
16301
+ shadowGradient.addColorStop(0.45, 'rgba(0,0,0,0.14)');
16302
+ shadowGradient.addColorStop(0.8, 'rgba(0,0,0,0.05)');
16303
+ shadowGradient.addColorStop(1, 'rgba(0,0,0,0)');
16304
+ context.save();
16305
+ context.translate(cx, cy);
16306
+ context.scale(1, ry / rx);
16307
+ context.fillStyle = shadowGradient;
16308
+ context.beginPath();
16309
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
16310
+ context.fill();
16311
+ context.restore();
16312
+ }
16248
16313
  drawVoxelCuboid(context, {
16249
16314
  x: bodyX,
16250
16315
  y: bodyY,
@@ -17296,6 +17361,35 @@ const LIGHT_DIRECTION$2 = normalizeVector3({
17296
17361
  y: -0.62,
17297
17362
  z: 0.94,
17298
17363
  });
17364
+ /**
17365
+ * Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
17366
+ *
17367
+ * @private helper of `octopus3dAvatarVisual`
17368
+ */
17369
+ const octopus3dStableStateCache = new WeakMap();
17370
+ /**
17371
+ * Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
17372
+ *
17373
+ * @private helper of `octopus3dAvatarVisual`
17374
+ */
17375
+ function getOctopus3dStableState(createRandom) {
17376
+ const cached = octopus3dStableStateCache.get(createRandom);
17377
+ if (cached !== undefined) {
17378
+ return cached;
17379
+ }
17380
+ const animationRandom = createRandom('octopus3d-animation-profile');
17381
+ const eyeRandom = createRandom('octopus3d-eye-profile');
17382
+ const leftEyePhaseOffset = eyeRandom() * 0.6;
17383
+ const rightEyePhaseOffset = eyeRandom() * 0.6;
17384
+ const state = {
17385
+ morphologyProfile: createOctopus3MorphologyProfile(createRandom),
17386
+ animationPhase: animationRandom() * Math.PI * 2,
17387
+ leftEyePhaseOffset,
17388
+ rightEyePhaseOffset,
17389
+ };
17390
+ octopus3dStableStateCache.set(createRandom, state);
17391
+ return state;
17392
+ }
17299
17393
  /**
17300
17394
  * Proper 3D Octopus visual built from projected organic meshes and tentacles.
17301
17395
  *
@@ -17308,10 +17402,7 @@ const octopus3dAvatarVisual = {
17308
17402
  isAnimated: true,
17309
17403
  supportsPointerTracking: true,
17310
17404
  render({ context, size, palette, createRandom, timeMs, interaction }) {
17311
- const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
17312
- const animationRandom = createRandom('octopus3d-animation-profile');
17313
- const eyeRandom = createRandom('octopus3d-eye-profile');
17314
- const animationPhase = animationRandom() * Math.PI * 2;
17405
+ const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3dStableState(createRandom);
17315
17406
  const sceneCenterX = size * 0.5;
17316
17407
  const sceneCenterY = size * 0.56;
17317
17408
  const bob = Math.sin(timeMs / 920 + animationPhase) * size * 0.014;
@@ -17408,12 +17499,12 @@ const octopus3dAvatarVisual = {
17408
17499
  x: -faceEyeSpacing,
17409
17500
  y: faceEyeYOffset,
17410
17501
  z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
17411
- }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
17502
+ }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
17412
17503
  drawProjectedOrganicEye(context, {
17413
17504
  x: faceEyeSpacing,
17414
17505
  y: faceEyeYOffset,
17415
17506
  z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
17416
- }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
17507
+ }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
17417
17508
  drawProjectedOrganicMouth(context, [
17418
17509
  {
17419
17510
  x: -mouthHalfWidth,
@@ -17457,14 +17548,28 @@ function drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCent
17457
17548
  /**
17458
17549
  * Draws the soft ground shadow below the octopus.
17459
17550
  *
17551
+ * Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
17552
+ * blurry ellipse without triggering a costly software rasterization pass on every frame.
17553
+ *
17460
17554
  * @private helper of `octopus3dAvatarVisual`
17461
17555
  */
17462
17556
  function drawOctopus3dShadow(context, size, palette, interaction, timeMs) {
17557
+ const cx = size * 0.5 + interaction.gazeX * size * 0.04;
17558
+ const cy = size * 0.87 + Math.sin(timeMs / 920) * size * 0.008;
17559
+ const rx = size * (0.18 + interaction.intensity * 0.02);
17560
+ const ry = size * 0.06;
17463
17561
  context.save();
17464
- context.fillStyle = `${palette.shadow}66`;
17465
- context.filter = `blur(${size * 0.022}px)`;
17562
+ context.translate(cx, cy);
17563
+ context.scale(1, ry / rx);
17564
+ const blurRadius = rx * 1.4;
17565
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
17566
+ shadowGradient.addColorStop(0, `${palette.shadow}7a`);
17567
+ shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
17568
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
17569
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
17570
+ context.fillStyle = shadowGradient;
17466
17571
  context.beginPath();
17467
- 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);
17572
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
17468
17573
  context.fill();
17469
17574
  context.restore();
17470
17575
  }
@@ -17695,6 +17800,35 @@ const LIGHT_DIRECTION$1 = normalizeVector3({
17695
17800
  y: -0.6,
17696
17801
  z: 0.98,
17697
17802
  });
17803
+ /**
17804
+ * Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
17805
+ *
17806
+ * @private helper of `octopus3d2AvatarVisual`
17807
+ */
17808
+ const octopus3d2StableStateCache = new WeakMap();
17809
+ /**
17810
+ * Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
17811
+ *
17812
+ * @private helper of `octopus3d2AvatarVisual`
17813
+ */
17814
+ function getOctopus3d2StableState(createRandom) {
17815
+ const cached = octopus3d2StableStateCache.get(createRandom);
17816
+ if (cached !== undefined) {
17817
+ return cached;
17818
+ }
17819
+ const animationRandom = createRandom('octopus3d2-animation-profile');
17820
+ const eyeRandom = createRandom('octopus3d2-eye-profile');
17821
+ const leftEyePhaseOffset = eyeRandom() * 0.7;
17822
+ const rightEyePhaseOffset = eyeRandom() * 0.7;
17823
+ const state = {
17824
+ morphologyProfile: createOctopus3MorphologyProfile(createRandom),
17825
+ animationPhase: animationRandom() * Math.PI * 2,
17826
+ leftEyePhaseOffset,
17827
+ rightEyePhaseOffset,
17828
+ };
17829
+ octopus3d2StableStateCache.set(createRandom, state);
17830
+ return state;
17831
+ }
17698
17832
  /**
17699
17833
  * Octopus 3D 2 avatar visual.
17700
17834
  *
@@ -17707,10 +17841,7 @@ const octopus3d2AvatarVisual = {
17707
17841
  isAnimated: true,
17708
17842
  supportsPointerTracking: true,
17709
17843
  render({ context, size, palette, createRandom, timeMs, interaction }) {
17710
- const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
17711
- const animationRandom = createRandom('octopus3d2-animation-profile');
17712
- const eyeRandom = createRandom('octopus3d2-eye-profile');
17713
- const animationPhase = animationRandom() * Math.PI * 2;
17844
+ const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3d2StableState(createRandom);
17714
17845
  const sceneCenterX = size * 0.5;
17715
17846
  const sceneCenterY = size * 0.575;
17716
17847
  const bob = Math.sin(timeMs / 940 + animationPhase) * size * 0.013;
@@ -17763,8 +17894,8 @@ const octopus3d2AvatarVisual = {
17763
17894
  const rightEyeLocalCenter = sampleBlobbyOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude);
17764
17895
  const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.78;
17765
17896
  const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.92;
17766
- drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
17767
- drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
17897
+ drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
17898
+ drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
17768
17899
  drawProjectedOrganicMouth(context, [
17769
17900
  sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
17770
17901
  sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
@@ -17793,14 +17924,28 @@ function drawBlobbyOctopusAtmosphere(context, size, palette, sceneCenterX, scene
17793
17924
  /**
17794
17925
  * Draws the soft floor shadow that anchors the single mesh in the frame.
17795
17926
  *
17927
+ * Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
17928
+ * blurry ellipse without triggering a costly software rasterization pass on every frame.
17929
+ *
17796
17930
  * @private helper of `octopus3d2AvatarVisual`
17797
17931
  */
17798
17932
  function drawBlobbyOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
17933
+ const cx = size * 0.5 + interaction.gazeX * size * 0.045;
17934
+ const cy = size * 0.88 + Math.sin(timeMs / 940) * size * 0.008;
17935
+ const rx = size * (0.18 + (morphologyProfile.body.horizontalStretch - 1) * 0.04 + interaction.intensity * 0.018);
17936
+ const ry = size * 0.062;
17799
17937
  context.save();
17800
- context.fillStyle = `${palette.shadow}66`;
17801
- context.filter = `blur(${size * 0.024}px)`;
17938
+ context.translate(cx, cy);
17939
+ context.scale(1, ry / rx);
17940
+ const blurRadius = rx * 1.4;
17941
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
17942
+ shadowGradient.addColorStop(0, `${palette.shadow}7a`);
17943
+ shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
17944
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
17945
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
17946
+ context.fillStyle = shadowGradient;
17802
17947
  context.beginPath();
17803
- 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);
17948
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
17804
17949
  context.fill();
17805
17950
  context.restore();
17806
17951
  }
@@ -17956,6 +18101,40 @@ const LIGHT_DIRECTION = normalizeVector3({
17956
18101
  * @private helper of `octopus3d3AvatarVisual`
17957
18102
  */
17958
18103
  const OCTOPUS_TENTACLE_COUNT = 8;
18104
+ /**
18105
+ * Cache keyed by the `createRandom` factory reference, which is stable for the lifetime of one
18106
+ * mounted `<Avatar/>` component (created inside `resolveAvatarRenderDefinition` and held in a
18107
+ * React `useMemo`). Using a `WeakMap` ensures the entry is collected when the component unmounts.
18108
+ *
18109
+ * @private helper of `octopus3d3AvatarVisual`
18110
+ */
18111
+ const stableStateCache = new WeakMap();
18112
+ /**
18113
+ * Returns the stable per-avatar state, computing it on first access and returning the cached
18114
+ * result on every subsequent call within the same `<Avatar/>` mount.
18115
+ *
18116
+ * @private helper of `octopus3d3AvatarVisual`
18117
+ */
18118
+ function getOctopus3d3StableState(createRandom) {
18119
+ const cached = stableStateCache.get(createRandom);
18120
+ if (cached !== undefined) {
18121
+ return cached;
18122
+ }
18123
+ const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
18124
+ const animationRandom = createRandom('octopus3d3-animation-profile');
18125
+ const eyeRandom = createRandom('octopus3d3-eye-profile');
18126
+ const leftEyePhaseOffset = eyeRandom() * 0.7;
18127
+ const rightEyePhaseOffset = eyeRandom() * 0.7;
18128
+ const state = {
18129
+ morphologyProfile,
18130
+ animationPhase: animationRandom() * Math.PI * 2,
18131
+ leftEyePhaseOffset,
18132
+ rightEyePhaseOffset,
18133
+ tentacleProfiles: createContinuousTentacleProfiles(createRandom, morphologyProfile),
18134
+ };
18135
+ stableStateCache.set(createRandom, state);
18136
+ return state;
18137
+ }
17959
18138
  /**
17960
18139
  * Octopus 3D 3 avatar visual.
17961
18140
  *
@@ -17968,11 +18147,7 @@ const octopus3d3AvatarVisual = {
17968
18147
  isAnimated: true,
17969
18148
  supportsPointerTracking: true,
17970
18149
  render({ context, size, palette, createRandom, timeMs, interaction }) {
17971
- const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
17972
- const animationRandom = createRandom('octopus3d3-animation-profile');
17973
- const eyeRandom = createRandom('octopus3d3-eye-profile');
17974
- const animationPhase = animationRandom() * Math.PI * 2;
17975
- const tentacleProfiles = createContinuousTentacleProfiles(createRandom, morphologyProfile);
18150
+ const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles } = getOctopus3d3StableState(createRandom);
17976
18151
  const sceneCenterX = size * 0.5;
17977
18152
  const sceneCenterY = size * 0.535;
17978
18153
  const bob = Math.sin(timeMs / 960 + animationPhase) * size * 0.012;
@@ -18049,8 +18224,8 @@ const octopus3d3AvatarVisual = {
18049
18224
  size,
18050
18225
  palette,
18051
18226
  });
18052
- drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
18053
- drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
18227
+ drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
18228
+ drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
18054
18229
  drawProjectedOrganicMouth(context, [
18055
18230
  sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
18056
18231
  sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
@@ -18101,14 +18276,30 @@ function drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, s
18101
18276
  /**
18102
18277
  * Draws the soft lower shadow that anchors the octopus in the avatar frame.
18103
18278
  *
18279
+ * Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
18280
+ * blurry ellipse without triggering a costly software rasterization pass on every frame.
18281
+ *
18104
18282
  * @private helper of `octopus3d3AvatarVisual`
18105
18283
  */
18106
18284
  function drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
18285
+ const cx = size * 0.5 + interaction.gazeX * size * 0.045;
18286
+ const cy = size * 0.9 + Math.sin(timeMs / 980) * size * 0.007;
18287
+ const rx = size * (0.19 + morphologyProfile.tentacles.rootSpreadScale * 0.022 + interaction.intensity * 0.02);
18288
+ const ry = size * 0.06;
18289
+ // Scale the context so that drawing a circle produces the correct ellipse aspect ratio,
18290
+ // then fill with a radial gradient that approximates the blurry edge without context.filter.
18107
18291
  context.save();
18108
- context.fillStyle = `${palette.shadow}66`;
18109
- context.filter = `blur(${size * 0.025}px)`;
18292
+ context.translate(cx, cy);
18293
+ context.scale(1, ry / rx);
18294
+ const blurRadius = rx * 1.4;
18295
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
18296
+ shadowGradient.addColorStop(0, `${palette.shadow}7a`);
18297
+ shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
18298
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
18299
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
18300
+ context.fillStyle = shadowGradient;
18110
18301
  context.beginPath();
18111
- context.ellipse(size * 0.5 + interaction.gazeX * size * 0.045, size * 0.9 + Math.sin(timeMs / 980) * size * 0.007, size * (0.19 + morphologyProfile.tentacles.rootSpreadScale * 0.022 + interaction.intensity * 0.02), size * 0.06, 0, 0, Math.PI * 2);
18302
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
18112
18303
  context.fill();
18113
18304
  context.restore();
18114
18305
  }
@@ -30484,6 +30675,7 @@ function createPromptRunnerOptions(options) {
30484
30675
  noUi: options.noUi,
30485
30676
  thinkingLevel: options.thinkingLevel,
30486
30677
  waitForUser: false,
30678
+ waitBetweenPrompts: 0,
30487
30679
  noCommit: true,
30488
30680
  ignoreGitChanges: true,
30489
30681
  normalizeLineEndings: false,
@@ -30614,6 +30806,48 @@ function resolveBookNodeAgentName(agentSource) {
30614
30806
  return Book.parse(agentSource).agentName.trim() || 'Agent';
30615
30807
  }
30616
30808
 
30809
+ // Note: [💞] Ignore a discrepancy between file name and entity name
30810
+ /**
30811
+ * All CLI harness names supported by `CliAgent` and `ptbk agent exec`.
30812
+ *
30813
+ * @public exported from `@promptbook/node`
30814
+ */
30815
+ const CLI_AGENT_HARNESS_NAMES = [
30816
+ 'openai-codex',
30817
+ 'github-copilot',
30818
+ 'cline',
30819
+ 'claude-code',
30820
+ 'opencode',
30821
+ 'gemini',
30822
+ ];
30823
+ /**
30824
+ * All supported thinking-level values for CLI coding-agent runners.
30825
+ *
30826
+ * @public exported from `@promptbook/node`
30827
+ */
30828
+ const CLI_AGENT_THINKING_LEVEL_VALUES = ['low', 'medium', 'high', 'xhigh'];
30829
+ /**
30830
+ * Environment variable used as the default runner identifier when `--harness` is omitted or not set in `CliAgent`.
30831
+ *
30832
+ * Set this to one of the harness names (`openai-codex`, `github-copilot`, `cline`, `claude-code`, `opencode`, `gemini`)
30833
+ * so that `CliAgent` and `ptbk agent exec` can run without an explicit `harness` option.
30834
+ *
30835
+ * @public exported from `@promptbook/node`
30836
+ */
30837
+ const PTBK_HARNESS_ENV = 'PTBK_HARNESS';
30838
+ /**
30839
+ * Environment variable used as the default runner model when `--model` is omitted or not set in `CliAgent`.
30840
+ *
30841
+ * @public exported from `@promptbook/node`
30842
+ */
30843
+ const PTBK_MODEL_ENV = 'PTBK_MODEL';
30844
+ /**
30845
+ * Environment variable used as the default thinking level when `--thinking-level` is omitted or not set in `CliAgent`.
30846
+ *
30847
+ * @public exported from `@promptbook/node`
30848
+ */
30849
+ const PTBK_THINKING_LEVEL_ENV = 'PTBK_THINKING_LEVEL';
30850
+
30617
30851
  /**
30618
30852
  * Default non-interactive mode used by `CliAgent`.
30619
30853
  *
@@ -30626,6 +30860,9 @@ const DEFAULT_CLI_AGENT_IS_NO_UI = true;
30626
30860
  * It uses the same harnesses and execution path as `ptbk agent exec`, running the runner
30627
30861
  * in-process instead of spawning a separate CLI process.
30628
30862
  *
30863
+ * When no `harness` is provided in the constructor or per-run options, `CliAgent` falls back
30864
+ * to the `PTBK_HARNESS` environment variable, mirroring `ptbk agent exec` behavior.
30865
+ *
30629
30866
  * @public exported from `@promptbook/node`
30630
30867
  */
30631
30868
  class CliAgent {
@@ -30641,7 +30878,7 @@ class CliAgent {
30641
30878
  * @returns Final agent answer.
30642
30879
  */
30643
30880
  async run(message, options = {}) {
30644
- var _a, _b;
30881
+ var _a, _b, _c, _d, _e, _f;
30645
30882
  const normalizedMessage = message.trim();
30646
30883
  if (!normalizedMessage) {
30647
30884
  throw new NotAllowed('Pass a non-empty message to `CliAgent.run(...)`.');
@@ -30649,15 +30886,26 @@ class CliAgent {
30649
30886
  const resolvedSource = await resolveBookNodeAgentSource(this.options);
30650
30887
  const agentPath = await this.resolveExecutableAgentPath(resolvedSource);
30651
30888
  const mergedOptions = mergeCliAgentRunOptions(this.options, options);
30889
+ const harness = (_a = mergedOptions.harness) !== null && _a !== void 0 ? _a : resolveCliAgentHarnessFromEnv();
30890
+ if (!harness) {
30891
+ throw new NotAllowed(spaceTrim(`
30892
+ No harness specified for \`CliAgent\`. Pass \`harness\` in the constructor options or per-run options,
30893
+ or set the \`${PTBK_HARNESS_ENV}\` environment variable.
30894
+
30895
+ Available harnesses: ${CLI_AGENT_HARNESS_NAMES.join(', ')}
30896
+
30897
+ Example: \`PTBK_HARNESS=claude-code\`
30898
+ `));
30899
+ }
30652
30900
  const result = await executeAgentChatTurn({
30653
30901
  agentPath,
30654
30902
  messages: [{ sender: 'USER', content: normalizedMessage }],
30655
- agentName: mergedOptions.harness,
30656
- model: mergedOptions.model,
30657
- noUi: (_a = mergedOptions.noUi) !== null && _a !== void 0 ? _a : DEFAULT_CLI_AGENT_IS_NO_UI,
30658
- thinkingLevel: mergedOptions.thinkingLevel,
30659
- allowCredits: (_b = mergedOptions.allowCredits) !== null && _b !== void 0 ? _b : false,
30660
- isVerbose: false,
30903
+ agentName: harness,
30904
+ model: (_b = mergedOptions.model) !== null && _b !== void 0 ? _b : process.env[PTBK_MODEL_ENV],
30905
+ noUi: (_c = mergedOptions.noUi) !== null && _c !== void 0 ? _c : DEFAULT_CLI_AGENT_IS_NO_UI,
30906
+ thinkingLevel: (_d = mergedOptions.thinkingLevel) !== null && _d !== void 0 ? _d : resolveCliAgentThinkingLevelFromEnv(),
30907
+ allowCredits: (_e = mergedOptions.allowCredits) !== null && _e !== void 0 ? _e : false,
30908
+ isVerbose: (_f = mergedOptions.isVerbose) !== null && _f !== void 0 ? _f : false,
30661
30909
  context: mergedOptions.context,
30662
30910
  currentWorkingDirectory: resolvedSource.currentWorkingDirectory,
30663
30911
  });
@@ -30686,16 +30934,55 @@ class CliAgent {
30686
30934
  * @private internal utility of `CliAgent`
30687
30935
  */
30688
30936
  function mergeCliAgentRunOptions(defaults, overrides) {
30689
- var _a, _b, _c, _d, _e, _f;
30937
+ var _a, _b, _c, _d, _e, _f, _g;
30690
30938
  return {
30691
30939
  allowCredits: (_a = overrides.allowCredits) !== null && _a !== void 0 ? _a : defaults.allowCredits,
30692
30940
  context: (_b = overrides.context) !== null && _b !== void 0 ? _b : defaults.context,
30693
30941
  harness: (_c = overrides.harness) !== null && _c !== void 0 ? _c : defaults.harness,
30694
- model: (_d = overrides.model) !== null && _d !== void 0 ? _d : defaults.model,
30695
- noUi: (_e = overrides.noUi) !== null && _e !== void 0 ? _e : defaults.noUi,
30696
- thinkingLevel: (_f = overrides.thinkingLevel) !== null && _f !== void 0 ? _f : defaults.thinkingLevel,
30942
+ isVerbose: (_d = overrides.isVerbose) !== null && _d !== void 0 ? _d : defaults.isVerbose,
30943
+ model: (_e = overrides.model) !== null && _e !== void 0 ? _e : defaults.model,
30944
+ noUi: (_f = overrides.noUi) !== null && _f !== void 0 ? _f : defaults.noUi,
30945
+ thinkingLevel: (_g = overrides.thinkingLevel) !== null && _g !== void 0 ? _g : defaults.thinkingLevel,
30697
30946
  };
30698
30947
  }
30948
+ /**
30949
+ * Reads and validates the harness name from the `PTBK_HARNESS` environment variable.
30950
+ *
30951
+ * @private internal utility of `CliAgent`
30952
+ */
30953
+ function resolveCliAgentHarnessFromEnv() {
30954
+ const envValue = process.env[PTBK_HARNESS_ENV];
30955
+ if (!envValue) {
30956
+ return undefined;
30957
+ }
30958
+ if (!CLI_AGENT_HARNESS_NAMES.includes(envValue)) {
30959
+ throw new NotAllowed(spaceTrim(`
30960
+ Invalid value for \`${PTBK_HARNESS_ENV}\` environment variable: \`${envValue}\`
30961
+
30962
+ Must be one of: ${CLI_AGENT_HARNESS_NAMES.join(', ')}
30963
+ `));
30964
+ }
30965
+ return envValue;
30966
+ }
30967
+ /**
30968
+ * Reads and validates the thinking level from the `PTBK_THINKING_LEVEL` environment variable.
30969
+ *
30970
+ * @private internal utility of `CliAgent`
30971
+ */
30972
+ function resolveCliAgentThinkingLevelFromEnv() {
30973
+ const envValue = process.env[PTBK_THINKING_LEVEL_ENV];
30974
+ if (!envValue) {
30975
+ return undefined;
30976
+ }
30977
+ if (!CLI_AGENT_THINKING_LEVEL_VALUES.includes(envValue)) {
30978
+ throw new NotAllowed(spaceTrim(`
30979
+ Invalid value for \`${PTBK_THINKING_LEVEL_ENV}\` environment variable: \`${envValue}\`
30980
+
30981
+ Must be one of: ${CLI_AGENT_THINKING_LEVEL_VALUES.join(', ')}
30982
+ `));
30983
+ }
30984
+ return envValue;
30985
+ }
30699
30986
  /**
30700
30987
  * Creates the stable temporary path used when `CliAgent` is initialized from in-memory Book source.
30701
30988
  *
@@ -33477,7 +33764,7 @@ class OpenAiVectorStoreKnowledgeSourcePreparer {
33477
33764
  return { skippedReason: 'invalid_data_url' };
33478
33765
  }
33479
33766
  return {
33480
- file: new File([parsed.buffer], parsed.filename, {
33767
+ file: new File([new Uint8Array(parsed.buffer)], parsed.filename, {
33481
33768
  type: parsed.mimeType,
33482
33769
  }),
33483
33770
  sizeBytes: parsed.buffer.length,
@@ -47615,5 +47902,5 @@ var RemoteAgent$1 = /*#__PURE__*/Object.freeze({
47615
47902
  RemoteAgent: RemoteAgent
47616
47903
  });
47617
47904
 
47618
- export { $execCommand, $execCommands, $provideExecutablesForNode, $provideExecutionToolsForNode, $provideFilesystemForNode, $provideLlmToolsConfigurationFromEnv, $provideLlmToolsFromEnv, $provideScrapersForNode, $provideScriptingForNode, AGENT_BOOK_FILE_PATH, AGENT_FAILED_MESSAGES_DIRECTORY_PATH, AGENT_FINISHED_MESSAGES_DIRECTORY_PATH, AGENT_MESSAGES_DIRECTORY_PATH, AGENT_QUEUED_MESSAGES_DIRECTORY_PATH, BOOK_LANGUAGE_VERSION, CliAgent, FileCacheStorage, LiteAgent, PROMPTBOOK_ENGINE_VERSION, createPipelineCollectionFromDirectory, getAllCommitmentsToolFunctionsForNode };
47905
+ export { $execCommand, $execCommands, $provideExecutablesForNode, $provideExecutionToolsForNode, $provideFilesystemForNode, $provideLlmToolsConfigurationFromEnv, $provideLlmToolsFromEnv, $provideScrapersForNode, $provideScriptingForNode, AGENT_BOOK_FILE_PATH, AGENT_FAILED_MESSAGES_DIRECTORY_PATH, AGENT_FINISHED_MESSAGES_DIRECTORY_PATH, AGENT_MESSAGES_DIRECTORY_PATH, AGENT_QUEUED_MESSAGES_DIRECTORY_PATH, BOOK_LANGUAGE_VERSION, CLI_AGENT_HARNESS_NAMES, CLI_AGENT_THINKING_LEVEL_VALUES, CliAgent, FileCacheStorage, LiteAgent, PROMPTBOOK_ENGINE_VERSION, PTBK_HARNESS_ENV, PTBK_MODEL_ENV, PTBK_THINKING_LEVEL_ENV, createPipelineCollectionFromDirectory, getAllCommitmentsToolFunctionsForNode };
47619
47906
  //# sourceMappingURL=index.es.js.map