@promptbook/components 0.112.0-47 → 0.112.0-49

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 (51) hide show
  1. package/esm/index.es.js +1175 -167
  2. package/esm/index.es.js.map +1 -1
  3. package/esm/src/avatars/AvatarOrImage.d.ts +5 -1
  4. package/esm/src/avatars/avatarAnimationScheduler.d.ts +16 -0
  5. package/esm/src/avatars/avatarInteractionUtils.d.ts +81 -0
  6. package/esm/src/avatars/avatarInteractionUtils.test.d.ts +1 -0
  7. package/esm/src/avatars/avatarPointerTracking.d.ts +33 -0
  8. package/esm/src/avatars/avatarRenderingUtils.d.ts +3 -2
  9. package/esm/src/avatars/avatarRenderingUtils.test.d.ts +1 -0
  10. package/esm/src/avatars/avatarVisibilityTracking.d.ts +17 -0
  11. package/esm/src/avatars/index.d.ts +1 -1
  12. package/esm/src/avatars/renderAvatarVisual.d.ts +29 -1
  13. package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +35 -0
  14. package/esm/src/avatars/visuals/octopusAvatarVisualShared.d.ts +34 -0
  15. package/esm/src/avatars/visuals/octopusAvatarVisualShared.test.d.ts +1 -0
  16. package/esm/src/book-components/Chat/Chat/ChatProps.d.ts +3 -3
  17. package/esm/src/book-components/Chat/Chat/TeamToolCallModalContent.test.d.ts +2 -0
  18. package/esm/src/commitments/USE/USE.d.ts +1 -0
  19. package/esm/src/commitments/USE/aggregateUseCommitmentSystemMessages.d.ts +1 -1
  20. package/esm/src/commitments/USE_DEEPSEARCH/USE_DEEPSEARCH.d.ts +47 -0
  21. package/esm/src/commitments/USE_DEEPSEARCH/USE_DEEPSEARCH.test.d.ts +1 -0
  22. package/esm/src/commitments/_common/createSerpSearchToolFunction.d.ts +12 -0
  23. package/esm/src/commitments/index.d.ts +2 -1
  24. package/esm/src/llm-providers/openai/OpenAiAgentKitExecutionTools.test.d.ts +1 -0
  25. package/esm/src/version.d.ts +1 -1
  26. package/package.json +1 -1
  27. package/umd/index.umd.js +1174 -166
  28. package/umd/index.umd.js.map +1 -1
  29. package/umd/src/avatars/AvatarOrImage.d.ts +5 -1
  30. package/umd/src/avatars/avatarAnimationScheduler.d.ts +16 -0
  31. package/umd/src/avatars/avatarInteractionUtils.d.ts +81 -0
  32. package/umd/src/avatars/avatarInteractionUtils.test.d.ts +1 -0
  33. package/umd/src/avatars/avatarPointerTracking.d.ts +33 -0
  34. package/umd/src/avatars/avatarRenderingUtils.d.ts +3 -2
  35. package/umd/src/avatars/avatarRenderingUtils.test.d.ts +1 -0
  36. package/umd/src/avatars/avatarVisibilityTracking.d.ts +17 -0
  37. package/umd/src/avatars/index.d.ts +1 -1
  38. package/umd/src/avatars/renderAvatarVisual.d.ts +29 -1
  39. package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +35 -0
  40. package/umd/src/avatars/visuals/octopusAvatarVisualShared.d.ts +34 -0
  41. package/umd/src/avatars/visuals/octopusAvatarVisualShared.test.d.ts +1 -0
  42. package/umd/src/book-components/Chat/Chat/ChatProps.d.ts +3 -3
  43. package/umd/src/book-components/Chat/Chat/TeamToolCallModalContent.test.d.ts +2 -0
  44. package/umd/src/commitments/USE/USE.d.ts +1 -0
  45. package/umd/src/commitments/USE/aggregateUseCommitmentSystemMessages.d.ts +1 -1
  46. package/umd/src/commitments/USE_DEEPSEARCH/USE_DEEPSEARCH.d.ts +47 -0
  47. package/umd/src/commitments/USE_DEEPSEARCH/USE_DEEPSEARCH.test.d.ts +1 -0
  48. package/umd/src/commitments/_common/createSerpSearchToolFunction.d.ts +12 -0
  49. package/umd/src/commitments/index.d.ts +2 -1
  50. package/umd/src/llm-providers/openai/OpenAiAgentKitExecutionTools.test.d.ts +1 -0
  51. package/umd/src/version.d.ts +1 -1
package/esm/index.es.js CHANGED
@@ -21,7 +21,7 @@ import { forTime } from 'waitasecond';
21
21
  import sha256 from 'crypto-js/sha256';
22
22
  import { parse, unparse } from 'papaparse';
23
23
  import colors from 'colors';
24
- import { Agent as Agent$1, setDefaultOpenAIClient, setDefaultOpenAIKey, fileSearchTool, tool, run } from '@openai/agents';
24
+ import { Agent as Agent$1, setDefaultOpenAIClient, setDefaultOpenAIKey, fileSearchTool, tool, run, webSearchTool } from '@openai/agents';
25
25
  import Bottleneck from 'bottleneck';
26
26
  import OpenAI from 'openai';
27
27
  import QRCode from 'qrcode';
@@ -40,7 +40,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
40
40
  * @generated
41
41
  * @see https://github.com/webgptorg/promptbook
42
42
  */
43
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-47';
43
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-49';
44
44
  /**
45
45
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
46
46
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -1402,11 +1402,12 @@ function createAvatarDefinitionFromAgentBasicInformation(agentBasicInformation)
1402
1402
  * Creates the shared derived palette used by every avatar visual.
1403
1403
  *
1404
1404
  * @param avatarDefinition Stable avatar definition.
1405
+ * @param surface Surface style used by the parent UI.
1405
1406
  * @returns Derived palette.
1406
1407
  *
1407
1408
  * @private utility of the avatar rendering system
1408
1409
  */
1409
- function createAvatarPalette(avatarDefinition) {
1410
+ function createAvatarPalette(avatarDefinition, surface = 'framed') {
1410
1411
  const normalizedAvatarDefinition = normalizeAvatarDefinition(avatarDefinition);
1411
1412
  const primaryColor = Color.fromSafe(normalizedAvatarDefinition.colors[0] || PROMPTBOOK_COLOR);
1412
1413
  const secondaryColor = Color.fromSafe(normalizedAvatarDefinition.colors[1] || primaryColor.then(lighten(0.12)).then(saturate(0.16)));
@@ -1416,8 +1417,8 @@ function createAvatarPalette(avatarDefinition) {
1416
1417
  const highlightColor = Color.fromSafe(accentColor.then(lighten(0.22)).then(saturate(0.08)));
1417
1418
  const shadowColor = Color.fromSafe(primaryColor.then(darken(0.46)).then(saturate(0.14)));
1418
1419
  return {
1419
- background: backgroundColor.toHex(),
1420
- backgroundSecondary: backgroundSecondaryColor.toHex(),
1420
+ background: surface === 'transparent' ? 'transparent' : backgroundColor.toHex(),
1421
+ backgroundSecondary: surface === 'transparent' ? 'transparent' : backgroundSecondaryColor.toHex(),
1421
1422
  primary: primaryColor.toHex(),
1422
1423
  secondary: secondaryColor.toHex(),
1423
1424
  accent: accentColor.toHex(),
@@ -1436,6 +1437,9 @@ function createAvatarPalette(avatarDefinition) {
1436
1437
  * @private utility of the avatar rendering system
1437
1438
  */
1438
1439
  function drawAvatarFrame(context, size, palette) {
1440
+ if (palette.background === 'transparent' && palette.backgroundSecondary === 'transparent') {
1441
+ return;
1442
+ }
1439
1443
  const gradient = context.createLinearGradient(0, 0, size, size);
1440
1444
  gradient.addColorStop(0, palette.background);
1441
1445
  gradient.addColorStop(1, palette.backgroundSecondary);
@@ -1515,10 +1519,22 @@ function createAvatarRandomFactory(avatarDefinition) {
1515
1519
  */
1516
1520
  function prepareAvatarCanvas(canvas, context, size, devicePixelRatio) {
1517
1521
  const normalizedDevicePixelRatio = Math.max(1, Math.round(devicePixelRatio * 100) / 100);
1518
- canvas.width = Math.round(size * normalizedDevicePixelRatio);
1519
- canvas.height = Math.round(size * normalizedDevicePixelRatio);
1520
- canvas.style.width = `${size}px`;
1521
- canvas.style.height = `${size}px`;
1522
+ const nextCanvasWidth = Math.round(size * normalizedDevicePixelRatio);
1523
+ const nextCanvasHeight = Math.round(size * normalizedDevicePixelRatio);
1524
+ const nextCanvasStyleWidth = `${size}px`;
1525
+ const nextCanvasStyleHeight = `${size}px`;
1526
+ if (canvas.width !== nextCanvasWidth) {
1527
+ canvas.width = nextCanvasWidth;
1528
+ }
1529
+ if (canvas.height !== nextCanvasHeight) {
1530
+ canvas.height = nextCanvasHeight;
1531
+ }
1532
+ if (canvas.style.width !== nextCanvasStyleWidth) {
1533
+ canvas.style.width = nextCanvasStyleWidth;
1534
+ }
1535
+ if (canvas.style.height !== nextCanvasStyleHeight) {
1536
+ canvas.style.height = nextCanvasStyleHeight;
1537
+ }
1522
1538
  context.setTransform(normalizedDevicePixelRatio, 0, 0, normalizedDevicePixelRatio, 0, 0);
1523
1539
  context.clearRect(0, 0, size, size);
1524
1540
  }
@@ -1587,7 +1603,7 @@ function generatePlaceholderAgentProfileImageUrl(agentIdOrName, agentsServerUrl)
1587
1603
  *
1588
1604
  * @private shared avatar contract
1589
1605
  */
1590
- const DEFAULT_AGENT_AVATAR_VISUAL_ID = 'octopus2';
1606
+ const DEFAULT_AGENT_AVATAR_VISUAL_ID = 'octopus3';
1591
1607
  /**
1592
1608
  * Resolve a base URL for relative images, preferring the provided base or browser location.
1593
1609
  *
@@ -11732,6 +11748,7 @@ class TemplateCommitmentDefinition extends BaseCommitmentDefinition {
11732
11748
  * Supported USE types:
11733
11749
  * - USE BROWSER: Enables the agent to use a web browser tool
11734
11750
  * - USE SEARCH ENGINE (future): Enables search engine access
11751
+ * - USE DEEPSEARCH: Enables deeper research-oriented search access
11735
11752
  * - USE FILE SYSTEM (future): Enables file system operations
11736
11753
  * - USE MCP (future): Enables MCP server connections
11737
11754
  *
@@ -11754,7 +11771,7 @@ class UseCommitmentDefinition extends BaseCommitmentDefinition {
11754
11771
  * Short one-line description of USE commitments.
11755
11772
  */
11756
11773
  get description() {
11757
- return 'Enable the agent to use specific tools or capabilities (BROWSER, SEARCH ENGINE, etc.).';
11774
+ return 'Enable the agent to use specific tools or capabilities (BROWSER, SEARCH ENGINE, DEEPSEARCH, etc.).';
11758
11775
  }
11759
11776
  /**
11760
11777
  * Icon for this commitment.
@@ -11775,6 +11792,7 @@ class UseCommitmentDefinition extends BaseCommitmentDefinition {
11775
11792
 
11776
11793
  - **USE BROWSER** - Enables the agent to use a web browser tool to access and retrieve information from the internet
11777
11794
  - **USE SEARCH ENGINE** (future) - Enables search engine access
11795
+ - **USE DEEPSEARCH** - Enables deeper research-oriented search access
11778
11796
  - **USE FILE SYSTEM** (future) - Enables file system operations
11779
11797
  - **USE MCP** (future) - Enables MCP server connections
11780
11798
 
@@ -11829,7 +11847,7 @@ class UseCommitmentDefinition extends BaseCommitmentDefinition {
11829
11847
  * Checks if this is a known USE type
11830
11848
  */
11831
11849
  isKnownUseType(useType) {
11832
- const knownTypes = ['BROWSER', 'SEARCH ENGINE', 'FILE SYSTEM', 'MCP'];
11850
+ const knownTypes = ['BROWSER', 'SEARCH ENGINE', 'DEEPSEARCH', 'FILE SYSTEM', 'MCP'];
11833
11851
  return knownTypes.includes(useType.toUpperCase());
11834
11852
  }
11835
11853
  }
@@ -11842,6 +11860,7 @@ class UseCommitmentDefinition extends BaseCommitmentDefinition {
11842
11860
  */
11843
11861
  const AGGREGATED_USE_COMMITMENT_TYPES = [
11844
11862
  'USE BROWSER',
11863
+ 'USE DEEPSEARCH',
11845
11864
  'USE SEARCH ENGINE',
11846
11865
  'USE TIME',
11847
11866
  ];
@@ -11921,6 +11940,15 @@ function createAggregatedUseCommitmentSystemMessage(type, additionalInstructions
11921
11940
  - Do not tell the user you cannot search for information, YOU CAN.
11922
11941
  ${block(formatOptionalInstructionBlock('Search instructions', combinedAdditionalInstructions))}
11923
11942
  `);
11943
+ case 'USE DEEPSEARCH':
11944
+ return spaceTrim$1((block) => `
11945
+ Tool:
11946
+ - You have access to DeepSearch via the tool "deep_search".
11947
+ - Use it for broader research tasks that need multi-step investigation, comparison, or synthesis across multiple sources.
11948
+ - Prefer it over quick search when the user asks for a well-grounded brief, report, or deeper investigation.
11949
+ - Do not pretend you cannot research current information when this tool is available.
11950
+ ${block(formatOptionalInstructionBlock('DeepSearch instructions', combinedAdditionalInstructions))}
11951
+ `);
11924
11952
  }
11925
11953
  }
11926
11954
  /**
@@ -13470,6 +13498,207 @@ function addConfiguredCalendarIfMissing(configuredCalendars, calendarReference)
13470
13498
  }
13471
13499
  // Note: [💞] Ignore a discrepancy between file name and entity name
13472
13500
 
13501
+ /**
13502
+ * A search engine implementation that uses the SerpApi to fetch Google search results.
13503
+ *
13504
+ * @private <- TODO: !!!! Export via some package
13505
+ */
13506
+ class SerpSearchEngine {
13507
+ get title() {
13508
+ return 'SerpApi Search Engine';
13509
+ }
13510
+ get description() {
13511
+ return 'Search engine that uses SerpApi to fetch Google search results';
13512
+ }
13513
+ checkConfiguration() {
13514
+ if (!process.env.SERP_API_KEY) {
13515
+ throw new Error('SERP_API_KEY is not configured');
13516
+ }
13517
+ }
13518
+ async search(query, options = {}) {
13519
+ const apiKey = process.env.SERP_API_KEY;
13520
+ if (!apiKey) {
13521
+ throw new Error('SERP_API_KEY is not configured');
13522
+ }
13523
+ const url = new URL('https://serpapi.com/search');
13524
+ url.searchParams.set('api_key', apiKey);
13525
+ url.searchParams.set('engine', 'google');
13526
+ url.searchParams.set('q', query);
13527
+ for (const [key, value] of Object.entries(options)) {
13528
+ url.searchParams.set(key, String(value));
13529
+ }
13530
+ const response = await fetch(url.toString());
13531
+ if (!response.ok) {
13532
+ const body = await response.text();
13533
+ throw new Error(`SerpApi failed with status ${response.status}: ${response.statusText}\n${body}`);
13534
+ }
13535
+ const data = (await response.json());
13536
+ return (data.organic_results || []).map((item) => ({
13537
+ title: item.title,
13538
+ url: item.link,
13539
+ snippet: item.snippet || '',
13540
+ }));
13541
+ }
13542
+ }
13543
+
13544
+ /**
13545
+ * Creates one SERP-backed tool function used as a local fallback for search-like commitments.
13546
+ *
13547
+ * @param toolName - Technical tool name used for validation messages.
13548
+ * @param resultLabel - Human-readable label used in formatted results.
13549
+ * @returns Async tool function compatible with commitment tool registration.
13550
+ *
13551
+ * @private internal helper for search-like commitments
13552
+ */
13553
+ function createSerpSearchToolFunction(toolName, resultLabel) {
13554
+ return async (rawArgs) => {
13555
+ const { query, ...searchOptions } = rawArgs;
13556
+ if (typeof query !== 'string' || !query.trim()) {
13557
+ throw new Error(`${toolName} query is required`);
13558
+ }
13559
+ const searchEngine = new SerpSearchEngine();
13560
+ const results = await searchEngine.search(query, searchOptions);
13561
+ return spaceTrim$1((block) => `
13562
+ ${resultLabel} results for "${query}"${Object.keys(searchOptions).length === 0
13563
+ ? ''
13564
+ : ` with options ${JSON.stringify(searchOptions)}`}:
13565
+
13566
+ ${block(results
13567
+ .map((result) => spaceTrim$1(`
13568
+ - **${result.title}**
13569
+ ${result.url}
13570
+ ${result.snippet}
13571
+ `))
13572
+ .join('\n\n'))}
13573
+ `);
13574
+ };
13575
+ }
13576
+
13577
+ /**
13578
+ * USE DEEPSEARCH commitment definition
13579
+ *
13580
+ * The `USE DEEPSEARCH` commitment indicates that the agent should use a deeper research-oriented
13581
+ * search workflow instead of lightweight web search when it needs fresh information from the internet.
13582
+ *
13583
+ * The content following `USE DEEPSEARCH` is an arbitrary text that the agent should know
13584
+ * (e.g. search scope or research instructions).
13585
+ *
13586
+ * Example usage in agent source:
13587
+ *
13588
+ * ```book
13589
+ * USE DEEPSEARCH
13590
+ * USE DEEPSEARCH Compare official vendor documentation with independent benchmarks.
13591
+ * ```
13592
+ *
13593
+ * @private [🪔] Maybe export the commitments through some package
13594
+ */
13595
+ class UseDeepSearchCommitmentDefinition extends BaseCommitmentDefinition {
13596
+ constructor() {
13597
+ super('USE DEEPSEARCH');
13598
+ }
13599
+ get requiresContent() {
13600
+ return false;
13601
+ }
13602
+ /**
13603
+ * Short one-line description of USE DEEPSEARCH.
13604
+ */
13605
+ get description() {
13606
+ return 'Enable the agent to use DeepSearch for more thorough internet research.';
13607
+ }
13608
+ /**
13609
+ * Icon for this commitment.
13610
+ */
13611
+ get icon() {
13612
+ return '🔬';
13613
+ }
13614
+ /**
13615
+ * Markdown documentation for USE DEEPSEARCH commitment.
13616
+ */
13617
+ get documentation() {
13618
+ return spaceTrim$1(`
13619
+ # USE DEEPSEARCH
13620
+
13621
+ Enables the agent to use DeepSearch for broader, more thorough internet research than lightweight web search.
13622
+
13623
+ ## Key aspects
13624
+
13625
+ - The content following \`USE DEEPSEARCH\` is arbitrary guidance for the research workflow.
13626
+ - In Agents Server, the OpenAI Agents SDK runtime uses a nested deep-research agent for this tool.
13627
+ - Use this for investigations, comparisons, market scans, or other tasks that benefit from deeper synthesis.
13628
+ - Prefer regular \`USE SEARCH ENGINE\` when a quick factual lookup is enough.
13629
+
13630
+ ## Examples
13631
+
13632
+ \`\`\`book
13633
+ Due Diligence Researcher
13634
+
13635
+ GOAL Investigate vendors thoroughly before making recommendations.
13636
+ USE DEEPSEARCH Compare official sources with credible third-party analysis.
13637
+ RULE Cite the strongest supporting sources in the final answer.
13638
+ \`\`\`
13639
+
13640
+ \`\`\`book
13641
+ Market Analyst
13642
+
13643
+ GOAL Build concise but well-grounded research briefs.
13644
+ USE DEEPSEARCH Focus on recent public information and competing viewpoints.
13645
+ CLOSED
13646
+ \`\`\`
13647
+ `);
13648
+ }
13649
+ applyToAgentModelRequirements(requirements, content) {
13650
+ const existingTools = requirements.tools || [];
13651
+ const updatedTools = existingTools.some((tool) => tool.name === 'deep_search')
13652
+ ? existingTools
13653
+ : [
13654
+ ...existingTools,
13655
+ {
13656
+ name: 'deep_search',
13657
+ description: spaceTrim$1(`
13658
+ Research the internet deeply and synthesize a grounded answer.
13659
+ Use this tool for broader investigations, comparisons, and requests that need more than a quick search.
13660
+ `),
13661
+ parameters: {
13662
+ type: 'object',
13663
+ properties: {
13664
+ query: {
13665
+ type: 'string',
13666
+ description: 'The research question or investigation request.',
13667
+ },
13668
+ },
13669
+ required: ['query'],
13670
+ additionalProperties: false,
13671
+ },
13672
+ },
13673
+ ];
13674
+ return appendAggregatedUseCommitmentPlaceholder({
13675
+ ...requirements,
13676
+ tools: updatedTools,
13677
+ _metadata: {
13678
+ ...requirements._metadata,
13679
+ useDeepSearch: content || true,
13680
+ },
13681
+ }, this.type);
13682
+ }
13683
+ /**
13684
+ * Gets human-readable titles for tool functions provided by this commitment.
13685
+ */
13686
+ getToolTitles() {
13687
+ return {
13688
+ deep_search: 'DeepSearch',
13689
+ };
13690
+ }
13691
+ /**
13692
+ * Gets the local fallback implementation for the `deep_search` tool.
13693
+ */
13694
+ getToolFunctions() {
13695
+ return {
13696
+ deep_search: createSerpSearchToolFunction('deep_search', 'DeepSearch'),
13697
+ };
13698
+ }
13699
+ }
13700
+ // Note: [💞] Ignore a discrepancy between file name and entity name
13701
+
13473
13702
  /**
13474
13703
  * Lightweight email token matcher used for `USE EMAIL` first-line parsing.
13475
13704
  *
@@ -15701,49 +15930,6 @@ function addConfiguredProjectIfMissing(configuredProjects, repositoryReference)
15701
15930
  }
15702
15931
  // Note: [💞] Ignore a discrepancy between file name and entity name
15703
15932
 
15704
- /**
15705
- * A search engine implementation that uses the SerpApi to fetch Google search results.
15706
- *
15707
- * @private <- TODO: !!!! Export via some package
15708
- */
15709
- class SerpSearchEngine {
15710
- get title() {
15711
- return 'SerpApi Search Engine';
15712
- }
15713
- get description() {
15714
- return 'Search engine that uses SerpApi to fetch Google search results';
15715
- }
15716
- checkConfiguration() {
15717
- if (!process.env.SERP_API_KEY) {
15718
- throw new Error('SERP_API_KEY is not configured');
15719
- }
15720
- }
15721
- async search(query, options = {}) {
15722
- const apiKey = process.env.SERP_API_KEY;
15723
- if (!apiKey) {
15724
- throw new Error('SERP_API_KEY is not configured');
15725
- }
15726
- const url = new URL('https://serpapi.com/search');
15727
- url.searchParams.set('api_key', apiKey);
15728
- url.searchParams.set('engine', 'google');
15729
- url.searchParams.set('q', query);
15730
- for (const [key, value] of Object.entries(options)) {
15731
- url.searchParams.set(key, String(value));
15732
- }
15733
- const response = await fetch(url.toString());
15734
- if (!response.ok) {
15735
- const body = await response.text();
15736
- throw new Error(`SerpApi failed with status ${response.status}: ${response.statusText}\n${body}`);
15737
- }
15738
- const data = (await response.json());
15739
- return (data.organic_results || []).map((item) => ({
15740
- title: item.title,
15741
- url: item.link,
15742
- snippet: item.snippet || '',
15743
- }));
15744
- }
15745
- }
15746
-
15747
15933
  /**
15748
15934
  * USE SEARCH ENGINE commitment definition
15749
15935
  *
@@ -15887,26 +16073,7 @@ class UseSearchEngineCommitmentDefinition extends BaseCommitmentDefinition {
15887
16073
  */
15888
16074
  getToolFunctions() {
15889
16075
  return {
15890
- async web_search(args) {
15891
- console.log('!!!! [Tool] web_search called', { args });
15892
- const { query, ...options } = args;
15893
- if (!query) {
15894
- throw new Error('Search query is required');
15895
- }
15896
- const searchEngine = new SerpSearchEngine();
15897
- const results = await searchEngine.search(query, options);
15898
- return spaceTrim$1((block) => `
15899
- Search results for "${query}"${Object.keys(options).length === 0 ? '' : ` with options ${JSON.stringify(options)}`}:
15900
-
15901
- ${block(results
15902
- .map((result) => spaceTrim$1(`
15903
- - **${result.title}**
15904
- ${result.url}
15905
- ${result.snippet}
15906
- `))
15907
- .join('\n\n'))}
15908
- `);
15909
- },
16076
+ web_search: createSerpSearchToolFunction('web_search', 'Search'),
15910
16077
  };
15911
16078
  }
15912
16079
  }
@@ -17552,6 +17719,7 @@ const COMMITMENT_REGISTRY = [
17552
17719
  new ClosedCommitmentDefinition(),
17553
17720
  new TeamCommitmentDefinition(),
17554
17721
  new UseBrowserCommitmentDefinition(),
17722
+ new UseDeepSearchCommitmentDefinition(),
17555
17723
  new UseSearchEngineCommitmentDefinition(),
17556
17724
  new UseSpawnCommitmentDefinition(),
17557
17725
  new UseTimeoutCommitmentDefinition(),
@@ -17863,6 +18031,11 @@ const SIMPLE_CAPABILITY_BY_COMMITMENT_TYPE = {
17863
18031
  label: 'Internet',
17864
18032
  iconName: 'Search',
17865
18033
  },
18034
+ 'USE DEEPSEARCH': {
18035
+ type: 'search-engine',
18036
+ label: 'DeepSearch',
18037
+ iconName: 'Search',
18038
+ },
17866
18039
  'USE TIME': {
17867
18040
  type: 'time',
17868
18041
  label: 'Time',
@@ -25738,6 +25911,496 @@ function ChatInputArea(props) {
25738
25911
  }), children: jsx(SendIcon, { size: 25 }) })] }), speechRecognition && (jsx(ChatInputAreaDictationPanel, { bubbleText: speechRecognitionUiDescriptor.bubbleText, bubbleTone: speechRecognitionUiDescriptor.bubbleTone, shouldShowPanel: shouldShowDictationPanel, isExpanded: isDictationPanelExpanded, interimText: dictationInterimText, error: dictationError, lastFinalChunk: dictationLastFinalChunk, editableChunk: dictationEditableChunk, canBacktrack: canBacktrack, dictationSettings: dictationSettings, isBrowserSpeechFallbackSupported: isBrowserSpeechFallbackSupported, canOpenBrowserSettings: canOpenBrowserSettings, onToggleExpanded: toggleDictationPanel, onExpand: expandDictationPanel, onEditableChunkChange: setDictationEditableChunk, onRetryPermissionRequest: handleRetryPermissionRequest, onOpenBrowserSettings: handleOpenBrowserSettings, onApplyCorrection: handleApplyCorrection, onBacktrackLastChunk: handleBacktrackLastChunk, onDictationSettingChange: handleDictationSettingChange })), isUploading && (jsxs("div", { className: styles$5.uploadProgress, children: [jsx("div", { className: styles$5.uploadProgressBar, children: jsx("div", { className: styles$5.uploadProgressFill }) }), jsx("span", { children: "Uploading files..." })] })), isDragOver && onFileUpload && (jsx("div", { className: styles$5.dragOverlay, children: jsxs("div", { className: styles$5.dragOverlayContent, children: [jsx(AttachmentIcon, { size: 48 }), jsx("span", { children: "Drop files here to upload" })] }) }))] }));
25739
25912
  }
25740
25913
 
25914
+ // Note: [💞] Ignore a discrepancy between file name and entity name
25915
+ /**
25916
+ * Maximum normalized eye travel used when the viewer moves across the viewport.
25917
+ *
25918
+ * @private utility of the avatar rendering system
25919
+ */
25920
+ const MAX_GAZE_OFFSET = 0.78;
25921
+ /**
25922
+ * Maximum normalized body lean used for subtle mantle response.
25923
+ *
25924
+ * @private utility of the avatar rendering system
25925
+ */
25926
+ const MAX_BODY_OFFSET = 0.28;
25927
+ /**
25928
+ * Smoothing window used while a live pointer or touch target is active.
25929
+ *
25930
+ * @private utility of the avatar rendering system
25931
+ */
25932
+ const ACTIVE_INTERACTION_SMOOTHING_MS = 90;
25933
+ /**
25934
+ * Slower smoothing window used when easing the avatar back to its idle state.
25935
+ *
25936
+ * @private utility of the avatar rendering system
25937
+ */
25938
+ const IDLE_INTERACTION_SMOOTHING_MS = 230;
25939
+ /**
25940
+ * Maximum frame delta allowed when smoothing interaction after tab stalls.
25941
+ *
25942
+ * @private utility of the avatar rendering system
25943
+ */
25944
+ const MAX_INTERACTION_FRAME_DELTA_MS = 64;
25945
+ /**
25946
+ * Extra damping used for the slower body lean compared with the quicker eye motion.
25947
+ *
25948
+ * @private utility of the avatar rendering system
25949
+ */
25950
+ const BODY_INTERACTION_SMOOTHING_MULTIPLIER = 1.2;
25951
+ /**
25952
+ * Stable zeroed interaction state used by non-interactive render paths.
25953
+ *
25954
+ * @private utility of the avatar rendering system
25955
+ */
25956
+ const IDLE_AVATAR_INTERACTION_STATE = {
25957
+ gazeX: 0,
25958
+ gazeY: 0,
25959
+ bodyOffsetX: 0,
25960
+ bodyOffsetY: 0,
25961
+ intensity: 0,
25962
+ isPointerActive: false,
25963
+ pointerType: 'idle',
25964
+ };
25965
+ /**
25966
+ * Creates one stable cache key from the meaningful avatar-definition fields.
25967
+ *
25968
+ * @param avatarDefinition Normalized or raw avatar definition.
25969
+ * @returns Stable cache key that ignores object identity churn.
25970
+ *
25971
+ * @private utility of the avatar rendering system
25972
+ */
25973
+ function createAvatarDefinitionKey(avatarDefinition) {
25974
+ const normalizedAvatarDefinition = normalizeAvatarDefinition(avatarDefinition);
25975
+ return [
25976
+ normalizedAvatarDefinition.agentName,
25977
+ normalizedAvatarDefinition.agentHash,
25978
+ normalizedAvatarDefinition.colors.join('|'),
25979
+ ].join('::');
25980
+ }
25981
+ /**
25982
+ * Returns the neutral interaction state used by static/server-side renders.
25983
+ *
25984
+ * @returns Zeroed interaction state.
25985
+ *
25986
+ * @private utility of the avatar rendering system
25987
+ */
25988
+ function createIdleAvatarInteractionState() {
25989
+ return IDLE_AVATAR_INTERACTION_STATE;
25990
+ }
25991
+ /**
25992
+ * Creates a fresh runtime state for the interactive animation loop.
25993
+ *
25994
+ * @returns Runtime interaction state with neutral values.
25995
+ *
25996
+ * @private utility of the avatar rendering system
25997
+ */
25998
+ function createAvatarInteractionRuntimeState() {
25999
+ return {
26000
+ ...IDLE_AVATAR_INTERACTION_STATE,
26001
+ lastFrameMs: null,
26002
+ };
26003
+ }
26004
+ /**
26005
+ * Converts the shared viewport pointer state into one avatar-local gaze target.
26006
+ *
26007
+ * @param avatarBounds Canvas bounds in viewport coordinates.
26008
+ * @param pointerSnapshot Latest shared pointer sample.
26009
+ * @returns Local target used to steer eyes and subtle body lean.
26010
+ *
26011
+ * @private utility of the avatar rendering system
26012
+ */
26013
+ function resolveAvatarPointerTarget(avatarBounds, pointerSnapshot) {
26014
+ if (!pointerSnapshot || !pointerSnapshot.isPointerActive) {
26015
+ return {
26016
+ ...IDLE_AVATAR_INTERACTION_STATE,
26017
+ };
26018
+ }
26019
+ const centerX = avatarBounds.left + avatarBounds.width / 2;
26020
+ const centerY = avatarBounds.top + avatarBounds.height / 2;
26021
+ const normalizedX = (pointerSnapshot.clientX - centerX) / Math.max(avatarBounds.width / 2, 1);
26022
+ const normalizedY = (pointerSnapshot.clientY - centerY) / Math.max(avatarBounds.height / 2, 1);
26023
+ const normalizedLength = Math.hypot(normalizedX, normalizedY) || 1;
26024
+ const clampedLength = Math.min(1, normalizedLength);
26025
+ const targetX = (normalizedX / normalizedLength) * clampedLength;
26026
+ const targetY = (normalizedY / normalizedLength) * clampedLength;
26027
+ const intensity = clamp01$1(clampedLength);
26028
+ return {
26029
+ gazeX: targetX * MAX_GAZE_OFFSET,
26030
+ gazeY: targetY * MAX_GAZE_OFFSET,
26031
+ bodyOffsetX: targetX * MAX_BODY_OFFSET,
26032
+ bodyOffsetY: targetY * MAX_BODY_OFFSET,
26033
+ intensity,
26034
+ isPointerActive: true,
26035
+ pointerType: pointerSnapshot.pointerType,
26036
+ };
26037
+ }
26038
+ /**
26039
+ * Advances the smoothed interaction state toward the latest pointer target.
26040
+ *
26041
+ * @param runtimeState Previous animation-frame state.
26042
+ * @param pointerTarget Latest local pointer target.
26043
+ * @param nowMs Current animation-frame timestamp.
26044
+ * @returns Next runtime state to keep in the animation loop.
26045
+ *
26046
+ * @private utility of the avatar rendering system
26047
+ */
26048
+ function stepAvatarInteractionRuntimeState(runtimeState, pointerTarget, nowMs) {
26049
+ const deltaMs = runtimeState.lastFrameMs === null
26050
+ ? 16
26051
+ : Math.min(MAX_INTERACTION_FRAME_DELTA_MS, Math.max(8, nowMs - runtimeState.lastFrameMs));
26052
+ const smoothingWindowMs = pointerTarget.isPointerActive
26053
+ ? ACTIVE_INTERACTION_SMOOTHING_MS
26054
+ : IDLE_INTERACTION_SMOOTHING_MS;
26055
+ // This exponential interpolation keeps the gaze response smooth regardless of fluctuating frame rates.
26056
+ return {
26057
+ gazeX: interpolateExponentially(runtimeState.gazeX, pointerTarget.gazeX, deltaMs, smoothingWindowMs),
26058
+ gazeY: interpolateExponentially(runtimeState.gazeY, pointerTarget.gazeY, deltaMs, smoothingWindowMs),
26059
+ bodyOffsetX: interpolateExponentially(runtimeState.bodyOffsetX, pointerTarget.bodyOffsetX, deltaMs, smoothingWindowMs * BODY_INTERACTION_SMOOTHING_MULTIPLIER),
26060
+ bodyOffsetY: interpolateExponentially(runtimeState.bodyOffsetY, pointerTarget.bodyOffsetY, deltaMs, smoothingWindowMs * BODY_INTERACTION_SMOOTHING_MULTIPLIER),
26061
+ intensity: interpolateExponentially(runtimeState.intensity, pointerTarget.intensity, deltaMs, smoothingWindowMs),
26062
+ isPointerActive: pointerTarget.isPointerActive,
26063
+ pointerType: pointerTarget.pointerType,
26064
+ lastFrameMs: nowMs,
26065
+ };
26066
+ }
26067
+ /**
26068
+ * Clamps a scalar into the inclusive `[0, 1]` range.
26069
+ *
26070
+ * @param value Arbitrary scalar.
26071
+ * @returns Clamped scalar.
26072
+ *
26073
+ * @private utility of the avatar rendering system
26074
+ */
26075
+ function clamp01$1(value) {
26076
+ return Math.min(1, Math.max(0, value));
26077
+ }
26078
+ /**
26079
+ * Interpolates between two values using frame-rate-independent exponential easing.
26080
+ *
26081
+ * @param currentValue Current smoothed value.
26082
+ * @param targetValue Target value.
26083
+ * @param deltaMs Elapsed milliseconds since the previous frame.
26084
+ * @param smoothingWindowMs Time constant controlling responsiveness.
26085
+ * @returns Smoothed next value.
26086
+ *
26087
+ * @private utility of the avatar rendering system
26088
+ */
26089
+ function interpolateExponentially(currentValue, targetValue, deltaMs, smoothingWindowMs) {
26090
+ const blend = 1 - Math.exp(-deltaMs / Math.max(1, smoothingWindowMs));
26091
+ return currentValue + (targetValue - currentValue) * blend;
26092
+ }
26093
+
26094
+ // Note: [💞] Ignore a discrepancy between file name and entity name
26095
+ /**
26096
+ * Next registration id used by the shared avatar animation scheduler.
26097
+ *
26098
+ * @private utility of the avatar rendering system
26099
+ */
26100
+ let nextAvatarAnimationListenerId = 1;
26101
+ /**
26102
+ * Active avatar animation callbacks keyed by their registration id.
26103
+ *
26104
+ * @private utility of the avatar rendering system
26105
+ */
26106
+ const avatarAnimationListeners = new Map();
26107
+ /**
26108
+ * Active shared animation-frame handle.
26109
+ *
26110
+ * @private utility of the avatar rendering system
26111
+ */
26112
+ let avatarAnimationFrameId = null;
26113
+ /**
26114
+ * Registers one avatar animation callback in the shared animation loop.
26115
+ *
26116
+ * @param avatarAnimationListener Frame callback invoked on every animation frame.
26117
+ * @returns Cleanup function that unregisters the callback.
26118
+ *
26119
+ * @private utility of the avatar rendering system
26120
+ */
26121
+ function retainAvatarAnimationListener(avatarAnimationListener) {
26122
+ if (typeof window === 'undefined') {
26123
+ return () => undefined;
26124
+ }
26125
+ const listenerId = nextAvatarAnimationListenerId++;
26126
+ avatarAnimationListeners.set(listenerId, avatarAnimationListener);
26127
+ ensureAvatarAnimationLoop();
26128
+ return () => {
26129
+ avatarAnimationListeners.delete(listenerId);
26130
+ if (avatarAnimationListeners.size === 0 && avatarAnimationFrameId !== null) {
26131
+ window.cancelAnimationFrame(avatarAnimationFrameId);
26132
+ avatarAnimationFrameId = null;
26133
+ }
26134
+ };
26135
+ }
26136
+ /**
26137
+ * Ensures the shared animation loop is running while at least one avatar listener is active.
26138
+ *
26139
+ * @private utility of the avatar rendering system
26140
+ */
26141
+ function ensureAvatarAnimationLoop() {
26142
+ if (avatarAnimationFrameId !== null || avatarAnimationListeners.size === 0 || typeof window === 'undefined') {
26143
+ return;
26144
+ }
26145
+ const runFrame = (now) => {
26146
+ avatarAnimationFrameId = null;
26147
+ for (const avatarAnimationListener of [...avatarAnimationListeners.values()]) {
26148
+ avatarAnimationListener(now);
26149
+ }
26150
+ ensureAvatarAnimationLoop();
26151
+ };
26152
+ avatarAnimationFrameId = window.requestAnimationFrame(runFrame);
26153
+ }
26154
+
26155
+ // Note: [💞] Ignore a discrepancy between file name and entity name
26156
+ /**
26157
+ * Shared `IntersectionObserver` callbacks grouped by observed element.
26158
+ *
26159
+ * @private utility of the avatar rendering system
26160
+ */
26161
+ const avatarVisibilityListeners = new Map();
26162
+ /**
26163
+ * Lazily created shared `IntersectionObserver` used by avatar canvases.
26164
+ *
26165
+ * @private utility of the avatar rendering system
26166
+ */
26167
+ let avatarVisibilityObserver = null;
26168
+ /**
26169
+ * Observes one avatar element and notifies the caller when it enters or leaves the viewport.
26170
+ *
26171
+ * @param element Observed avatar element.
26172
+ * @param avatarVisibilityListener Listener notified with the current visibility state.
26173
+ * @returns Cleanup function that stops observing the element.
26174
+ *
26175
+ * @private utility of the avatar rendering system
26176
+ */
26177
+ function observeAvatarVisibility(element, avatarVisibilityListener) {
26178
+ if (typeof window === 'undefined' || typeof IntersectionObserver === 'undefined') {
26179
+ avatarVisibilityListener(true);
26180
+ return () => undefined;
26181
+ }
26182
+ const observer = getAvatarVisibilityObserver();
26183
+ const elementListeners = avatarVisibilityListeners.get(element) || new Set();
26184
+ elementListeners.add(avatarVisibilityListener);
26185
+ avatarVisibilityListeners.set(element, elementListeners);
26186
+ observer.observe(element);
26187
+ return () => {
26188
+ const currentElementListeners = avatarVisibilityListeners.get(element);
26189
+ if (!currentElementListeners) {
26190
+ return;
26191
+ }
26192
+ currentElementListeners.delete(avatarVisibilityListener);
26193
+ if (currentElementListeners.size > 0) {
26194
+ return;
26195
+ }
26196
+ avatarVisibilityListeners.delete(element);
26197
+ observer.unobserve(element);
26198
+ };
26199
+ }
26200
+ /**
26201
+ * Creates the shared `IntersectionObserver` used by all avatar canvases.
26202
+ *
26203
+ * @returns Shared observer instance.
26204
+ *
26205
+ * @private utility of the avatar rendering system
26206
+ */
26207
+ function getAvatarVisibilityObserver() {
26208
+ if (avatarVisibilityObserver) {
26209
+ return avatarVisibilityObserver;
26210
+ }
26211
+ avatarVisibilityObserver = new IntersectionObserver((entries) => {
26212
+ for (const entry of entries) {
26213
+ const elementListeners = avatarVisibilityListeners.get(entry.target);
26214
+ if (!elementListeners) {
26215
+ continue;
26216
+ }
26217
+ const isVisible = entry.isIntersecting && entry.intersectionRatio > 0;
26218
+ for (const avatarVisibilityListener of elementListeners) {
26219
+ avatarVisibilityListener(isVisible);
26220
+ }
26221
+ }
26222
+ });
26223
+ return avatarVisibilityObserver;
26224
+ }
26225
+
26226
+ // Note: [💞] Ignore a discrepancy between file name and entity name
26227
+ /**
26228
+ * Active avatar instances currently consuming the shared pointer tracker.
26229
+ *
26230
+ * @private utility of the avatar rendering system
26231
+ */
26232
+ let avatarPointerTrackingConsumerCount = 0;
26233
+ /**
26234
+ * Latest shared viewport pointer sample.
26235
+ *
26236
+ * @private utility of the avatar rendering system
26237
+ */
26238
+ let currentAvatarPointerSnapshot = null;
26239
+ /**
26240
+ * Monotonic version incremented whenever the shared pointer snapshot changes.
26241
+ *
26242
+ * @private utility of the avatar rendering system
26243
+ */
26244
+ let currentAvatarPointerSnapshotVersion = 0;
26245
+ /**
26246
+ * Monotonic version incremented whenever scrolling or resizing may invalidate cached avatar bounds.
26247
+ *
26248
+ * @private utility of the avatar rendering system
26249
+ */
26250
+ let currentAvatarViewportLayoutVersion = 0;
26251
+ /**
26252
+ * Cleanup function for the lazily attached global listeners.
26253
+ *
26254
+ * @private utility of the avatar rendering system
26255
+ */
26256
+ let releaseAvatarPointerTrackingListeners = null;
26257
+ /**
26258
+ * Starts the shared pointer tracker and returns a disposer for the caller.
26259
+ *
26260
+ * @returns Cleanup function that releases one consumer.
26261
+ *
26262
+ * @private utility of the avatar rendering system
26263
+ */
26264
+ function retainAvatarPointerTracking() {
26265
+ avatarPointerTrackingConsumerCount++;
26266
+ if (avatarPointerTrackingConsumerCount === 1) {
26267
+ releaseAvatarPointerTrackingListeners = attachAvatarPointerTrackingListeners();
26268
+ }
26269
+ return () => {
26270
+ avatarPointerTrackingConsumerCount = Math.max(0, avatarPointerTrackingConsumerCount - 1);
26271
+ if (avatarPointerTrackingConsumerCount === 0) {
26272
+ currentAvatarPointerSnapshot = null;
26273
+ releaseAvatarPointerTrackingListeners === null || releaseAvatarPointerTrackingListeners === void 0 ? void 0 : releaseAvatarPointerTrackingListeners();
26274
+ releaseAvatarPointerTrackingListeners = null;
26275
+ }
26276
+ };
26277
+ }
26278
+ /**
26279
+ * Returns the latest shared viewport pointer sample when available.
26280
+ *
26281
+ * @returns Shared pointer snapshot or `null`.
26282
+ *
26283
+ * @private utility of the avatar rendering system
26284
+ */
26285
+ function getAvatarPointerSnapshot() {
26286
+ return currentAvatarPointerSnapshot;
26287
+ }
26288
+ /**
26289
+ * Returns the current pointer snapshot version.
26290
+ *
26291
+ * @returns Monotonic pointer snapshot version.
26292
+ *
26293
+ * @private utility of the avatar rendering system
26294
+ */
26295
+ function getAvatarPointerSnapshotVersion() {
26296
+ return currentAvatarPointerSnapshotVersion;
26297
+ }
26298
+ /**
26299
+ * Returns the current viewport-layout version used to invalidate cached avatar bounds.
26300
+ *
26301
+ * @returns Monotonic viewport-layout version.
26302
+ *
26303
+ * @private utility of the avatar rendering system
26304
+ */
26305
+ function getAvatarViewportLayoutVersion() {
26306
+ return currentAvatarViewportLayoutVersion;
26307
+ }
26308
+ /**
26309
+ * Attaches the global pointer/touch listeners used by all live avatar canvases.
26310
+ *
26311
+ * @returns Cleanup function for the attached listeners.
26312
+ *
26313
+ * @private utility of the avatar rendering system
26314
+ */
26315
+ function attachAvatarPointerTrackingListeners() {
26316
+ if (typeof window === 'undefined') {
26317
+ return () => undefined;
26318
+ }
26319
+ const clearPointerSnapshot = () => {
26320
+ if (currentAvatarPointerSnapshot === null) {
26321
+ return;
26322
+ }
26323
+ currentAvatarPointerSnapshot = null;
26324
+ currentAvatarPointerSnapshotVersion++;
26325
+ };
26326
+ const updatePointerSnapshot = (clientX, clientY, pointerType) => {
26327
+ currentAvatarPointerSnapshot = {
26328
+ clientX,
26329
+ clientY,
26330
+ isPointerActive: true,
26331
+ pointerType,
26332
+ };
26333
+ currentAvatarPointerSnapshotVersion++;
26334
+ };
26335
+ const invalidateViewportLayout = () => {
26336
+ currentAvatarViewportLayoutVersion++;
26337
+ };
26338
+ const handlePointerMove = (event) => {
26339
+ updatePointerSnapshot(event.clientX, event.clientY, normalizeAvatarPointerType(event.pointerType));
26340
+ };
26341
+ const handlePointerDown = (event) => {
26342
+ updatePointerSnapshot(event.clientX, event.clientY, normalizeAvatarPointerType(event.pointerType));
26343
+ };
26344
+ const handlePointerUp = (event) => {
26345
+ if (normalizeAvatarPointerType(event.pointerType) !== 'mouse') {
26346
+ clearPointerSnapshot();
26347
+ }
26348
+ };
26349
+ const handleMouseOut = (event) => {
26350
+ if (!event.relatedTarget) {
26351
+ clearPointerSnapshot();
26352
+ }
26353
+ };
26354
+ const handleTouchEvent = (event) => {
26355
+ const touch = event.touches[0] || event.changedTouches[0];
26356
+ if (!touch) {
26357
+ clearPointerSnapshot();
26358
+ return;
26359
+ }
26360
+ updatePointerSnapshot(touch.clientX, touch.clientY, 'touch');
26361
+ };
26362
+ window.addEventListener('pointermove', handlePointerMove, { passive: true });
26363
+ window.addEventListener('pointerdown', handlePointerDown, { passive: true });
26364
+ window.addEventListener('pointerup', handlePointerUp, { passive: true });
26365
+ window.addEventListener('pointercancel', clearPointerSnapshot, { passive: true });
26366
+ window.addEventListener('mouseout', handleMouseOut, { passive: true });
26367
+ window.addEventListener('blur', clearPointerSnapshot);
26368
+ window.addEventListener('touchstart', handleTouchEvent, { passive: true });
26369
+ window.addEventListener('touchmove', handleTouchEvent, { passive: true });
26370
+ window.addEventListener('touchend', clearPointerSnapshot, { passive: true });
26371
+ window.addEventListener('touchcancel', clearPointerSnapshot, { passive: true });
26372
+ window.addEventListener('scroll', invalidateViewportLayout, { passive: true, capture: true });
26373
+ window.addEventListener('resize', invalidateViewportLayout, { passive: true });
26374
+ return () => {
26375
+ window.removeEventListener('pointermove', handlePointerMove);
26376
+ window.removeEventListener('pointerdown', handlePointerDown);
26377
+ window.removeEventListener('pointerup', handlePointerUp);
26378
+ window.removeEventListener('pointercancel', clearPointerSnapshot);
26379
+ window.removeEventListener('mouseout', handleMouseOut);
26380
+ window.removeEventListener('blur', clearPointerSnapshot);
26381
+ window.removeEventListener('touchstart', handleTouchEvent);
26382
+ window.removeEventListener('touchmove', handleTouchEvent);
26383
+ window.removeEventListener('touchend', clearPointerSnapshot);
26384
+ window.removeEventListener('touchcancel', clearPointerSnapshot);
26385
+ window.removeEventListener('scroll', invalidateViewportLayout, true);
26386
+ window.removeEventListener('resize', invalidateViewportLayout);
26387
+ };
26388
+ }
26389
+ /**
26390
+ * Normalizes browser pointer-type strings into the shared avatar contract.
26391
+ *
26392
+ * @param pointerType Raw browser pointer type.
26393
+ * @returns Shared pointer type.
26394
+ *
26395
+ * @private utility of the avatar rendering system
26396
+ */
26397
+ function normalizeAvatarPointerType(pointerType) {
26398
+ if (pointerType === 'touch' || pointerType === 'pen') {
26399
+ return pointerType;
26400
+ }
26401
+ return 'mouse';
26402
+ }
26403
+
25741
26404
  /* eslint-disable no-magic-numbers */
25742
26405
  /**
25743
26406
  * Builds a smoothly morphing octopus-like silhouette from deterministic parameters.
@@ -25814,8 +26477,9 @@ function traceSmoothClosedPath(context, points) {
25814
26477
  * @private shared geometry helper of `octopus3AvatarVisual` and `asciiOctopusAvatarVisual`
25815
26478
  */
25816
26479
  function createOrganicOctopusTentacleShapes(options) {
25817
- const { size, centerX, centerY, bodyRadius, horizontalStretch, tentacleCount, shapePhase, createRandom, timeMs, saltPrefix, } = options;
26480
+ const { size, centerX, centerY, bodyRadius, horizontalStretch, tentacleCount, shapePhase, createRandom, timeMs, saltPrefix, bodyPoints } = options;
25818
26481
  const baseY = centerY + bodyRadius * 0.74;
26482
+ const lowerBodyAnchorPoints = bodyPoints ? resolveTentacleBodyAnchorPoints(bodyPoints, centerY + bodyRadius * 0.04) : null;
25819
26483
  return Array.from({ length: tentacleCount }, (_, tentacleIndex) => {
25820
26484
  const tentacleRandom = createRandom(`${saltPrefix}-tentacle-${tentacleIndex}`);
25821
26485
  const spreadProgress = tentacleCount === 1 ? 0.5 : tentacleIndex / (tentacleCount - 1);
@@ -25827,10 +26491,21 @@ function createOrganicOctopusTentacleShapes(options) {
25827
26491
  const curlDirection = centeredProgress === 0 ? (tentacleRandom() < 0.5 ? -1 : 1) : Math.sign(centeredProgress);
25828
26492
  const lateralReach = centeredProgress * size * (0.1 + tentacleRandom() * 0.1) + temporalSway;
25829
26493
  const tipReach = curlDirection * size * (0.025 + tentacleRandom() * 0.07);
25830
- const startPoint = {
25831
- x: centerX + centeredProgress * bodyRadius * horizontalStretch * 1.52,
25832
- y: baseY + Math.abs(centeredProgress) * size * 0.012 + tentacleRandom() * size * 0.01,
25833
- };
26494
+ const startYOffset = Math.abs(centeredProgress) * size * 0.012 + tentacleRandom() * size * 0.01;
26495
+ const startPoint = lowerBodyAnchorPoints && lowerBodyAnchorPoints.length >= 2
26496
+ ? createInsetTentacleStartPoint({
26497
+ bodyPoints: lowerBodyAnchorPoints,
26498
+ anchorProgress: spreadProgress,
26499
+ centerX,
26500
+ centerY,
26501
+ bodyRadius,
26502
+ centeredProgress,
26503
+ startYOffset,
26504
+ })
26505
+ : {
26506
+ x: centerX + centeredProgress * bodyRadius * horizontalStretch * 1.52,
26507
+ y: baseY + startYOffset,
26508
+ };
25834
26509
  const controlPointOne = {
25835
26510
  x: startPoint.x + centeredProgress * size * 0.045 + temporalSway * 0.4,
25836
26511
  y: startPoint.y + flowLength * (0.21 + tentacleRandom() * 0.08),
@@ -25860,6 +26535,67 @@ function createOrganicOctopusTentacleShapes(options) {
25860
26535
  };
25861
26536
  });
25862
26537
  }
26538
+ /**
26539
+ * Narrows the body contour to lower anchor points that can safely host tentacle roots.
26540
+ *
26541
+ * @param bodyPoints Generated closed-loop body points.
26542
+ * @param lowerBodyThresholdY Minimum Y coordinate considered part of the lower mantle.
26543
+ * @returns Body points sorted from left to right across the lower silhouette.
26544
+ *
26545
+ * @private shared geometry helper of `octopus3AvatarVisual`
26546
+ */
26547
+ function resolveTentacleBodyAnchorPoints(bodyPoints, lowerBodyThresholdY) {
26548
+ const lowerBodyPoints = bodyPoints.filter((bodyPoint) => bodyPoint.y >= lowerBodyThresholdY).sort((leftPoint, rightPoint) => leftPoint.x - rightPoint.x);
26549
+ if (lowerBodyPoints.length >= 2) {
26550
+ return lowerBodyPoints;
26551
+ }
26552
+ return [...bodyPoints].sort((leftPoint, rightPoint) => leftPoint.x - rightPoint.x);
26553
+ }
26554
+ /**
26555
+ * Resolves one tentacle root from the provided lower body contour and nudges it inside the mantle.
26556
+ *
26557
+ * @param options Tentacle anchor options.
26558
+ * @returns Tentacle start point safely embedded inside the body silhouette.
26559
+ *
26560
+ * @private shared geometry helper of `octopus3AvatarVisual`
26561
+ */
26562
+ function createInsetTentacleStartPoint(options) {
26563
+ const { bodyPoints, anchorProgress, centerX, centerY, bodyRadius, centeredProgress, startYOffset } = options;
26564
+ const clampedAnchorProgress = Math.min(0.94, Math.max(0.06, anchorProgress));
26565
+ const bodyAnchorPoint = interpolatePointAlongTentacleAnchors(bodyPoints, clampedAnchorProgress);
26566
+ const inwardX = centerX - bodyAnchorPoint.x;
26567
+ const inwardY = centerY + bodyRadius * 0.08 - bodyAnchorPoint.y;
26568
+ const inwardLength = Math.hypot(inwardX, inwardY) || 1;
26569
+ const insetDistance = bodyRadius * (0.12 + Math.abs(centeredProgress) * 0.05) + startYOffset * 0.32;
26570
+ return {
26571
+ x: bodyAnchorPoint.x + (inwardX / inwardLength) * insetDistance,
26572
+ y: bodyAnchorPoint.y + (inwardY / inwardLength) * insetDistance,
26573
+ };
26574
+ }
26575
+ /**
26576
+ * Interpolates one left-to-right anchor point along the prepared lower body contour.
26577
+ *
26578
+ * @param bodyPoints Lower body contour points sorted from left to right.
26579
+ * @param progress Interpolation progress in the range `[0, 1]`.
26580
+ * @returns Interpolated anchor point.
26581
+ *
26582
+ * @private shared geometry helper of `octopus3AvatarVisual`
26583
+ */
26584
+ function interpolatePointAlongTentacleAnchors(bodyPoints, progress) {
26585
+ if (bodyPoints.length === 1) {
26586
+ return bodyPoints[0];
26587
+ }
26588
+ const anchorIndex = progress * (bodyPoints.length - 1);
26589
+ const startIndex = Math.floor(anchorIndex);
26590
+ const endIndex = Math.min(bodyPoints.length - 1, startIndex + 1);
26591
+ const blend = anchorIndex - startIndex;
26592
+ const startPoint = bodyPoints[startIndex];
26593
+ const endPoint = bodyPoints[endIndex];
26594
+ return {
26595
+ x: startPoint.x + (endPoint.x - startPoint.x) * blend,
26596
+ y: startPoint.y + (endPoint.y - startPoint.y) * blend,
26597
+ };
26598
+ }
25863
26599
  /**
25864
26600
  * Samples the cubic tentacle centerline and offsets normals to build a filled ribbon.
25865
26601
  *
@@ -25889,6 +26625,26 @@ function sampleOrganicTentacleRibbonPoints(tentacleShape) {
25889
26625
  };
25890
26626
  });
25891
26627
  }
26628
+ /**
26629
+ * Resolves smooth pupil offsets that blend autonomous idle drift with live viewer tracking.
26630
+ *
26631
+ * @param options Eye motion options.
26632
+ * @returns Resolved pupil offsets.
26633
+ *
26634
+ * @private shared geometry helper of octopus avatar visuals
26635
+ */
26636
+ function resolveOrganicEyeMotion(options) {
26637
+ const { radiusX, radiusY, timeMs, phase, interaction, autonomousDriftRatioX = 0.12, autonomousDriftRatioY = 0.08, } = options;
26638
+ const autonomousOffsetX = Math.sin(timeMs / 1280 + phase) * radiusX * autonomousDriftRatioX;
26639
+ const autonomousOffsetY = Math.cos(timeMs / 940 + phase) * radiusY * autonomousDriftRatioY;
26640
+ const interactionBlend = Math.min(1, interaction.intensity * 0.9);
26641
+ return {
26642
+ pupilOffsetX: autonomousOffsetX * (1 - interactionBlend) +
26643
+ interaction.gazeX * radiusX * (0.18 + interactionBlend * 0.18),
26644
+ pupilOffsetY: autonomousOffsetY * (1 - interactionBlend) +
26645
+ interaction.gazeY * radiusY * (0.16 + interactionBlend * 0.16),
26646
+ };
26647
+ }
25892
26648
  /**
25893
26649
  * Samples one point on a cubic Bezier curve.
25894
26650
  *
@@ -25942,13 +26698,14 @@ const ATMOSPHERE_GLYPHS = ['.', ':', '\'', '`'];
25942
26698
  const asciiOctopusAvatarVisual = {
25943
26699
  id: 'ascii-octopus',
25944
26700
  title: 'AsciiOctopus',
25945
- description: 'Morphing alien octopus translated into animated ASCII glyphs with deterministic blob and tentacle geometry.',
26701
+ description: 'Morphing alien octopus translated into animated ASCII glyphs with responsive eyes and seeded geometry.',
25946
26702
  isAnimated: true,
25947
- render({ context, size, palette, createRandom, timeMs }) {
26703
+ supportsPointerTracking: true,
26704
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
25948
26705
  const gridRandom = createRandom('ascii-octopus-grid');
25949
26706
  const staticRandom = createRandom('ascii-octopus-static');
25950
26707
  const gridMetrics = createAsciiGridMetrics(size, gridRandom);
25951
- const layout = createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom);
26708
+ const layout = createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom, interaction);
25952
26709
  drawAvatarFrame(context, size, palette);
25953
26710
  drawAsciiBackdrop(context, size, palette, layout, timeMs);
25954
26711
  context.save();
@@ -26022,7 +26779,8 @@ function drawAsciiBackdrop(context, size, palette, layout, timeMs) {
26022
26779
  */
26023
26780
  function resolveAsciiGlyph(options) {
26024
26781
  const { point, layout, palette, cellWidth, cellHeight, noise, timeMs } = options;
26025
- const eyeGlyphDescriptor = resolveEyeGlyph(point, layout.leftEye, palette, timeMs) || resolveEyeGlyph(point, layout.rightEye, palette, timeMs);
26782
+ const eyeGlyphDescriptor = resolveEyeGlyph(point, layout.leftEye, layout.interaction, palette, timeMs) ||
26783
+ resolveEyeGlyph(point, layout.rightEye, layout.interaction, palette, timeMs);
26026
26784
  if (eyeGlyphDescriptor) {
26027
26785
  return eyeGlyphDescriptor;
26028
26786
  }
@@ -26067,9 +26825,14 @@ function resolveAsciiGlyph(options) {
26067
26825
  *
26068
26826
  * @private helper of `asciiOctopusAvatarVisual`
26069
26827
  */
26070
- function resolveEyeGlyph(point, eyeFeature, palette, timeMs) {
26071
- const pupilOffsetX = Math.sin(timeMs / 1280 + eyeFeature.phase) * eyeFeature.radiusX * 0.12;
26072
- const pupilOffsetY = Math.cos(timeMs / 940 + eyeFeature.phase) * eyeFeature.radiusY * 0.08;
26828
+ function resolveEyeGlyph(point, eyeFeature, interaction, palette, timeMs) {
26829
+ const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
26830
+ radiusX: eyeFeature.radiusX,
26831
+ radiusY: eyeFeature.radiusY,
26832
+ timeMs,
26833
+ phase: eyeFeature.phase,
26834
+ interaction,
26835
+ });
26073
26836
  const scleraDistance = measureRotatedEllipseDistance(point, eyeFeature.centerX, eyeFeature.centerY, eyeFeature.radiusX, eyeFeature.radiusY, eyeFeature.rotation);
26074
26837
  if (scleraDistance > 1.08) {
26075
26838
  return null;
@@ -26243,9 +27006,9 @@ function createAsciiGridMetrics(size, staticRandom) {
26243
27006
  *
26244
27007
  * @private helper of `asciiOctopusAvatarVisual`
26245
27008
  */
26246
- function createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom) {
26247
- const centerX = size * (0.5 + (staticRandom() - 0.5) * 0.02);
26248
- const centerY = size * (0.41 + staticRandom() * 0.05);
27009
+ function createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom, interaction) {
27010
+ const centerX = size * (0.5 + (staticRandom() - 0.5) * 0.02) + interaction.bodyOffsetX * size * 0.05;
27011
+ const centerY = size * (0.41 + staticRandom() * 0.05) + interaction.bodyOffsetY * size * 0.035;
26249
27012
  const bodyRadius = size * (0.195 + staticRandom() * 0.05);
26250
27013
  const horizontalStretch = 1.08 + staticRandom() * 0.22;
26251
27014
  const verticalStretch = 0.88 + staticRandom() * 0.14;
@@ -26285,6 +27048,7 @@ function createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom) {
26285
27048
  createRandom,
26286
27049
  timeMs,
26287
27050
  saltPrefix: 'ascii-octopus',
27051
+ bodyPoints,
26288
27052
  });
26289
27053
  const sampledTentacles = tentacleShapes.map(sampleOrganicTentacleRibbonPoints);
26290
27054
  const leftEye = {
@@ -26305,7 +27069,7 @@ function createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom) {
26305
27069
  };
26306
27070
  const mouthPoints = sampleQuadraticBezierPoints({ x: centerX - size * 0.074, y: centerY + size * 0.092 }, {
26307
27071
  x: centerX,
26308
- y: centerY + size * (0.142 + Math.sin(timeMs / 620 + shapePhase) * 0.016),
27072
+ y: centerY + size * (0.142 + Math.sin(timeMs / 620 + shapePhase) * 0.016) + interaction.gazeY * size * 0.012,
26309
27073
  }, { x: centerX + size * 0.074, y: centerY + size * 0.092 }, 12);
26310
27074
  let leftBound = Number.POSITIVE_INFINITY;
26311
27075
  let rightBound = Number.NEGATIVE_INFINITY;
@@ -26331,6 +27095,7 @@ function createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom) {
26331
27095
  bodyRadius,
26332
27096
  horizontalStretch,
26333
27097
  shapePhase,
27098
+ interaction,
26334
27099
  bodyPoints,
26335
27100
  sampledTentacles,
26336
27101
  leftEye,
@@ -27051,15 +27816,16 @@ function createMinecraftShirtTexture(random, palette) {
27051
27816
  const octopusAvatarVisual = {
27052
27817
  id: 'octopus',
27053
27818
  title: 'Octopus',
27054
- description: 'Playful underwater mascot with animated tentacles, bubbles, and seed-based markings.',
27819
+ description: 'Playful underwater mascot with cursor-following eyes, animated tentacles, bubbles, and seeded markings.',
27055
27820
  isAnimated: true,
27056
- render({ context, size, palette, createRandom, timeMs }) {
27821
+ supportsPointerTracking: true,
27822
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
27057
27823
  const staticRandom = createRandom('octopus-static');
27058
27824
  const bubbleRandom = createRandom('octopus-bubbles');
27059
27825
  const bubbleCount = 8;
27060
27826
  const bubbleRadiusBase = size * 0.02;
27061
- const centerX = size * 0.5;
27062
- const centerY = size * 0.42;
27827
+ const centerX = size * 0.5 + interaction.bodyOffsetX * size * 0.035;
27828
+ const centerY = size * 0.42 + interaction.bodyOffsetY * size * 0.024;
27063
27829
  const headRadius = size * (0.19 + staticRandom() * 0.03);
27064
27830
  const mantleHeight = headRadius * 1.18;
27065
27831
  const tentacleLength = size * (0.18 + staticRandom() * 0.06);
@@ -27139,11 +27905,9 @@ const octopusAvatarVisual = {
27139
27905
  }
27140
27906
  const eyeOffsetX = headRadius * 0.42;
27141
27907
  const eyeY = centerY + headRadius * 0.04;
27142
- const pupilDriftX = Math.sin(timeMs / 850) * headRadius * 0.05;
27143
- const pupilDriftY = Math.cos(timeMs / 930) * headRadius * 0.03;
27144
27908
  const eyeRadius = headRadius * 0.22;
27145
- drawEye(context, centerX - eyeOffsetX, eyeY, eyeRadius, palette, pupilDriftX, pupilDriftY);
27146
- drawEye(context, centerX + eyeOffsetX, eyeY, eyeRadius, palette, pupilDriftX, pupilDriftY);
27909
+ drawEye(context, centerX - eyeOffsetX, eyeY, eyeRadius, palette, timeMs, interaction, 0);
27910
+ drawEye(context, centerX + eyeOffsetX, eyeY, eyeRadius, palette, timeMs, interaction, Math.PI / 5);
27147
27911
  context.beginPath();
27148
27912
  context.arc(centerX - headRadius * 0.28, centerY + headRadius * 0.3, headRadius * 0.12, 0, Math.PI * 2);
27149
27913
  context.arc(centerX + headRadius * 0.28, centerY + headRadius * 0.3, headRadius * 0.12, 0, Math.PI * 2);
@@ -27166,22 +27930,32 @@ const octopusAvatarVisual = {
27166
27930
  * @param centerY Eye center Y coordinate.
27167
27931
  * @param radius Eye radius.
27168
27932
  * @param palette Derived avatar palette.
27169
- * @param pupilDriftX Horizontal pupil drift.
27170
- * @param pupilDriftY Vertical pupil drift.
27933
+ * @param timeMs Current animation time in milliseconds.
27934
+ * @param interaction Smoothed avatar interaction state.
27935
+ * @param phase Seed-based phase offset.
27171
27936
  *
27172
27937
  * @private helper of `octopusAvatarVisual`
27173
27938
  */
27174
- function drawEye(context, centerX, centerY, radius, palette, pupilDriftX, pupilDriftY) {
27939
+ function drawEye(context, centerX, centerY, radius, palette, timeMs, interaction, phase) {
27940
+ const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
27941
+ radiusX: radius,
27942
+ radiusY: radius,
27943
+ timeMs,
27944
+ phase,
27945
+ interaction,
27946
+ autonomousDriftRatioX: 0.05,
27947
+ autonomousDriftRatioY: 0.03,
27948
+ });
27175
27949
  context.beginPath();
27176
27950
  context.arc(centerX, centerY, radius, 0, Math.PI * 2);
27177
27951
  context.fillStyle = '#ffffff';
27178
27952
  context.fill();
27179
27953
  context.beginPath();
27180
- context.arc(centerX + pupilDriftX, centerY + pupilDriftY, radius * 0.45, 0, Math.PI * 2);
27954
+ context.arc(centerX + pupilOffsetX, centerY + pupilOffsetY, radius * 0.45, 0, Math.PI * 2);
27181
27955
  context.fillStyle = palette.ink;
27182
27956
  context.fill();
27183
27957
  context.beginPath();
27184
- context.arc(centerX + pupilDriftX - radius * 0.12, centerY + pupilDriftY - radius * 0.12, radius * 0.15, 0, Math.PI * 2);
27958
+ context.arc(centerX + pupilOffsetX - radius * 0.12, centerY + pupilOffsetY - radius * 0.12, radius * 0.15, 0, Math.PI * 2);
27185
27959
  context.fillStyle = '#ffffff';
27186
27960
  context.fill();
27187
27961
  context.beginPath();
@@ -27200,12 +27974,13 @@ function drawEye(context, centerX, centerY, radius, palette, pupilDriftX, pupilD
27200
27974
  const octopus2AvatarVisual = {
27201
27975
  id: 'octopus2',
27202
27976
  title: 'Octopus2',
27203
- description: 'Organic alien octopus rendered as one continuously morphing blob with luminous eyes and soft inner motion.',
27977
+ description: 'Organic alien octopus rendered as one continuously morphing blob with responsive luminous eyes.',
27204
27978
  isAnimated: true,
27205
- render({ context, size, palette, createRandom, timeMs }) {
27979
+ supportsPointerTracking: true,
27980
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
27206
27981
  const staticRandom = createRandom('octopus2-static');
27207
- const centerX = size * 0.5;
27208
- const centerY = size * (0.48 + staticRandom() * 0.03);
27982
+ const centerX = size * 0.5 + interaction.bodyOffsetX * size * 0.042;
27983
+ const centerY = size * (0.48 + staticRandom() * 0.03) + interaction.bodyOffsetY * size * 0.028;
27209
27984
  const bodyRadius = size * (0.25 + staticRandom() * 0.035);
27210
27985
  const horizontalStretch = 1.04 + staticRandom() * 0.16;
27211
27986
  const verticalStretch = 0.94 + staticRandom() * 0.12;
@@ -27276,11 +28051,11 @@ const octopus2AvatarVisual = {
27276
28051
  const eyeCenterY = centerY - size * 0.02;
27277
28052
  const eyeRadiusX = size * 0.072;
27278
28053
  const eyeRadiusY = size * 0.086;
27279
- drawAlienEye(context, centerX - eyeOffsetX, eyeCenterY, eyeRadiusX, eyeRadiusY, palette, timeMs, shapePhase);
27280
- drawAlienEye(context, centerX + eyeOffsetX, eyeCenterY, eyeRadiusX, eyeRadiusY, palette, timeMs, shapePhase + Math.PI / 5);
28054
+ drawAlienEye(context, centerX - eyeOffsetX, eyeCenterY, eyeRadiusX, eyeRadiusY, palette, timeMs, shapePhase, interaction);
28055
+ drawAlienEye(context, centerX + eyeOffsetX, eyeCenterY, eyeRadiusX, eyeRadiusY, palette, timeMs, shapePhase + Math.PI / 5, interaction);
27281
28056
  context.beginPath();
27282
28057
  context.moveTo(centerX - size * 0.08, centerY + size * 0.12);
27283
- context.quadraticCurveTo(centerX, centerY + size * (0.175 + Math.sin(timeMs / 520 + shapePhase) * 0.012), centerX + size * 0.08, centerY + size * 0.12);
28058
+ context.quadraticCurveTo(centerX, centerY + size * (0.175 + Math.sin(timeMs / 520 + shapePhase) * 0.012) + interaction.gazeY * size * 0.01, centerX + size * 0.08, centerY + size * 0.12);
27284
28059
  context.strokeStyle = `${palette.ink}b3`;
27285
28060
  context.lineWidth = size * 0.013;
27286
28061
  context.lineCap = 'round';
@@ -27358,12 +28133,19 @@ function drawLowerSuckers(context, centerX, centerY, size, palette, createRandom
27358
28133
  * @param palette Derived avatar palette.
27359
28134
  * @param timeMs Current animation time in milliseconds.
27360
28135
  * @param phase Seed-based animation phase.
28136
+ * @param interaction Smoothed avatar interaction state.
27361
28137
  *
27362
28138
  * @private helper of `octopus2AvatarVisual`
27363
28139
  */
27364
- function drawAlienEye(context, centerX, centerY, radiusX, radiusY, palette, timeMs, phase) {
27365
- const pupilOffsetX = Math.sin(timeMs / 1300 + phase) * radiusX * 0.12;
27366
- const pupilOffsetY = Math.cos(timeMs / 970 + phase) * radiusY * 0.1;
28140
+ function drawAlienEye(context, centerX, centerY, radiusX, radiusY, palette, timeMs, phase, interaction) {
28141
+ const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
28142
+ radiusX,
28143
+ radiusY,
28144
+ timeMs,
28145
+ phase,
28146
+ interaction,
28147
+ autonomousDriftRatioY: 0.1,
28148
+ });
27367
28149
  context.save();
27368
28150
  context.beginPath();
27369
28151
  context.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, Math.PI * 2);
@@ -27403,12 +28185,13 @@ function drawAlienEye(context, centerX, centerY, radiusX, radiusY, palette, time
27403
28185
  const octopus3AvatarVisual = {
27404
28186
  id: 'octopus3',
27405
28187
  title: 'Octopus3',
27406
- description: 'Gelatinous alien octopus with a morphing mantle, visible ribbon tentacles, and seeded facial features.',
28188
+ description: 'Gelatinous alien octopus with a morphing mantle, responsive eyes, and visible ribbon tentacles.',
27407
28189
  isAnimated: true,
27408
- render({ context, size, palette, createRandom, timeMs }) {
28190
+ supportsPointerTracking: true,
28191
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
27409
28192
  const staticRandom = createRandom('octopus3-static');
27410
- const centerX = size * (0.5 + (staticRandom() - 0.5) * 0.02);
27411
- const centerY = size * (0.41 + staticRandom() * 0.05);
28193
+ const centerX = size * (0.5 + (staticRandom() - 0.5) * 0.02) + interaction.bodyOffsetX * size * 0.05;
28194
+ const centerY = size * (0.41 + staticRandom() * 0.05) + interaction.bodyOffsetY * size * 0.035;
27412
28195
  const bodyRadius = size * (0.2 + staticRandom() * 0.045);
27413
28196
  const horizontalStretch = 1.08 + staticRandom() * 0.22;
27414
28197
  const verticalStretch = 0.9 + staticRandom() * 0.12;
@@ -27448,6 +28231,7 @@ const octopus3AvatarVisual = {
27448
28231
  createRandom,
27449
28232
  timeMs,
27450
28233
  saltPrefix: 'octopus3',
28234
+ bodyPoints,
27451
28235
  });
27452
28236
  drawAvatarFrame(context, size, palette);
27453
28237
  drawOctopus3Atmosphere(context, size, palette, centerX, centerY, timeMs, shapePhase);
@@ -27493,11 +28277,11 @@ const octopus3AvatarVisual = {
27493
28277
  context.ellipse(centerX, centerY - size * 0.14, size * 0.18, size * 0.062, 0, Math.PI, Math.PI * 2);
27494
28278
  context.fillStyle = `${palette.highlight}3d`;
27495
28279
  context.fill();
27496
- drawSeededEye(context, centerX - eyeSpacing, centerY - size * 0.01, eyeRadiusX, eyeRadiusY, (staticRandom() - 0.5) * 0.28, palette, timeMs, shapePhase);
27497
- drawSeededEye(context, centerX + eyeSpacing, centerY - size * 0.01, eyeRadiusX, eyeRadiusY, (staticRandom() - 0.5) * 0.28, palette, timeMs, shapePhase + Math.PI / 4);
28280
+ drawSeededEye(context, centerX - eyeSpacing, centerY - size * 0.01, eyeRadiusX, eyeRadiusY, (staticRandom() - 0.5) * 0.28, palette, timeMs, shapePhase, interaction);
28281
+ drawSeededEye(context, centerX + eyeSpacing, centerY - size * 0.01, eyeRadiusX, eyeRadiusY, (staticRandom() - 0.5) * 0.28, palette, timeMs, shapePhase + Math.PI / 4, interaction);
27498
28282
  context.beginPath();
27499
28283
  context.moveTo(centerX - size * 0.07, centerY + size * 0.09);
27500
- context.quadraticCurveTo(centerX, centerY + size * (0.14 + Math.sin(timeMs / 620 + shapePhase) * 0.016), centerX + size * 0.07, centerY + size * 0.09);
28284
+ context.quadraticCurveTo(centerX, centerY + size * (0.14 + Math.sin(timeMs / 620 + shapePhase) * 0.016) + interaction.gazeY * size * 0.012, centerX + size * 0.07, centerY + size * 0.09);
27501
28285
  context.strokeStyle = `${palette.ink}b3`;
27502
28286
  context.lineWidth = size * 0.012;
27503
28287
  context.lineCap = 'round';
@@ -27685,12 +28469,18 @@ function drawMantleNodes(context, centerX, centerY, size, palette, createRandom)
27685
28469
  * @param palette Derived avatar palette.
27686
28470
  * @param timeMs Current animation time in milliseconds.
27687
28471
  * @param phase Seed-based animation phase.
28472
+ * @param interaction Smoothed avatar interaction state.
27688
28473
  *
27689
28474
  * @private helper of `octopus3AvatarVisual`
27690
28475
  */
27691
- function drawSeededEye(context, centerX, centerY, radiusX, radiusY, rotation, palette, timeMs, phase) {
27692
- const pupilOffsetX = Math.sin(timeMs / 1280 + phase) * radiusX * 0.12;
27693
- const pupilOffsetY = Math.cos(timeMs / 940 + phase) * radiusY * 0.08;
28476
+ function drawSeededEye(context, centerX, centerY, radiusX, radiusY, rotation, palette, timeMs, phase, interaction) {
28477
+ const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
28478
+ radiusX,
28479
+ radiusY,
28480
+ timeMs,
28481
+ phase,
28482
+ interaction,
28483
+ });
27694
28484
  context.save();
27695
28485
  context.translate(centerX, centerY);
27696
28486
  context.rotate(rotation);
@@ -27726,7 +28516,7 @@ function drawSeededEye(context, centerX, centerY, radiusX, radiusY, rotation, pa
27726
28516
  context.stroke();
27727
28517
  context.beginPath();
27728
28518
  context.moveTo(-radiusX * 0.88, -radiusY * 0.08);
27729
- context.quadraticCurveTo(0, -radiusY * 0.9, radiusX * 0.88, -radiusY * 0.08);
28519
+ context.quadraticCurveTo(0, -radiusY * (0.9 - interaction.gazeY * 0.16 + interaction.intensity * 0.08), radiusX * 0.88, -radiusY * 0.08);
27730
28520
  context.strokeStyle = `${palette.shadow}73`;
27731
28521
  context.lineWidth = radiusX * 0.16;
27732
28522
  context.lineCap = 'round';
@@ -27864,30 +28654,56 @@ function getAvatarVisualById(visualId) {
27864
28654
  return avatarVisual;
27865
28655
  }
27866
28656
 
28657
+ /**
28658
+ * Resolves the stable avatar render inputs reused across multiple frames.
28659
+ *
28660
+ * @param options Avatar identity and visual selection.
28661
+ * @returns Stable render data ready to be used by `renderAvatarVisual`.
28662
+ *
28663
+ * @private shared helper for canvas avatar rendering
28664
+ */
28665
+ function resolveAvatarRenderDefinition(options) {
28666
+ const avatarDefinition = normalizeAvatarDefinition(options.avatarDefinition);
28667
+ const surface = options.surface || 'framed';
28668
+ return {
28669
+ avatarDefinition,
28670
+ avatarVisual: getAvatarVisualById(options.visualId),
28671
+ surface,
28672
+ palette: createAvatarPalette(avatarDefinition, surface),
28673
+ createRandom: createAvatarRandomFactory(avatarDefinition),
28674
+ };
28675
+ }
27867
28676
  /**
27868
28677
  * Renders one deterministic avatar frame into the provided canvas.
27869
28678
  *
27870
28679
  * @param options Rendering options.
28680
+ * @param resolvedAvatarRenderDefinition Optional stable render data reused between frames.
27871
28681
  *
27872
28682
  * @private shared helper for canvas avatar rendering
27873
28683
  */
27874
- function renderAvatarVisual(options) {
27875
- const normalizedAvatarDefinition = normalizeAvatarDefinition(options.avatarDefinition);
27876
- const avatarVisual = getAvatarVisualById(options.visualId);
28684
+ function renderAvatarVisual(options, resolvedAvatarRenderDefinition) {
28685
+ const resolvedRenderDefinition = resolvedAvatarRenderDefinition ||
28686
+ resolveAvatarRenderDefinition({
28687
+ avatarDefinition: options.avatarDefinition,
28688
+ visualId: options.visualId,
28689
+ surface: options.surface,
28690
+ });
27877
28691
  const context = options.canvas.getContext('2d');
27878
28692
  if (!context) {
27879
28693
  throw new Error('2D canvas rendering context is unavailable.');
27880
28694
  }
27881
28695
  prepareAvatarCanvas(options.canvas, context, options.size, options.devicePixelRatio || 1);
27882
- avatarVisual.render({
28696
+ resolvedRenderDefinition.avatarVisual.render({
27883
28697
  canvas: options.canvas,
27884
28698
  context,
27885
28699
  size: options.size,
27886
28700
  devicePixelRatio: options.devicePixelRatio || 1,
27887
28701
  timeMs: options.timeMs,
27888
- avatarDefinition: normalizedAvatarDefinition,
27889
- palette: createAvatarPalette(normalizedAvatarDefinition),
27890
- createRandom: createAvatarRandomFactory(normalizedAvatarDefinition),
28702
+ avatarDefinition: resolvedRenderDefinition.avatarDefinition,
28703
+ palette: resolvedRenderDefinition.palette,
28704
+ createRandom: resolvedRenderDefinition.createRandom,
28705
+ surface: resolvedRenderDefinition.surface,
28706
+ interaction: options.interaction || createIdleAvatarInteractionState(),
27891
28707
  });
27892
28708
  }
27893
28709
 
@@ -27897,48 +28713,136 @@ function renderAvatarVisual(options) {
27897
28713
  * @private helper of `<Avatar/>`
27898
28714
  */
27899
28715
  const AVATAR_CANVAS_RADIUS_RATIO = 0.18;
28716
+ /**
28717
+ * Maximum time between layout-bound refreshes while pointer tracking is active.
28718
+ *
28719
+ * This keeps pointer-aware visuals aligned with chat/layout shifts without forcing a layout read every frame.
28720
+ *
28721
+ * @private helper of `<Avatar/>`
28722
+ */
28723
+ const ACTIVE_POINTER_BOUNDS_REFRESH_MS = 120;
27900
28724
  /**
27901
28725
  * Canvas-based deterministic avatar component.
27902
28726
  *
27903
28727
  * @private shared component for in-repository avatar previews
27904
28728
  */
27905
28729
  function Avatar(props) {
27906
- const { avatarDefinition, visualId, size = DEFAULT_AVATAR_SIZE, title, className, style } = props;
28730
+ const { avatarDefinition, visualId, surface = 'framed', size = DEFAULT_AVATAR_SIZE, title, className, style } = props;
27907
28731
  const canvasRef = useRef(null);
27908
- const normalizedAvatarDefinition = useMemo(() => normalizeAvatarDefinition(avatarDefinition), [avatarDefinition]);
27909
- const avatarVisual = useMemo(() => getAvatarVisualById(visualId), [visualId]);
28732
+ const animationStartRef = useRef(null);
28733
+ const interactionRuntimeStateRef = useRef(createAvatarInteractionRuntimeState());
28734
+ const avatarBoundsRef = useRef(null);
28735
+ const lastResolvedPointerVersionRef = useRef(-1);
28736
+ const lastResolvedViewportLayoutVersionRef = useRef(-1);
28737
+ const lastAvatarBoundsRefreshAtRef = useRef(0);
28738
+ const avatarColorsKey = avatarDefinition.colors.join('|');
28739
+ const resolvedAvatarRenderDefinition = useMemo(() => resolveAvatarRenderDefinition({
28740
+ avatarDefinition,
28741
+ visualId,
28742
+ surface,
28743
+ }), [avatarDefinition.agentHash, avatarDefinition.agentName, avatarColorsKey, surface, visualId]);
28744
+ const avatarDefinitionKey = useMemo(() => createAvatarDefinitionKey(resolvedAvatarRenderDefinition.avatarDefinition), [
28745
+ resolvedAvatarRenderDefinition.avatarDefinition.agentHash,
28746
+ resolvedAvatarRenderDefinition.avatarDefinition.agentName,
28747
+ resolvedAvatarRenderDefinition.avatarDefinition.colors.join('|'),
28748
+ ]);
28749
+ const [isVisible, setIsVisible] = useState(true);
27910
28750
  useEffect(() => {
27911
28751
  const canvas = canvasRef.current;
27912
28752
  if (!canvas) {
27913
28753
  throw new Error('Avatar canvas is not mounted.');
27914
28754
  }
27915
- let animationFrameId = null;
27916
- const animationStart = performance.now();
28755
+ return observeAvatarVisibility(canvas, setIsVisible);
28756
+ }, []);
28757
+ useEffect(() => {
28758
+ interactionRuntimeStateRef.current = createAvatarInteractionRuntimeState();
28759
+ avatarBoundsRef.current = null;
28760
+ lastResolvedPointerVersionRef.current = -1;
28761
+ lastResolvedViewportLayoutVersionRef.current = -1;
28762
+ lastAvatarBoundsRefreshAtRef.current = 0;
28763
+ }, [avatarDefinitionKey, isVisible, visualId]);
28764
+ useEffect(() => {
28765
+ const canvas = canvasRef.current;
28766
+ if (!canvas || typeof ResizeObserver === 'undefined') {
28767
+ return;
28768
+ }
28769
+ const resizeObserver = new ResizeObserver(() => {
28770
+ avatarBoundsRef.current = null;
28771
+ });
28772
+ resizeObserver.observe(canvas);
28773
+ return () => {
28774
+ resizeObserver.disconnect();
28775
+ };
28776
+ }, []);
28777
+ useEffect(() => {
28778
+ const canvas = canvasRef.current;
28779
+ if (!canvas) {
28780
+ throw new Error('Avatar canvas is not mounted.');
28781
+ }
28782
+ const avatarVisual = resolvedAvatarRenderDefinition.avatarVisual;
28783
+ const isDynamicAvatar = avatarVisual.isAnimated || avatarVisual.supportsPointerTracking === true;
28784
+ const shouldTrackPointer = avatarVisual.supportsPointerTracking === true && isVisible;
28785
+ const releasePointerTracking = shouldTrackPointer ? retainAvatarPointerTracking() : null;
28786
+ if (animationStartRef.current === null) {
28787
+ animationStartRef.current = performance.now();
28788
+ }
27917
28789
  const renderFrame = (now) => {
28790
+ const pointerSnapshot = avatarVisual.supportsPointerTracking ? getAvatarPointerSnapshot() : null;
28791
+ let interactionState = createIdleAvatarInteractionState();
28792
+ if (avatarVisual.supportsPointerTracking && pointerSnapshot) {
28793
+ const pointerSnapshotVersion = getAvatarPointerSnapshotVersion();
28794
+ const viewportLayoutVersion = getAvatarViewportLayoutVersion();
28795
+ if (avatarBoundsRef.current === null ||
28796
+ lastResolvedPointerVersionRef.current !== pointerSnapshotVersion ||
28797
+ lastResolvedViewportLayoutVersionRef.current !== viewportLayoutVersion ||
28798
+ now - lastAvatarBoundsRefreshAtRef.current >= ACTIVE_POINTER_BOUNDS_REFRESH_MS) {
28799
+ avatarBoundsRef.current = canvas.getBoundingClientRect();
28800
+ lastResolvedPointerVersionRef.current = pointerSnapshotVersion;
28801
+ lastResolvedViewportLayoutVersionRef.current = viewportLayoutVersion;
28802
+ lastAvatarBoundsRefreshAtRef.current = now;
28803
+ }
28804
+ interactionRuntimeStateRef.current = stepAvatarInteractionRuntimeState(interactionRuntimeStateRef.current, resolveAvatarPointerTarget(avatarBoundsRef.current, pointerSnapshot), now);
28805
+ interactionState = interactionRuntimeStateRef.current;
28806
+ }
28807
+ else if (avatarVisual.supportsPointerTracking) {
28808
+ interactionRuntimeStateRef.current = stepAvatarInteractionRuntimeState(interactionRuntimeStateRef.current, createIdleAvatarInteractionState(), now);
28809
+ interactionState = interactionRuntimeStateRef.current;
28810
+ }
27918
28811
  renderAvatarVisual({
27919
28812
  canvas,
27920
- avatarDefinition: normalizedAvatarDefinition,
28813
+ avatarDefinition: resolvedAvatarRenderDefinition.avatarDefinition,
27921
28814
  visualId,
28815
+ surface,
27922
28816
  size,
27923
- timeMs: now - animationStart,
28817
+ timeMs: now - animationStartRef.current,
27924
28818
  devicePixelRatio: window.devicePixelRatio || 1,
27925
- });
27926
- if (avatarVisual.isAnimated) {
27927
- animationFrameId = window.requestAnimationFrame(renderFrame);
27928
- }
28819
+ interaction: interactionState,
28820
+ }, resolvedAvatarRenderDefinition);
27929
28821
  };
27930
- renderFrame(animationStart);
28822
+ renderFrame(performance.now());
28823
+ if (!isDynamicAvatar || !isVisible) {
28824
+ return () => {
28825
+ releasePointerTracking === null || releasePointerTracking === void 0 ? void 0 : releasePointerTracking();
28826
+ };
28827
+ }
28828
+ const releaseAnimationListener = retainAvatarAnimationListener(renderFrame);
27931
28829
  return () => {
27932
- if (animationFrameId !== null) {
27933
- window.cancelAnimationFrame(animationFrameId);
27934
- }
28830
+ releaseAnimationListener();
28831
+ releasePointerTracking === null || releasePointerTracking === void 0 ? void 0 : releasePointerTracking();
27935
28832
  };
27936
- }, [avatarVisual.isAnimated, normalizedAvatarDefinition, size, visualId]);
27937
- return (jsx("canvas", { ref: canvasRef, title: title || `${normalizedAvatarDefinition.agentName} avatar`, className: className, style: {
28833
+ }, [
28834
+ avatarDefinitionKey,
28835
+ isVisible,
28836
+ resolvedAvatarRenderDefinition,
28837
+ size,
28838
+ surface,
28839
+ visualId,
28840
+ ]);
28841
+ return (jsx("canvas", { ref: canvasRef, title: title || `${resolvedAvatarRenderDefinition.avatarDefinition.agentName} avatar`, className: className, style: {
27938
28842
  width: size,
27939
28843
  height: size,
27940
28844
  display: 'block',
27941
- borderRadius: size * AVATAR_CANVAS_RADIUS_RATIO,
28845
+ borderRadius: surface === 'transparent' ? 0 : size * AVATAR_CANVAS_RADIUS_RATIO,
27942
28846
  ...style,
27943
28847
  } }));
27944
28848
  }
@@ -27949,9 +28853,9 @@ function Avatar(props) {
27949
28853
  * @private shared component for avatar media rendering
27950
28854
  */
27951
28855
  function AvatarOrImage(props) {
27952
- const { imageUrl, avatarDefinition, visualId, size, alt, className, style } = props;
28856
+ const { imageUrl, avatarDefinition, visualId, surface, size, alt, className, style } = props;
27953
28857
  if (avatarDefinition && visualId) {
27954
- return (jsx(Avatar, { avatarDefinition: avatarDefinition, visualId: visualId, size: size, title: alt, className: className, style: style }));
28858
+ return (jsx(Avatar, { avatarDefinition: avatarDefinition, visualId: visualId, surface: surface, size: size, title: alt, className: className, style: style }));
27955
28859
  }
27956
28860
  if (!imageUrl) {
27957
28861
  return null;
@@ -38559,6 +39463,14 @@ class OpenAiVectorStoreHandler extends OpenAiExecutionTools {
38559
39463
  * Constant for default agent kit model name.
38560
39464
  */
38561
39465
  const DEFAULT_AGENT_KIT_MODEL_NAME = 'gpt-5.4-mini';
39466
+ /**
39467
+ * Default model used for nested DeepSearch tool invocations.
39468
+ */
39469
+ const DEFAULT_DEEP_SEARCH_MODEL_NAME = 'o4-mini-deep-research';
39470
+ /**
39471
+ * Tool name used by the Book commitment-backed DeepSearch capability.
39472
+ */
39473
+ const DEEP_SEARCH_TOOL_NAME = 'deep_search';
38562
39474
  /**
38563
39475
  * Creates one structured log entry for streamed tool-call updates.
38564
39476
  *
@@ -38601,6 +39513,98 @@ function resolveFinalToolCallState$1(options) {
38601
39513
  }
38602
39514
  return 'COMPLETE';
38603
39515
  }
39516
+ /**
39517
+ * Returns true when one tool definition represents the dedicated DeepSearch capability.
39518
+ *
39519
+ * @param toolDefinition - Tool definition from compiled model requirements.
39520
+ * @returns `true` when the tool should be backed by a nested deep-research agent.
39521
+ *
39522
+ * @private helper of `OpenAiAgentKitExecutionTools`
39523
+ */
39524
+ function isDeepSearchToolDefinition(toolDefinition) {
39525
+ return toolDefinition.name === DEEP_SEARCH_TOOL_NAME;
39526
+ }
39527
+ /**
39528
+ * Normalizes Promptbook JSON-schema tool parameters for AgentKit function tools.
39529
+ *
39530
+ * @param parameters - Promptbook tool parameters.
39531
+ * @returns AgentKit-compatible JSON schema or `undefined`.
39532
+ *
39533
+ * @private helper of `OpenAiAgentKitExecutionTools`
39534
+ */
39535
+ function normalizeAgentKitToolParameters(parameters) {
39536
+ var _a, _b;
39537
+ if (!parameters) {
39538
+ return undefined;
39539
+ }
39540
+ return {
39541
+ ...parameters,
39542
+ additionalProperties: (_a = parameters.additionalProperties) !== null && _a !== void 0 ? _a : false,
39543
+ required: (_b = parameters.required) !== null && _b !== void 0 ? _b : [],
39544
+ };
39545
+ }
39546
+ /**
39547
+ * Creates instructions for the nested DeepSearch specialist agent.
39548
+ *
39549
+ * @param toolDescription - Model-facing description from the original tool definition.
39550
+ * @returns System instructions for the nested deep-research agent.
39551
+ *
39552
+ * @private helper of `OpenAiAgentKitExecutionTools`
39553
+ */
39554
+ function createDeepSearchAgentInstructions(toolDescription) {
39555
+ const normalizedDescription = toolDescription.trim();
39556
+ return spaceTrim$1((block) => `
39557
+ You are a DeepSearch specialist working as a tool for another agent.
39558
+ Perform thorough, source-grounded public-web research based on the provided request.
39559
+ Use web search to gather current information, compare relevant viewpoints, and synthesize a concise research brief.
39560
+ Do not ask follow-up questions. If the request is not specific enough, state the assumptions you had to make.
39561
+ Include citations in the research brief whenever sources were used.
39562
+ ${block(normalizedDescription ? `Tool guidance:\n${normalizedDescription}` : '')}
39563
+ `);
39564
+ }
39565
+ /**
39566
+ * Builds the nested DeepSearch prompt from structured tool arguments.
39567
+ *
39568
+ * @param rawInput - Parsed function-tool arguments provided by the outer agent.
39569
+ * @returns Prompt text passed to the nested deep-research agent.
39570
+ *
39571
+ * @private helper of `OpenAiAgentKitExecutionTools`
39572
+ */
39573
+ function buildDeepSearchToolInput(rawInput) {
39574
+ const input = rawInput && typeof rawInput === 'object' ? rawInput : {};
39575
+ const query = typeof input.query === 'string' ? input.query.trim() : '';
39576
+ const additionalHints = Object.entries(input)
39577
+ .filter(([key, value]) => key !== 'query' && value !== undefined && value !== null && String(value).trim() !== '')
39578
+ .map(([key, value]) => `- ${key}: ${typeof value === 'string' ? value : JSON.stringify(value)}`);
39579
+ return spaceTrim$1((block) => `
39580
+ Research request:
39581
+ ${query || JSON.stringify(input)}
39582
+ ${block(additionalHints.length > 0 ? `Execution hints:\n${additionalHints.join('\n')}` : '')}
39583
+ `);
39584
+ }
39585
+ /**
39586
+ * Creates the native Agent SDK tool used for `USE DEEPSEARCH`.
39587
+ *
39588
+ * @param toolDefinition - Promptbook tool definition for `deep_search`.
39589
+ * @returns AgentKit tool backed by a nested deep-research agent.
39590
+ *
39591
+ * @private helper of `OpenAiAgentKitExecutionTools`
39592
+ */
39593
+ function createDeepSearchAgentKitTool(toolDefinition) {
39594
+ const deepSearchAgent = new Agent$1({
39595
+ name: 'DeepSearch',
39596
+ model: DEFAULT_DEEP_SEARCH_MODEL_NAME,
39597
+ instructions: createDeepSearchAgentInstructions(toolDefinition.description),
39598
+ tools: [webSearchTool({ searchContextSize: 'high' })],
39599
+ });
39600
+ return deepSearchAgent.asTool({
39601
+ toolName: toolDefinition.name,
39602
+ toolDescription: toolDefinition.description,
39603
+ parameters: normalizeAgentKitToolParameters(toolDefinition.parameters),
39604
+ inputBuilder: ({ params }) => buildDeepSearchToolInput(params),
39605
+ customOutputExtractor: (result) => { var _a; return typeof result.finalOutput === 'string' ? result.finalOutput : JSON.stringify((_a = result.finalOutput) !== null && _a !== void 0 ? _a : ''); },
39606
+ });
39607
+ }
38604
39608
  /**
38605
39609
  * Constant for default JSON schema name.
38606
39610
  */
@@ -38941,25 +39945,23 @@ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
38941
39945
  * Builds the tool list for AgentKit, including hosted file search when applicable.
38942
39946
  */
38943
39947
  buildAgentKitTools(options) {
38944
- var _a;
38945
39948
  const { tools, vectorStoreId } = options;
38946
39949
  const agentKitTools = [];
38947
39950
  if (vectorStoreId) {
38948
39951
  agentKitTools.push(fileSearchTool(vectorStoreId));
38949
39952
  }
38950
39953
  if (tools && tools.length > 0) {
38951
- const scriptTools = this.resolveScriptTools();
39954
+ let scriptTools = null;
38952
39955
  for (const toolDefinition of tools) {
39956
+ if (isDeepSearchToolDefinition(toolDefinition)) {
39957
+ agentKitTools.push(createDeepSearchAgentKitTool(toolDefinition));
39958
+ continue;
39959
+ }
39960
+ scriptTools !== null && scriptTools !== void 0 ? scriptTools : (scriptTools = this.resolveScriptTools());
38953
39961
  agentKitTools.push(tool({
38954
39962
  name: toolDefinition.name,
38955
39963
  description: toolDefinition.description,
38956
- parameters: toolDefinition.parameters
38957
- ? {
38958
- ...toolDefinition.parameters,
38959
- additionalProperties: false,
38960
- required: (_a = toolDefinition.parameters.required) !== null && _a !== void 0 ? _a : [],
38961
- }
38962
- : undefined,
39964
+ parameters: normalizeAgentKitToolParameters(toolDefinition.parameters),
38963
39965
  strict: false,
38964
39966
  execute: async (input, runContext, details) => {
38965
39967
  var _a, _b, _c, _d;
@@ -45146,6 +46148,7 @@ const TOOL_TITLES = {
45146
46148
  delete_wallet_record: { title: 'Deleting wallet', emoji: '👛' },
45147
46149
  request_wallet_record: { title: 'Requesting wallet', emoji: '👛' },
45148
46150
  web_search: { title: 'Searching the web', emoji: '🔎' },
46151
+ deep_search: { title: 'Deep research', emoji: '🔬' },
45149
46152
  useSearchEngine: { title: 'Searching the web', emoji: '🔎' },
45150
46153
  search: { title: 'Searching the web', emoji: '🔎' },
45151
46154
  useBrowser: { title: 'Browsing the web', emoji: '🌐' },
@@ -48107,7 +49110,7 @@ function renderGenericToolCallDetails(options) {
48107
49110
  * @private function of ChatToolCallModal
48108
49111
  */
48109
49112
  function isSearchToolCallName(toolName) {
48110
- return toolName === 'web_search' || toolName === 'useSearchEngine' || toolName === 'search';
49113
+ return toolName === 'web_search' || toolName === 'deep_search' || toolName === 'useSearchEngine' || toolName === 'search';
48111
49114
  }
48112
49115
  /**
48113
49116
  * Checks whether a tool name should use the time renderer.
@@ -48843,7 +49846,12 @@ function TeamToolCallModalContent(options) {
48843
49846
  avatarSrc: resolvedTeammateAvatar || undefined,
48844
49847
  },
48845
49848
  ];
48846
- return (jsxs(Fragment, { children: [jsx("div", { className: classNames(styles$5.searchModalHeader, styles$5.teamModalHeader), children: jsxs("div", { className: styles$5.teamHeaderParticipants, children: [jsx(TeamHeaderProfile, { label: resolvedAgentLabel, avatarSrc: resolvedAgentAvatar, avatarDefinition: resolvedAgentAvatarDefinition, avatarVisualId: resolvedAgentAvatarVisualId, fallbackColor: resolvedAgentHeaderColor }), jsx("span", { className: styles$5.teamHeaderDivider, children: "talking with" }), jsx(TeamHeaderProfile, { label: resolvedTeammateLabel, avatarSrc: resolvedTeammateAvatar, fallbackColor: "#0ea5e9", href: teammateLink })] }) }), jsxs("div", { className: styles$5.searchModalContent, children: [messages.length > 0 ? (jsx("div", { className: styles$5.teamChatContainer, children: jsx(MockedChat, { title: `Chat between ${resolvedAgentLabel} and ${resolvedTeammateLabel}`, messages: messages, participants: participants, isResettable: false, isPausable: false, isSaveButtonEnabled: false, isCopyButtonEnabled: false, visual: "STANDALONE", delayConfig: FAST_FLOW }) })) : (jsx("div", { className: styles$5.noResults, children: "No teammate conversation available." })), (hasTeamToolCalls || hasTeamCitations) && (jsxs("div", { className: styles$5.teamToolCallSection, children: [hasTeamToolCalls && (jsxs("div", { className: styles$5.teamToolCallGroup, children: [jsx("div", { className: styles$5.teamToolCallHeading, children: "Actions" }), jsx("div", { className: styles$5.teamToolCallChips, children: teamToolCalls.map((toolCallEntry, index) => {
49849
+ return (jsxs(Fragment, { children: [jsx("div", { className: classNames(styles$5.searchModalHeader, styles$5.teamModalHeader), children: jsxs("div", { className: styles$5.teamHeaderParticipants, children: [jsx(TeamHeaderProfile, { label: resolvedAgentLabel, avatarSrc: resolvedAgentAvatar, avatarDefinition: resolvedAgentAvatarDefinition, avatarVisualId: resolvedAgentAvatarVisualId, fallbackColor: resolvedAgentHeaderColor }), jsx("span", { className: styles$5.teamHeaderDivider, children: "talking with" }), jsx(TeamHeaderProfile, { label: resolvedTeammateLabel, avatarSrc: resolvedTeammateAvatar, fallbackColor: "#0ea5e9", href: teammateLink })] }) }), jsxs("div", { className: styles$5.searchModalContent, children: [messages.length > 0 ? (jsx("div", { className: styles$5.teamChatContainer, children: jsx(MockedChat, { title: `Chat between ${resolvedAgentLabel} and ${resolvedTeammateLabel}`, messages: messages, participants: participants, isResettable: false, isPausable: false, isSaveButtonEnabled: false, isCopyButtonEnabled: false, layout: "STANDALONE", delayConfig: {
49850
+ // Note+TODO: For some strange reason, <MockedChat/> is not running and stays static on the initial frame, so doing this hack to force it to show the entire chat at once. Need to investigate why the animation is not running as expected and then just use `delayConfig={FAST_FLOW}`
49851
+ ...FAST_FLOW,
49852
+ beforeFirstMessage: 0,
49853
+ showIntermediateMessages: messages.length,
49854
+ }, visualMode: "BUBBLE_MODE" }) })) : (jsx("div", { className: styles$5.noResults, children: "No teammate conversation available." })), (hasTeamToolCalls || hasTeamCitations) && (jsxs("div", { className: styles$5.teamToolCallSection, children: [hasTeamToolCalls && (jsxs("div", { className: styles$5.teamToolCallGroup, children: [jsx("div", { className: styles$5.teamToolCallHeading, children: "Actions" }), jsx("div", { className: styles$5.teamToolCallChips, children: teamToolCalls.map((toolCallEntry, index) => {
48847
49855
  const chipletInfo = getToolCallChipletInfo(toolCallEntry.toolCall, undefined, toolTitles);
48848
49856
  const chipletText = buildToolCallChipText(chipletInfo);
48849
49857
  return (jsxs("button", { className: styles$5.completedToolCall, onClick: () => {
@@ -49782,7 +50790,7 @@ function hasChatActions(postprocessedMessages, { onReset, newChatButtonHref, onU
49782
50790
  * @public exported from `@promptbook/components`
49783
50791
  */
49784
50792
  function Chat(props) {
49785
- const { title = 'Chat', messages, onChange, onMessage, onActionButton, onQuickMessageButton, onReplyToMessage, onCancelReply, onReset, resetRequiresConfirmation = true, newChatButtonHref, onFeedback, feedbackMode = 'stars', feedbackTranslations, timingTranslations, onFileUpload, chatLocale, speechRecognition, placeholderMessageContent, defaultMessage, enterBehavior, resolveEnterBehavior, children, className, style, isAiTextHumanizedAndPromptbookified = true, isVoiceCalling = false, isFocusedOnLoad, participants = [], canReplyToMessage, replyingToMessage, extraActions, actionsContainer, saveFormats, isSaveButtonEnabled = true, isCopyButtonEnabled = true, buttonColor: buttonColorRaw, onUseTemplate, onCreateAgent, toolTitles, teammates, teamAgentProfiles, visual, visualMode = 'ARTICLE_MODE', effectConfigs, soundSystem, speechRecognitionLanguage, isSpeechPlaybackEnabled = true, elevenLabsVoiceId, chatUiTranslations, } = props;
50793
+ const { title = 'Chat', messages, onChange, onMessage, onActionButton, onQuickMessageButton, onReplyToMessage, onCancelReply, onReset, resetRequiresConfirmation = true, newChatButtonHref, onFeedback, feedbackMode = 'stars', feedbackTranslations, timingTranslations, onFileUpload, chatLocale, speechRecognition, placeholderMessageContent, defaultMessage, enterBehavior, resolveEnterBehavior, children, className, style, isAiTextHumanizedAndPromptbookified = true, isVoiceCalling = false, isFocusedOnLoad, participants = [], canReplyToMessage, replyingToMessage, extraActions, actionsContainer, saveFormats, isSaveButtonEnabled = true, isCopyButtonEnabled = true, buttonColor: buttonColorRaw, onUseTemplate, onCreateAgent, toolTitles, teammates, teamAgentProfiles, layout, visualMode = 'ARTICLE_MODE', effectConfigs, soundSystem, speechRecognitionLanguage, isSpeechPlaybackEnabled = true, elevenLabsVoiceId, chatUiTranslations, } = props;
49786
50794
  const buttonColor = useMemo(() => Color.from(buttonColorRaw || '#0066cc'), [buttonColorRaw]);
49787
50795
  const agentParticipant = useMemo(() => participants.find((participant) => participant.name === 'AGENT'), [participants]);
49788
50796
  const postprocessedMessages = useChatPostprocessedMessages({
@@ -49823,11 +50831,11 @@ function Chat(props) {
49823
50831
  extraActions,
49824
50832
  isSaveButtonEnabled,
49825
50833
  });
49826
- const isConstrainedArticleMode = visualMode === 'ARTICLE_MODE' && visual === 'FULL_PAGE';
50834
+ const isConstrainedArticleMode = visualMode === 'ARTICLE_MODE' && layout === 'FULL_PAGE';
49827
50835
  useChatCompleteNotification(messages, soundSystem);
49828
50836
  return (jsxs(Fragment, { children: [feedbackStatus && (jsx("div", { className: classNames(styles$5.feedbackStatus, feedbackStatus.variant === 'success'
49829
50837
  ? styles$5.feedbackStatusSuccess
49830
- : styles$5.feedbackStatusError), "aria-live": "polite", role: "status", children: feedbackStatus.message })), effectConfigs && effectConfigs.length > 0 && (jsx(ChatEffectsSystem, { messages: postprocessedMessages, effectConfigs: effectConfigs, soundSystem: soundSystem })), jsx("div", { className: classNames(className, styles$5.Chat, visual === 'STANDALONE' && styles$5.standaloneVisual, visual === 'FULL_PAGE' && styles$5.fullPageVisual, isConstrainedArticleMode && styles$5.constrainedArticleVisual, getChatCssClassName('Chat'), chatCssClassNames.chat), style, children: jsxs("div", { className: classNames(className, styles$5.chatMainFlow, getChatCssClassName('chatMainFlow'), chatCssClassNames.chatMainFlow), children: [children && jsx("div", { className: classNames(styles$5.chatChildren), children: children }), shouldShowScrollToBottom && (jsx("div", { className: styles$5.scrollToBottomContainer, children: jsxs("div", { className: styles$5.scrollToBottomWrapper, children: [jsx(SolidArrowButton, { "data-button-type": "custom", direction: "down", iconSize: 33, className: classNames(styles$5.scrollToBottom, scrollToBottomCssClassName), onClick: handleButtonClick(() => scrollToBottom()), "aria-label": ariaLabel, title: ariaLabel }), badgeLabel && (jsx("span", { className: styles$5.scrollToBottomBadge, "aria-live": "polite", role: "status", children: badgeLabel }))] }) })), isVoiceCalling && (jsx("div", { className: styles$5.voiceCallIndicatorBar, children: jsxs("div", { className: styles$5.voiceCallIndicator, children: [jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: jsx("path", { d: "M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z" }) }), jsx("span", { children: "Voice call active" }), jsx("div", { className: styles$5.voiceCallPulse })] }) })), jsx(ChatActionsBar, { actionsRef: actionsRef, actionsContainer: actionsContainer, messages: postprocessedMessages, participants: participants, title: title, onReset: onReset, resetRequiresConfirmation: resetRequiresConfirmation, newChatButtonHref: newChatButtonHref, onUseTemplate: onUseTemplate, extraActions: extraActions, saveFormats: saveFormats, isSaveButtonEnabled: isSaveButtonEnabled, shouldFadeActions: shouldFadeActions, shouldDisableActions: shouldDisableActions, chatUiTranslations: chatUiTranslations, onButtonClick: handleButtonClick }), jsx(ChatMessageList, { messages: postprocessedMessages, participants: participants, expandedMessageId: expandedMessageId, messageRatings: messageRatings, setExpandedMessageId: setExpandedMessageId, handleRating: handleRating, mode: mode, isCopyButtonEnabled: isCopyButtonEnabled, isFeedbackEnabled: isFeedbackEnabled, feedbackMode: feedbackMode, feedbackTranslations: feedbackTranslations, timingTranslations: timingTranslations, chatLocale: chatLocale, onCopy: handleCopy, onMessage: onMessage, onActionButton: onActionButton, onQuickMessageButton: onQuickMessageButton, onReplyToMessage: onReplyToMessage, canReplyToMessage: canReplyToMessage, onCreateAgent: onCreateAgent, toolTitles: toolTitles, teammates: teammates, teamAgentProfiles: teamAgentProfiles, visualMode: visualMode, soundSystem: soundSystem, onToolCallClick: openToolCall, onCitationClick: openCitation, setChatMessagesElement: setChatMessagesElement, onScroll: handleChatScroll, isSpeechPlaybackEnabled: isSpeechPlaybackEnabled, elevenLabsVoiceId: elevenLabsVoiceId, chatUiTranslations: chatUiTranslations, chatMessagesClassName: classNames(isConstrainedArticleMode && styles$5.articleModeChatMessages, getChatCssClassName('chatMessages'), chatCssClassNames.chatMessages), hasActions: hasActions }), onMessage && (jsx(ChatInputArea, { onMessage: onMessage, onChange: onChange, onFileUpload: onFileUpload, speechRecognition: speechRecognition, speechRecognitionLanguage: speechRecognitionLanguage, replyingToMessage: replyingToMessage, onCancelReply: onCancelReply, defaultMessage: defaultMessage, enterBehavior: enterBehavior, resolveEnterBehavior: resolveEnterBehavior, placeholderMessageContent: placeholderMessageContent || (chatUiTranslations === null || chatUiTranslations === void 0 ? void 0 : chatUiTranslations.inputPlaceholder), isFocusedOnLoad: isFocusedOnLoad, isMobile: isMobile, isVoiceCalling: isVoiceCalling, participants: participants, buttonColor: buttonColor, soundSystem: soundSystem, onButtonClick: handleButtonClick, chatUiTranslations: chatUiTranslations, chatInputClassName: classNames(isConstrainedArticleMode && styles$5.articleModeChatInput, getChatCssClassName('chatInput'), chatCssClassNames.chatInput) }))] }) }), jsx(ChatToolCallModal, { isOpen: toolCallModalOpen, toolCall: selectedToolCall, toolCallIdentity: selectedToolCallIdentity, onClose: closeToolCallModal, toolTitles: toolTitles, agentParticipant: agentParticipant, buttonColor: buttonColor, teamAgentProfiles: teamAgentProfiles, chatUiTranslations: chatUiTranslations, locale: chatLocale, availableTools: selectedMessageAvailableTools }), jsx(ChatCitationModal, { isOpen: citationModalOpen, citation: selectedCitation, participants: participants, soundSystem: soundSystem, onClose: closeCitationModal }), jsx(ChatRatingModal, { isOpen: ratingModalOpen, selectedMessage: selectedMessage, postprocessedMessages: postprocessedMessages, messages: messages, hoveredRating: hoveredRating, messageRatings: messageRatings, textRating: textRating, feedbackMode: feedbackMode, feedbackTranslations: feedbackTranslations, mode: mode, isMobile: isMobile, onClose: () => setRatingModalOpen(false), setHoveredRating: setHoveredRating, setMessageRatings: setMessageRatings, setSelectedMessage: setSelectedMessage, setTextRating: setTextRating, submitRating: submitRating })] }));
50838
+ : styles$5.feedbackStatusError), "aria-live": "polite", role: "status", children: feedbackStatus.message })), effectConfigs && effectConfigs.length > 0 && (jsx(ChatEffectsSystem, { messages: postprocessedMessages, effectConfigs: effectConfigs, soundSystem: soundSystem })), jsx("div", { className: classNames(className, styles$5.Chat, layout === 'STANDALONE' && styles$5.standaloneVisual, layout === 'FULL_PAGE' && styles$5.fullPageVisual, isConstrainedArticleMode && styles$5.constrainedArticleVisual, getChatCssClassName('Chat'), chatCssClassNames.chat), style, children: jsxs("div", { className: classNames(className, styles$5.chatMainFlow, getChatCssClassName('chatMainFlow'), chatCssClassNames.chatMainFlow), children: [children && jsx("div", { className: classNames(styles$5.chatChildren), children: children }), shouldShowScrollToBottom && (jsx("div", { className: styles$5.scrollToBottomContainer, children: jsxs("div", { className: styles$5.scrollToBottomWrapper, children: [jsx(SolidArrowButton, { "data-button-type": "custom", direction: "down", iconSize: 33, className: classNames(styles$5.scrollToBottom, scrollToBottomCssClassName), onClick: handleButtonClick(() => scrollToBottom()), "aria-label": ariaLabel, title: ariaLabel }), badgeLabel && (jsx("span", { className: styles$5.scrollToBottomBadge, "aria-live": "polite", role: "status", children: badgeLabel }))] }) })), isVoiceCalling && (jsx("div", { className: styles$5.voiceCallIndicatorBar, children: jsxs("div", { className: styles$5.voiceCallIndicator, children: [jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: jsx("path", { d: "M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z" }) }), jsx("span", { children: "Voice call active" }), jsx("div", { className: styles$5.voiceCallPulse })] }) })), jsx(ChatActionsBar, { actionsRef: actionsRef, actionsContainer: actionsContainer, messages: postprocessedMessages, participants: participants, title: title, onReset: onReset, resetRequiresConfirmation: resetRequiresConfirmation, newChatButtonHref: newChatButtonHref, onUseTemplate: onUseTemplate, extraActions: extraActions, saveFormats: saveFormats, isSaveButtonEnabled: isSaveButtonEnabled, shouldFadeActions: shouldFadeActions, shouldDisableActions: shouldDisableActions, chatUiTranslations: chatUiTranslations, onButtonClick: handleButtonClick }), jsx(ChatMessageList, { messages: postprocessedMessages, participants: participants, expandedMessageId: expandedMessageId, messageRatings: messageRatings, setExpandedMessageId: setExpandedMessageId, handleRating: handleRating, mode: mode, isCopyButtonEnabled: isCopyButtonEnabled, isFeedbackEnabled: isFeedbackEnabled, feedbackMode: feedbackMode, feedbackTranslations: feedbackTranslations, timingTranslations: timingTranslations, chatLocale: chatLocale, onCopy: handleCopy, onMessage: onMessage, onActionButton: onActionButton, onQuickMessageButton: onQuickMessageButton, onReplyToMessage: onReplyToMessage, canReplyToMessage: canReplyToMessage, onCreateAgent: onCreateAgent, toolTitles: toolTitles, teammates: teammates, teamAgentProfiles: teamAgentProfiles, visualMode: visualMode, soundSystem: soundSystem, onToolCallClick: openToolCall, onCitationClick: openCitation, setChatMessagesElement: setChatMessagesElement, onScroll: handleChatScroll, isSpeechPlaybackEnabled: isSpeechPlaybackEnabled, elevenLabsVoiceId: elevenLabsVoiceId, chatUiTranslations: chatUiTranslations, chatMessagesClassName: classNames(isConstrainedArticleMode && styles$5.articleModeChatMessages, getChatCssClassName('chatMessages'), chatCssClassNames.chatMessages), hasActions: hasActions }), onMessage && (jsx(ChatInputArea, { onMessage: onMessage, onChange: onChange, onFileUpload: onFileUpload, speechRecognition: speechRecognition, speechRecognitionLanguage: speechRecognitionLanguage, replyingToMessage: replyingToMessage, onCancelReply: onCancelReply, defaultMessage: defaultMessage, enterBehavior: enterBehavior, resolveEnterBehavior: resolveEnterBehavior, placeholderMessageContent: placeholderMessageContent || (chatUiTranslations === null || chatUiTranslations === void 0 ? void 0 : chatUiTranslations.inputPlaceholder), isFocusedOnLoad: isFocusedOnLoad, isMobile: isMobile, isVoiceCalling: isVoiceCalling, participants: participants, buttonColor: buttonColor, soundSystem: soundSystem, onButtonClick: handleButtonClick, chatUiTranslations: chatUiTranslations, chatInputClassName: classNames(isConstrainedArticleMode && styles$5.articleModeChatInput, getChatCssClassName('chatInput'), chatCssClassNames.chatInput) }))] }) }), jsx(ChatToolCallModal, { isOpen: toolCallModalOpen, toolCall: selectedToolCall, toolCallIdentity: selectedToolCallIdentity, onClose: closeToolCallModal, toolTitles: toolTitles, agentParticipant: agentParticipant, buttonColor: buttonColor, teamAgentProfiles: teamAgentProfiles, chatUiTranslations: chatUiTranslations, locale: chatLocale, availableTools: selectedMessageAvailableTools }), jsx(ChatCitationModal, { isOpen: citationModalOpen, citation: selectedCitation, participants: participants, soundSystem: soundSystem, onClose: closeCitationModal }), jsx(ChatRatingModal, { isOpen: ratingModalOpen, selectedMessage: selectedMessage, postprocessedMessages: postprocessedMessages, messages: messages, hoveredRating: hoveredRating, messageRatings: messageRatings, textRating: textRating, feedbackMode: feedbackMode, feedbackTranslations: feedbackTranslations, mode: mode, isMobile: isMobile, onClose: () => setRatingModalOpen(false), setHoveredRating: setHoveredRating, setMessageRatings: setMessageRatings, setSelectedMessage: setSelectedMessage, setTextRating: setTextRating, submitRating: submitRating })] }));
49831
50839
  }
49832
50840
 
49833
50841
  /**
@@ -51133,7 +52141,7 @@ function PromptbookAgentSeamlessIntegration(props) {
51133
52141
  : styles.PromptbookAgentSeamlessIntegrationStatusPending) }), jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationText, children: [jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLabel, children: "Chat" }), jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationHint, children: displayName })] }), jsx("span", { className: styles.PromptbookAgentSeamlessIntegrationScreenReaderOnly, children: connectionStatusText })] }), isOpen && (jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationWindow, id: windowId, children: [jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationHeader, style: { backgroundColor: color }, ref: setHeaderElement, children: [jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationTitleWrap, children: [jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationTitle, children: displayName }), jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationSubtitle, children: connectionStatusText })] }), isIframeUsed && (jsx("button", { className: styles.PromptbookAgentSeamlessIntegrationClose, onClick: () => setOpen(false), title: "Close", "aria-label": "Close chat", children: jsx(CloseIcon, {}) }))] }), jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationContent, children: isIframeUsed ? (jsxs(Fragment, { children: [!isIframeLoaded && (jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationLoading, children: [jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingShimmer }), jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingSpinner }), jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingTitle, children: "Preparing your chat" }), jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingText, children: ["Connecting to ", displayName, "..."] })] })), jsx("iframe", { src: agentUrl + '/chat?headless', className: styles.PromptbookAgentSeamlessIntegrationIframe, style: { opacity: isIframeLoaded ? 1 : 0 }, tabIndex: -1, onLoad: () => {
51134
52142
  setIsIframeLoaded(true);
51135
52143
  setIsChatConnected(true);
51136
- } })] })) : agent ? (jsx(AgentChat, { agent: agent, actionsContainer: headerElement, isFocusedOnLoad: isFocusedOnLoad, extraActions: jsx("button", { className: styles.PromptbookAgentSeamlessIntegrationClose, onClick: () => setOpen(false), title: "Close", "aria-label": "Close chat", children: jsx(CloseIcon, {}) }), visual: "STANDALONE" })) : error ? (jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationError, children: [jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationErrorTitle, children: "Failed to connect to the agent" }), jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationErrorMessage, children: error.message })] })) : (jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationLoading, children: [jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingShimmer }), jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingSpinner }), jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingTitle, children: "Preparing your chat" }), jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingText, children: ["Connecting to ", displayName, "..."] })] })) })] }))] }));
52144
+ } })] })) : agent ? (jsx(AgentChat, { agent: agent, actionsContainer: headerElement, isFocusedOnLoad: isFocusedOnLoad, extraActions: jsx("button", { className: styles.PromptbookAgentSeamlessIntegrationClose, onClick: () => setOpen(false), title: "Close", "aria-label": "Close chat", children: jsx(CloseIcon, {}) }), layout: "STANDALONE" })) : error ? (jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationError, children: [jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationErrorTitle, children: "Failed to connect to the agent" }), jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationErrorMessage, children: error.message })] })) : (jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationLoading, children: [jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingShimmer }), jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingSpinner }), jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingTitle, children: "Preparing your chat" }), jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingText, children: ["Connecting to ", displayName, "..."] })] })) })] }))] }));
51137
52145
  }
51138
52146
 
51139
52147
  /**