@cognizant-ai-lab/ui-common 1.7.0 → 1.9.0

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 (98) hide show
  1. package/README.md +3 -3
  2. package/dist/Theme/Palettes.d.ts +0 -9
  3. package/dist/Theme/Palettes.js +0 -18
  4. package/dist/components/AgentChat/ChatCommon/ChatCommon.d.ts +13 -4
  5. package/dist/components/AgentChat/ChatCommon/ChatCommon.js +131 -38
  6. package/dist/components/AgentChat/ChatCommon/ChatHistory.d.ts +7 -0
  7. package/dist/components/AgentChat/ChatCommon/ChatHistory.js +1 -1
  8. package/dist/components/AgentChat/ChatCommon/Const.d.ts +1 -0
  9. package/dist/components/AgentChat/ChatCommon/Const.js +2 -0
  10. package/dist/components/AgentChat/ChatCommon/ControlButtons.d.ts +5 -3
  11. package/dist/components/AgentChat/ChatCommon/ControlButtons.js +13 -4
  12. package/dist/components/AgentChat/ChatCommon/ConversationTurn.d.ts +5 -5
  13. package/dist/components/AgentChat/ChatCommon/ConversationTurn.js +1 -0
  14. package/dist/components/AgentChat/ChatCommon/FormattedMarkdown.js +3 -0
  15. package/dist/components/AgentChat/ChatCommon/SendButton.js +7 -6
  16. package/dist/components/AgentChat/ChatCommon/SyntaxHighlighterThemes.js +1 -1
  17. package/dist/components/AgentChat/ChatCommon/Thinking.d.ts +1 -0
  18. package/dist/components/AgentChat/ChatCommon/Thinking.js +10 -7
  19. package/dist/components/AgentChat/Common/LlmChatButton.d.ts +0 -2
  20. package/dist/components/AgentChat/Common/LlmChatButton.js +6 -9
  21. package/dist/components/AgentChat/Common/Utils.d.ts +4 -0
  22. package/dist/components/AgentChat/Common/Utils.js +5 -1
  23. package/dist/components/AgentChat/VoiceChat/MicrophoneButton.js +12 -10
  24. package/dist/components/Common/Breadcrumbs.js +1 -1
  25. package/dist/components/Common/Navbar.js +24 -22
  26. package/dist/components/Common/StatusLight.d.ts +9 -0
  27. package/dist/components/Common/StatusLight.js +21 -0
  28. package/dist/components/Common/notification.d.ts +4 -4
  29. package/dist/components/ErrorPage/ErrorBoundary.js +1 -1
  30. package/dist/components/ErrorPage/ErrorPage.d.ts +3 -3
  31. package/dist/components/ErrorPage/ErrorPage.js +4 -3
  32. package/dist/components/Logo/Common.d.ts +4 -0
  33. package/dist/components/Logo/Common.js +8 -0
  34. package/dist/components/Logo/CustomerLogo.d.ts +23 -0
  35. package/dist/components/Logo/CustomerLogo.js +41 -0
  36. package/dist/components/MultiAgentAccelerator/AgentConversations.js +0 -1
  37. package/dist/components/MultiAgentAccelerator/{AgentFlow.d.ts → AgentFlow/AgentFlow.d.ts} +15 -5
  38. package/dist/components/MultiAgentAccelerator/{AgentFlow.js → AgentFlow/AgentFlow.js} +125 -84
  39. package/dist/components/MultiAgentAccelerator/{AgentNode.d.ts → AgentFlow/AgentNode.d.ts} +1 -1
  40. package/dist/components/MultiAgentAccelerator/{AgentNode.js → AgentFlow/AgentNode.js} +26 -30
  41. package/dist/components/MultiAgentAccelerator/AgentFlow/GraphLayouts.d.ts +43 -0
  42. package/dist/components/MultiAgentAccelerator/{GraphLayouts.js → AgentFlow/GraphLayouts.js} +10 -41
  43. package/dist/components/MultiAgentAccelerator/AgentFlow/GraphStructure.d.ts +21 -0
  44. package/dist/components/MultiAgentAccelerator/AgentFlow/GraphStructure.js +27 -0
  45. package/dist/components/MultiAgentAccelerator/AgentFlow/PlasmaEdge.d.ts +7 -0
  46. package/dist/components/MultiAgentAccelerator/{PlasmaEdge.js → AgentFlow/PlasmaEdge.js} +13 -8
  47. package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.d.ts +3 -9
  48. package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.js +137 -103
  49. package/dist/components/MultiAgentAccelerator/Schema/SlyData.d.ts +17 -0
  50. package/dist/components/MultiAgentAccelerator/Schema/SlyData.js +23 -0
  51. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.d.ts +0 -5
  52. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +38 -28
  53. package/dist/components/MultiAgentAccelerator/Sidebar/ImportNetworkModal.d.ts +67 -0
  54. package/dist/components/MultiAgentAccelerator/Sidebar/ImportNetworkModal.js +585 -0
  55. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.d.ts +5 -4
  56. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.js +77 -168
  57. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.d.ts +31 -13
  58. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.js +177 -100
  59. package/dist/components/MultiAgentAccelerator/TemporaryNetworks.d.ts +22 -0
  60. package/dist/components/MultiAgentAccelerator/TemporaryNetworks.js +74 -3
  61. package/dist/components/MultiAgentAccelerator/{ThoughtBubbleEdge.d.ts → ThoughtBubbles/ThoughtBubbleEdge.d.ts} +1 -1
  62. package/dist/components/MultiAgentAccelerator/{ThoughtBubbleOverlay.js → ThoughtBubbles/ThoughtBubbleOverlay.js} +1 -1
  63. package/dist/components/MultiAgentAccelerator/Tour/MainTourSteps.js +15 -8
  64. package/dist/components/MultiAgentAccelerator/const.d.ts +3 -0
  65. package/dist/components/MultiAgentAccelerator/const.js +6 -0
  66. package/dist/components/Settings/ApiKeyInput.js +7 -8
  67. package/dist/components/Settings/InfoTip.d.ts +8 -0
  68. package/dist/components/Settings/InfoTip.js +14 -0
  69. package/dist/components/Settings/SettingsDialog.js +373 -183
  70. package/dist/components/Settings/SettingsRow.d.ts +18 -0
  71. package/dist/components/Settings/SettingsRow.js +35 -0
  72. package/dist/controller/agent/Agent.d.ts +16 -25
  73. package/dist/controller/agent/Agent.js +24 -54
  74. package/dist/controller/agent/IconSuggestions.d.ts +35 -0
  75. package/dist/controller/agent/IconSuggestions.js +85 -0
  76. package/dist/index.d.ts +1 -3
  77. package/dist/index.js +1 -3
  78. package/dist/state/IconSuggestions.d.ts +27 -0
  79. package/dist/state/IconSuggestions.js +50 -0
  80. package/dist/state/Settings.d.ts +97 -6
  81. package/dist/state/Settings.js +110 -8
  82. package/dist/tsconfig.build.tsbuildinfo +1 -1
  83. package/dist/utils/BrowserNavigation.js +2 -0
  84. package/dist/utils/File.d.ts +4 -1
  85. package/dist/utils/File.js +4 -9
  86. package/dist/utils/text.js +3 -7
  87. package/dist/utils/title.js +1 -1
  88. package/package.json +9 -6
  89. package/dist/components/Common/CustomerLogo.d.ts +0 -17
  90. package/dist/components/Common/CustomerLogo.js +0 -47
  91. package/dist/components/Common/MUIAccordion.d.ts +0 -17
  92. package/dist/components/Common/MUIAccordion.js +0 -66
  93. package/dist/components/MultiAgentAccelerator/GraphLayouts.d.ts +0 -36
  94. package/dist/components/MultiAgentAccelerator/PlasmaEdge.d.ts +0 -3
  95. package/dist/utils/useLocalStorage.d.ts +0 -1
  96. package/dist/utils/useLocalStorage.js +0 -55
  97. /package/dist/components/MultiAgentAccelerator/{ThoughtBubbleEdge.js → ThoughtBubbles/ThoughtBubbleEdge.js} +0 -0
  98. /package/dist/components/MultiAgentAccelerator/{ThoughtBubbleOverlay.d.ts → ThoughtBubbles/ThoughtBubbleOverlay.d.ts} +0 -0
@@ -35,32 +35,32 @@ import Typography from "@mui/material/Typography";
35
35
  import { applyNodeChanges, Background, ConnectionMode, ControlButton, Controls, ReactFlow, useReactFlow, useStore, } from "@xyflow/react";
36
36
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
37
37
  import { AgentNode, NODE_HEIGHT, NODE_WIDTH } from "./AgentNode.js";
38
- import { AgentNodePopup } from "./AgentNodePopup.js";
39
- import { AGENT_NETWORK_DEFINITION_KEY, AGENT_NETWORK_DESIGNER_ID, AGENT_NETWORK_NAME_KEY, BASE_RADIUS, DEFAULT_FRONTMAN_X_POS, DEFAULT_FRONTMAN_Y_POS, LEVEL_SPACING, } from "./const.js";
38
+ import { AGENT_NETWORK_DEFINITION_KEY, AGENT_NETWORK_DESIGNER_ID, AGENT_NETWORK_NAME_KEY, BASE_RADIUS, DEFAULT_FRONTMAN_X_POS, DEFAULT_FRONTMAN_Y_POS, LEVEL_SPACING, } from "../const.js";
40
39
  import { addThoughtBubbleEdge, layoutLinear, layoutRadial } from "./GraphLayouts.js";
41
40
  import { PlasmaEdge } from "./PlasmaEdge.js";
42
- import { convertReservationsToNetworks, extractNetworkHocon, extractReservations, isEditableAgent, mergeNetworks, } from "./TemporaryNetworks.js";
43
- import { ThoughtBubbleEdge } from "./ThoughtBubbleEdge.js";
44
- import { ThoughtBubbleOverlay } from "./ThoughtBubbleOverlay.js";
45
- import { sendChatQuery } from "../../controller/agent/Agent.js";
46
- import { StreamingUnit } from "../../controller/llm/LlmChat.js";
47
- import { useAgentChatHistoryStore } from "../../state/ChatHistory.js";
48
- import { useTempNetworksStore } from "../../state/TemporaryNetworks.js";
49
- import { usePalette } from "../../Theme/Palettes.js";
50
- import { getZIndex } from "../../utils/zIndexLayers.js";
51
- import { chatMessageFromChunk } from "../AgentChat/Common/Utils.js";
52
- import { MUIAlert } from "../Common/MUIAlert.js";
53
- import { NotificationType, sendNotification } from "../Common/notification.js";
54
- // #endregion: Types
55
- // #region: Constants
41
+ import { sendChatQuery } from "../../../controller/agent/Agent.js";
42
+ import { StreamingUnit } from "../../../controller/llm/LlmChat.js";
43
+ import { useAgentChatHistoryStore } from "../../../state/ChatHistory.js";
44
+ import { usePalette, useSettingsStore } from "../../../state/Settings.js";
45
+ import { useTempNetworksStore } from "../../../state/TemporaryNetworks.js";
46
+ import { getZIndex } from "../../../utils/zIndexLayers.js";
47
+ import { chatMessageFromChunk } from "../../AgentChat/Common/Utils.js";
48
+ import { MUIAlert } from "../../Common/MUIAlert.js";
49
+ import { NotificationType, sendNotification } from "../../Common/notification.js";
50
+ import { AgentNodePopup } from "../AgentNodePopup.js";
51
+ import { convertReservationsToNetworks, extractNetworkHocon, extractReservations, isEditableAgent, mergeNetworks, } from "../TemporaryNetworks.js";
52
+ import { ThoughtBubbleEdge } from "../ThoughtBubbles/ThoughtBubbleEdge.js";
53
+ import { ThoughtBubbleOverlay } from "../ThoughtBubbles/ThoughtBubbleOverlay.js";
54
+ //#endregion: Types
55
+ //#region: Constants
56
56
  // Timeout for thought bubbles is set to 10 seconds
57
57
  const THOUGHT_BUBBLE_TIMEOUT_MS = 10_000;
58
- // How long the dock'sstatus banner stays visible before auto-dismissing. Error banners persist until dismissed.
58
+ // How long the dock's status banner stays visible before auto-dismissing. Error banners persist until dismissed.
59
59
  // Exported for tests.
60
60
  export const DOCK_BANNER_AUTO_DISMISS_MS = 5_000;
61
- // #endregion: Constants
62
61
  const DOCK_PROMPT_PLACEHOLDER = "Describe a change to the network";
63
- // #region: Helpers
62
+ //#endregion: Constants
63
+ //#region: Helpers
64
64
  /**
65
65
  * Streams the Agent Network Designer endpoint with a natural-language prompt and the current
66
66
  * network definition, collecting any returned reservations.
@@ -86,7 +86,22 @@ const streamNetworkDesignerPrompt = async (neuroSanURL, signal, userPrompt, curr
86
86
  }, currentUser, StreamingUnit.Line);
87
87
  return newNetworks;
88
88
  };
89
- // #endregion: Helpers
89
+ /**
90
+ * Filters node events based on the current mode. Don't allow any topological-modifying events (adding, deleting nodes)
91
+ * and in Agent Network Designer mode, don't allow dragging nodes (position changes) either.
92
+ * @param change The node change event to filter.
93
+ * @param isAgentNetworkDesignerMode Whether the flow is in Agent Network Designer mode (read-only preview).
94
+ * @return True if the event should be allowed, false if it should be filtered out.
95
+ */
96
+ export const filterNodeEvents = (change, isAgentNetworkDesignerMode) => {
97
+ // Only allow nodes to be dragged, no topological edits to the graph (read-only)
98
+ if (["remove", "add", "replace"].includes(change.type))
99
+ return false;
100
+ // Disallow dragging nodes in AND mode since it's supposed to be a read-only preview but
101
+ // pass along all other event types as
102
+ return !(change.type === "position" && isAgentNetworkDesignerMode);
103
+ };
104
+ //#endregion: Helpers
90
105
  export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork, currentConversations, currentUser, id, isAgentNetworkDesignerMode, isAwaitingLlm, isEditMode, isStreaming, isSelectedNetworkTemporary: isTemporaryNetwork, networkDisplayName, networkId, neuroSanURL, onEnterEditMode, onNetworkReplaced, onExitEditMode, onSaveAgent, thoughtBubbleEdges, setThoughtBubbleEdges, }) => {
91
106
  const theme = useTheme();
92
107
  const { fitView } = useReactFlow();
@@ -113,6 +128,8 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
113
128
  }, []);
114
129
  // Ref for isStreaming, read inside the cleanup interval.
115
130
  const isStreamingRef = useRef(isStreaming);
131
+ // Display option for agent/network names
132
+ const useNativeNames = useSettingsStore((state) => state.settings.appearance.useNativeNames);
116
133
  // Keep the ref current after every render.
117
134
  useEffect(() => {
118
135
  isStreamingRef.current = isStreaming;
@@ -166,7 +183,7 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
166
183
  return edgesMap ?? prev;
167
184
  });
168
185
  }, [currentConversations, showThoughtBubbles, setThoughtBubbleEdges]);
169
- // Cleanup expired thought bubble edges — created once on mount, reads isStreaming via ref.
186
+ // Clean up expired thought bubble edges — created once on mount, reads isStreaming via ref.
170
187
  useEffect(() => {
171
188
  const cleanupInterval = setInterval(() => {
172
189
  if (!isStreamingRef.current)
@@ -187,9 +204,6 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
187
204
  }, 1000);
188
205
  return () => clearInterval(cleanupInterval);
189
206
  }, [setThoughtBubbleEdges]); // mount/unmount only
190
- // Shadow color for icon
191
- const isDarkMode = theme.palette.mode === "dark";
192
- const foregroundColor = isDarkMode ? theme.palette.common.white : theme.palette.common.black;
193
207
  const isHeatmap = coloringOption === "heatmap";
194
208
  const palette = usePalette();
195
209
  // Merge agents from active thought bubbles with agentsInNetwork for layout
@@ -216,8 +230,28 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
216
230
  // Create the flow layout depending on user preference
217
231
  // Memoize layoutResult so it only recalculates when relevant data changes
218
232
  const layoutResult = useMemo(() => layout === "linear"
219
- ? layoutLinear(isHeatmap ? agentCounts : undefined, mergedAgentsInNetwork, currentConversations, isAwaitingLlm, isAgentNetworkDesignerMode, thoughtBubbleEdges, agentIconSuggestions, isTemporaryNetwork)
220
- : layoutRadial(isHeatmap ? agentCounts : undefined, mergedAgentsInNetwork, currentConversations, isAwaitingLlm, isAgentNetworkDesignerMode, thoughtBubbleEdges, agentIconSuggestions, isTemporaryNetwork), [
233
+ ? layoutLinear({
234
+ agentCounts: isHeatmap ? agentCounts : undefined,
235
+ agentIconSuggestions,
236
+ agentsInNetwork: mergedAgentsInNetwork,
237
+ currentConversations,
238
+ isAgentNetworkDesignerMode,
239
+ isAwaitingLlm,
240
+ isTemporaryNetwork,
241
+ thoughtBubbleEdges,
242
+ useNativeNames,
243
+ })
244
+ : layoutRadial({
245
+ agentCounts: isHeatmap ? agentCounts : undefined,
246
+ agentIconSuggestions,
247
+ agentsInNetwork: mergedAgentsInNetwork,
248
+ currentConversations,
249
+ isAgentNetworkDesignerMode,
250
+ isAwaitingLlm,
251
+ isTemporaryNetwork,
252
+ thoughtBubbleEdges,
253
+ useNativeNames,
254
+ }), [
221
255
  agentCounts,
222
256
  agentIconSuggestions,
223
257
  currentConversations,
@@ -228,6 +262,7 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
228
262
  layout,
229
263
  mergedAgentsInNetwork,
230
264
  thoughtBubbleEdges,
265
+ useNativeNames,
231
266
  ]);
232
267
  const [nodes, setNodes] = useState(layoutResult.nodes);
233
268
  // Sync up the nodes with the layout result
@@ -237,7 +272,7 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
237
272
  // Track which node the user clicked on so we can open the popup
238
273
  const [selectedAgent, setSelectedAgent] = useState(null);
239
274
  const [isPopupOpen, setIsPopupOpen] = useState(false);
240
- // True while the agent-edit request is in-flight so we can disable the Save button.
275
+ // True, while the agent-edit request is in-flight so we can disable the Save button.
241
276
  const [isSavingAgent, setIsSavingAgent] = useState(false);
242
277
  // AbortController for the in-flight save request — stored in a ref so handlePopupClose can cancel it.
243
278
  const saveAbortControllerRef = useRef(null);
@@ -247,7 +282,7 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
247
282
  const dockAbortControllerRef = useRef(null);
248
283
  // Stop-confirm overlay state: null = not shown, "confirming" = abort dialog open.
249
284
  const [stopState, setStopState] = useState(null);
250
- // Inline status banner shown above the dock header after an apply succeeds, is cancelled, or fails.
285
+ // Inline status banner shown above the dock header after an "apply" succeeds, is canceled, or fails.
251
286
  const [dockBanner, setDockBanner] = useState(null);
252
287
  const bannerTimeoutRef = useRef(null);
253
288
  // Clear the banner auto-dismiss timer on unmount.
@@ -333,7 +368,7 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
333
368
  }
334
369
  return true;
335
370
  }
336
- // Reservations came back but none matched the current network — surface this in the dock banner.
371
+ // Reservations came back, but none matched the current network — surface this in the dock banner.
337
372
  showDockBanner({
338
373
  severity: "error",
339
374
  title: "Failed to apply network change.",
@@ -370,15 +405,17 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
370
405
  }
371
406
  catch (e) {
372
407
  const isAbort = e instanceof DOMException && e.name === "AbortError";
373
- if (!isAbort) {
374
- showDockBanner({ severity: "error", title: "Failed to apply network change.", detail: String(e) });
408
+ if (isAbort) {
409
+ if (hasTimedOut) {
410
+ showDockBanner({
411
+ severity: "error",
412
+ title: "Failed to apply network change.",
413
+ detail: "The request timed out. Please try again.",
414
+ });
415
+ }
375
416
  }
376
- else if (hasTimedOut) {
377
- showDockBanner({
378
- severity: "error",
379
- title: "Failed to apply network change.",
380
- detail: "The request timed out. Please try again.",
381
- });
417
+ else {
418
+ showDockBanner({ severity: "error", title: "Failed to apply network change.", detail: String(e) });
382
419
  }
383
420
  }
384
421
  finally {
@@ -444,10 +481,7 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
444
481
  }, 50);
445
482
  }, [agentsInNetwork, fitView, layout]);
446
483
  const onNodesChange = useCallback((changes) => {
447
- setNodes((currentNodes) => applyNodeChanges(
448
- // For now, we only allow dragging, no updates. In agent network designer mode, doesn't make sense
449
- // to allow position changes since the user isn't actually manipulating a real network
450
- changes.filter((c) => c.type === "position" && !isAgentNetworkDesignerMode), currentNodes));
484
+ setNodes((currentNodes) => applyNodeChanges(changes.filter((change) => filterNodeEvents(change, isAgentNetworkDesignerMode)), currentNodes));
451
485
  }, [isAgentNetworkDesignerMode]);
452
486
  const transform = useStore((state) => state.transform);
453
487
  // Why not just a "const"? See: https://reactflow.dev/learn/customization/custom-nodes
@@ -479,53 +513,61 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
479
513
  const getLegend = () => {
480
514
  const length = isHeatmap ? palette.length : Math.min(maxDepth, palette.length);
481
515
  return (_jsxs(Box, { id: `${id}-legend`, sx: {
482
- position: "absolute",
483
- top: "1.5rem",
484
- right: "10px",
485
- padding: "5px",
516
+ alignItems: "center",
517
+ backgroundColor: theme.palette.background.paper,
518
+ border: `1px solid ${theme.palette.divider}`,
519
+ boxShadow: `0 2px 8px ${alpha(theme.palette.text.primary, 0.18)}`,
486
520
  borderRadius: "5px",
487
- boxShadow: `0 0 5px color-mix(in srgb, ${foregroundColor} 30%, transparent)`,
488
521
  display: "flex",
489
- alignItems: "center",
522
+ padding: theme.spacing(0.5),
523
+ position: "absolute",
524
+ right: theme.spacing(2),
525
+ top: theme.spacing(4),
490
526
  zIndex: getZIndex(2, theme),
491
- }, children: [Array.from({ length }, (_, i) => (_jsx(Box, { id: `${id}-legend-depth-${i}`, style: {
527
+ }, children: [Array.from({ length }, (_, i) => (_jsx(Box, { id: `${id}-legend-depth-${i}`, sx: {
492
528
  alignItems: "center",
493
529
  backgroundColor: palette[i],
494
530
  borderRadius: "50%",
495
531
  color: theme.palette.getContrastText(palette[i]),
496
532
  display: "flex",
497
- height: "15px",
533
+ fontSize: "0.5rem",
498
534
  justifyContent: "center",
499
- marginLeft: "5px",
535
+ marginLeft: theme.spacing(0.75),
500
536
  width: "15px",
501
- }, children: _jsx(Typography, { id: `${id}-legend-depth-${i}-text`, sx: {
502
- fontSize: "8px",
503
- }, children: i }) }, i))), _jsxs(ToggleButtonGroup, { id: `${id}-coloring-toggle`, value: coloringOption, exclusive: true, onChange: (_, newValue) => {
537
+ }, children: i }, i))), _jsxs(ToggleButtonGroup, { id: `${id}-coloring-toggle`, value: coloringOption, exclusive: true, onChange: (_, newValue) => {
504
538
  if (newValue !== null) {
505
539
  setColoringOption(newValue);
506
540
  }
507
- }, sx: {
508
- fontSize: "2rem",
509
- zIndex: 10,
510
- marginLeft: "0.5rem",
511
- }, size: "small", children: [_jsx(ToggleButton, { id: `${id}-depth-toggle`, size: "small", value: "depth", sx: {
541
+ }, size: "small", sx: {
542
+ marginLeft: theme.spacing(2),
543
+ "& .MuiToggleButton-root": {
544
+ borderColor: theme.palette.divider,
545
+ color: theme.palette.text.primary,
546
+ minHeight: 22,
547
+ px: 1,
548
+ "&:hover": {
549
+ backgroundColor: theme.palette.action.hover,
550
+ },
551
+ "&.Mui-selected": {
552
+ backgroundColor: theme.palette.action.selected,
553
+ borderColor: theme.palette.text.primary,
554
+ },
555
+ "&.Mui-selected:hover": {
556
+ backgroundColor: theme.palette.action.selected,
557
+ borderColor: theme.palette.text.primary,
558
+ },
559
+ },
560
+ }, children: [_jsx(ToggleButton, { id: `${id}-depth-toggle`, value: "depth", sx: {
512
561
  fontSize: "0.5rem",
513
562
  height: "1rem",
514
- }, children: _jsx(Typography, { id: `${id}-depth-label`, sx: {
515
- fontSize: "10px",
516
- }, children: "Depth" }) }), _jsx(ToggleButton, { id: `${id}-heatmap-toggle`, size: "small", value: "heatmap", sx: {
563
+ }, children: "Depth" }), _jsx(ToggleButton, { id: `${id}-heatmap-toggle`, value: "heatmap", sx: {
517
564
  fontSize: "0.5rem",
518
565
  height: "1rem",
519
- }, children: _jsx(Typography, { id: `${id}-heatmap-label`, sx: {
520
- fontSize: "10px",
521
- }, children: "Heatmap" }) })] })] }));
566
+ }, children: "Heatmap" })] })] }));
522
567
  };
523
- // Get the background color for the control buttons based on the layout and dark mode setting
568
+ // Get the background color for the control buttons; differs based on whether the button is active or not
524
569
  const getControlButtonBackgroundColor = (isActive) => {
525
- if (!isActive) {
526
- return undefined;
527
- }
528
- return isDarkMode ? theme.palette.grey[800] : theme.palette.grey[200];
570
+ return isActive ? theme.palette.action.selected : undefined;
529
571
  };
530
572
  // Only show radial guides if radial layout is selected, radial guides are enabled, and it's not just Frontman
531
573
  const shouldShowRadialGuides = enableRadialGuides && layout === "radial" && maxDepth > 1;
@@ -562,12 +604,10 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
562
604
  }, children: [_jsx(Tooltip, { title: networkDisplayName, placement: "top", children: _jsx(Box, { sx: { pointerEvents: "auto" }, children: _jsx(Typography, { id: `${id}-network-title`, variant: "subtitle1", sx: {
563
605
  backdropFilter: "blur(6px)",
564
606
  backgroundColor: titleBackgroundColor,
565
- border: `1px solid ${alpha(theme.palette.divider, 0.6)}`,
607
+ border: `1px solid ${alpha(theme.palette.divider, 0.75)}`,
566
608
  borderRadius: 2,
567
- boxShadow: theme.palette.mode === "dark"
568
- ? `0 6px 20px ${alpha(theme.palette.common.black, 0.35)}`
569
- : `0 6px 16px ${alpha(theme.palette.common.black, 0.12)}`,
570
- color: theme.palette.getContrastText(alpha(titleBackgroundColor, 0.65)),
609
+ boxShadow: theme.shadows[6],
610
+ color: theme.palette.text.primary,
571
611
  fontWeight: 600,
572
612
  letterSpacing: "0.01em",
573
613
  lineHeight: 1.35,
@@ -592,21 +632,24 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
592
632
  "& .react-flow__node": {
593
633
  border: "1px solid divider",
594
634
  },
595
- "& .react-flow__panel": {
635
+ "& .react-flow__panel, & .react-flow__controls-button": {
596
636
  backgroundColor: theme.palette.background.paper,
597
- border: "1px solid divider",
598
637
  color: theme.palette.text.primary,
599
638
  },
639
+ "& .react-flow__panel": {
640
+ border: "1px solid divider",
641
+ },
600
642
  "& .react-flow__controls-button": {
601
- backgroundColor: theme.palette.background.paper,
602
643
  borderBottom: "1px solid divider",
603
- color: theme.palette.text.primary,
604
644
  fill: theme.palette.text.primary,
605
645
  },
606
- }, children: [_jsxs(Box, { id: `${id}-react-flow-wrapper`, sx: { position: "relative", flex: 1, minHeight: 0 }, children: [networkDisplayName ? _jsx(Box, { sx: { marginBottom: "1rem" }, children: getTitle() }) : null, _jsx(ReactFlow, { id: `${id}-react-flow`, nodes: nodes, edges: edges, onNodesChange: onNodesChange, onNodeClick: handleNodeClick, fitView: true, nodeTypes: nodeTypes, edgeTypes: edgeTypes, connectionMode: ConnectionMode.Loose, children: !isAwaitingLlm && (_jsxs(_Fragment, { children: [agentsInNetwork?.length && !isAgentNetworkDesignerMode && !isEditMode ? getLegend() : null, _jsx(Background, { id: `${id}-background` }), !isAgentNetworkDesignerMode && !isEditMode && getControls(), shouldShowRadialGuides ? getRadialGuides() : null] })) }), _jsx(ThoughtBubbleOverlay, { nodes: nodes, edges: thoughtBubbleEdgesForOverlay, showThoughtBubbles: showThoughtBubbles, isStreaming: isStreaming, onBubbleHoverChange: handleBubbleHoverChange })] }), isEditMode && isTemporaryNetwork && !isAwaitingLlm && (_jsxs(Box, { sx: {
646
+ }, children: [_jsxs(Box, { id: `${id}-react-flow-wrapper`, sx: { position: "relative", flex: 1, minHeight: 0 }, children: [networkDisplayName ? _jsx(Box, { sx: { marginBottom: "1rem" }, children: getTitle() }) : null, _jsx(ReactFlow, { connectionMode: ConnectionMode.Loose, edgeTypes: edgeTypes, edges: edges, fitView: true, id: `${id}-react-flow`, nodeTypes: nodeTypes, nodes: nodes, nodesDraggable: !isAgentNetworkDesignerMode, onNodeClick: handleNodeClick, onNodesChange: onNodesChange, children: !isAwaitingLlm && (_jsxs(_Fragment, { children: [agentsInNetwork?.length && !isAgentNetworkDesignerMode && !isEditMode ? getLegend() : null, _jsx(Background, { id: `${id}-background` }), !isAgentNetworkDesignerMode && !isEditMode && getControls(), shouldShowRadialGuides ? getRadialGuides() : null] })) }), _jsx(ThoughtBubbleOverlay, { nodes: nodes, edges: thoughtBubbleEdgesForOverlay, showThoughtBubbles: showThoughtBubbles, isStreaming: isStreaming, onBubbleHoverChange: handleBubbleHoverChange })] }), isEditMode && isTemporaryNetwork && !isAwaitingLlm && (_jsxs(Box, { sx: {
647
+ backdropFilter: "blur(8px)",
648
+ backgroundColor: alpha(theme.palette.background.paper, 0.2),
607
649
  borderTop: `2px solid ${theme.palette.primary.main}`,
608
- backgroundColor: theme.palette.background.paper,
609
650
  flexShrink: 0,
651
+ position: "relative",
652
+ zIndex: getZIndex(2, theme),
610
653
  }, children: [dockBanner && (_jsx(MUIAlert, { closeable: true, id: `${id}-dock-banner`, onClose: handleDismissBanner, severity: dockBanner.severity, sx: {
611
654
  borderRadius: 0,
612
655
  // Override MUIAlert's default 1rem bottom margin so the banner sits flush
@@ -617,10 +660,8 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
617
660
  // vertically with the header's close X below it.
618
661
  paddingRight: 0.5,
619
662
  alignItems: "center",
620
- // Frost the banner like the dock header so the graph doesn't show through the
621
- // app's translucent paper background; keep a tinted, mostly-opaque severity wash.
622
- backdropFilter: "blur(8px)",
623
- backgroundColor: alpha(theme.palette[dockBanner.severity].main, isDarkMode ? 0.28 : 0.16),
663
+ // Frost the banner like the dock header, so the graph doesn't show through the
664
+ // app's translucent paper background; keep a tinted, mostly opaque severity wash.
624
665
  "& .MuiAlert-action": {
625
666
  alignItems: "center",
626
667
  marginRight: 0,
@@ -1,7 +1,7 @@
1
1
  import { NodeProps } from "@xyflow/react";
2
2
  import type { Node as RFNode } from "@xyflow/react";
3
3
  import { FC } from "react";
4
- import { AgentConversation } from "./AgentConversations.js";
4
+ import { AgentConversation } from "../AgentConversations.js";
5
5
  export interface AgentNodeProps extends Record<string, unknown> {
6
6
  readonly agentCounts?: Map<string, number>;
7
7
  readonly agentName: string;
@@ -28,11 +28,10 @@ import Tooltip from "@mui/material/Tooltip";
28
28
  import Typography from "@mui/material/Typography";
29
29
  import { Handle, Position } from "@xyflow/react";
30
30
  import { useState } from "react";
31
- import { DisplayAs } from "./const.js";
32
- import { useSettingsStore } from "../../state/Settings.js";
33
- import { usePalette } from "../../Theme/Palettes.js";
34
- import { isLightColor } from "../../Theme/Theme.js";
35
- import { getZIndex } from "../../utils/zIndexLayers.js";
31
+ import { usePalette, useSettingsStore } from "../../../state/Settings.js";
32
+ import { isLightColor } from "../../../Theme/Theme.js";
33
+ import { getZIndex } from "../../../utils/zIndexLayers.js";
34
+ import { DisplayAs } from "../const.js";
36
35
  // A zero-width space character, used to give the browser a hint where to break long names
37
36
  const ZERO_WIDTH_SPACE = "\u200B";
38
37
  // Node dimensions
@@ -125,32 +124,28 @@ export const AgentNode = (props) => {
125
124
  // Determine which icon to display based on the agent type whether it is Frontman or not
126
125
  const getDisplayAsIcon = () => {
127
126
  const id = `${agentId}-icon`;
128
- if (agentIconSuggestion && MuiIcons[agentIconSuggestion]) {
129
- const IconComponent = MuiIcons[agentIconSuggestion];
130
- return _jsx(IconComponent, { sx: { fontSize: AGENT_ICON_SIZE } });
127
+ // Check for valid MUI icon suggestion from the LLM suggestion service
128
+ const SuggestedIcon = agentIconSuggestion ? MuiIcons[agentIconSuggestion] : undefined;
129
+ // If the LLM provided a valid icon suggestion, use it. Otherwise, fall back to default icons based on
130
+ // agent type.
131
+ if (SuggestedIcon) {
132
+ return (_jsx(SuggestedIcon, { id: id, sx: { fontSize: AGENT_ICON_SIZE } }));
131
133
  }
132
- else {
133
- if (agentIconSuggestion) {
134
- console.warn(`Invalid MUI icon suggestion: ${agentIconSuggestion}`);
135
- }
136
- if (isFrontman) {
137
- return (
138
- // Use special icon and larger size for Frontman
139
- _jsx(PersonIcon, { id: id, sx: { fontSize: FRONTMAN_ICON_SIZE } }));
140
- }
141
- else {
142
- switch (displayAs) {
143
- case DisplayAs.EXTERNAL_AGENT:
144
- return (_jsx(TravelExploreIcon, { id: id, sx: { fontSize: AGENT_ICON_SIZE } }));
145
- // This should be a supported type but we're not seeing it?
146
- case DisplayAs.LANGCHAIN_TOOL:
147
- case DisplayAs.CODED_TOOL:
148
- return (_jsx(HandymanIcon, { id: id, sx: { fontSize: AGENT_ICON_SIZE } }));
149
- case DisplayAs.LLM_AGENT:
150
- default:
151
- return (_jsx(AutoAwesomeIcon, { id: id, sx: { fontSize: AGENT_ICON_SIZE } }));
152
- }
153
- }
134
+ if (isFrontman) {
135
+ return (
136
+ // Use special icon and larger size for Frontman
137
+ _jsx(PersonIcon, { id: id, sx: { fontSize: FRONTMAN_ICON_SIZE } }));
138
+ }
139
+ switch (displayAs) {
140
+ case DisplayAs.EXTERNAL_AGENT:
141
+ return (_jsx(TravelExploreIcon, { id: id, sx: { fontSize: AGENT_ICON_SIZE } }));
142
+ // This should be a supported type but we're not seeing it?
143
+ case DisplayAs.LANGCHAIN_TOOL:
144
+ case DisplayAs.CODED_TOOL:
145
+ return (_jsx(HandymanIcon, { id: id, sx: { fontSize: AGENT_ICON_SIZE } }));
146
+ case DisplayAs.LLM_AGENT:
147
+ default:
148
+ return (_jsx(AutoAwesomeIcon, { id: id, sx: { fontSize: AGENT_ICON_SIZE } }));
154
149
  }
155
150
  };
156
151
  // Determine icon color based on settings. If auto color is enabled, use contrasting color for readability.
@@ -160,6 +155,7 @@ export const AgentNode = (props) => {
160
155
  ? theme.palette.common.black
161
156
  : theme.palette.common.white;
162
157
  const [isHovered, setIsHovered] = useState(false);
158
+ // Insert zero-width spaces into the agent name to give the browser hints where to wrap.
163
159
  const wrappedAgentName = agentName
164
160
  // Allow wrap after underscore
165
161
  .replaceAll("_", `_${ZERO_WIDTH_SPACE}`)
@@ -0,0 +1,43 @@
1
+ import { Edge, Node as RFNode } from "@xyflow/react";
2
+ import { AgentConversation } from "../AgentConversations.js";
3
+ import { AgentNodeProps } from "./AgentNode.js";
4
+ import { AgentIconSuggestions } from "../../../controller/Types/AgentIconSuggestions.js";
5
+ import { ConnectivityInfo } from "../../../generated/neuro-san/NeuroSanClient.js";
6
+ import { ThoughtBubbleEdgeShape } from "../ThoughtBubbles/ThoughtBubbleEdge.js";
7
+ export declare const MAX_GLOBAL_THOUGHT_BUBBLES = 5;
8
+ /**
9
+ * Result of the layout algorithms, containing the nodes and edges with their computed positions and properties.
10
+ */
11
+ export type LayoutResult = {
12
+ nodes: RFNode<AgentNodeProps>[];
13
+ edges: Edge[];
14
+ };
15
+ export declare const addThoughtBubbleEdge: (thoughtBubbleEdges: Map<string, {
16
+ edge: ThoughtBubbleEdgeShape;
17
+ timestamp: number;
18
+ }>, conversationId: string, edge: ThoughtBubbleEdgeShape) => void;
19
+ export declare const removeThoughtBubbleEdge: (thoughtBubbleEdges: Map<string, {
20
+ edge: ThoughtBubbleEdgeShape;
21
+ timestamp: number;
22
+ }>, conversationId: string) => void;
23
+ export declare const getThoughtBubbleEdges: (thoughtBubbleEdges: Map<string, {
24
+ edge: ThoughtBubbleEdgeShape;
25
+ timestamp: number;
26
+ }>) => ThoughtBubbleEdgeShape[];
27
+ /** * Shared options for graph layout functions. */
28
+ export type LayoutOptions = {
29
+ readonly agentCounts: Map<string, number>;
30
+ readonly agentIconSuggestions?: AgentIconSuggestions | null;
31
+ readonly agentsInNetwork: ConnectivityInfo[];
32
+ readonly currentConversations: AgentConversation[] | null;
33
+ readonly isAgentNetworkDesignerMode: boolean;
34
+ readonly isAwaitingLlm: boolean;
35
+ readonly isTemporaryNetwork?: boolean;
36
+ readonly thoughtBubbleEdges: Map<string, {
37
+ edge: ThoughtBubbleEdgeShape;
38
+ timestamp: number;
39
+ }>;
40
+ readonly useNativeNames: boolean;
41
+ };
42
+ export declare const layoutRadial: ({ agentCounts, agentIconSuggestions, agentsInNetwork, currentConversations, isAgentNetworkDesignerMode, isAwaitingLlm, isTemporaryNetwork, thoughtBubbleEdges, useNativeNames, }: LayoutOptions) => LayoutResult;
43
+ export declare const layoutLinear: ({ agentCounts, agentIconSuggestions, agentsInNetwork, currentConversations, isAgentNetworkDesignerMode, isAwaitingLlm, isTemporaryNetwork, thoughtBubbleEdges, useNativeNames, }: LayoutOptions) => LayoutResult;
@@ -20,9 +20,10 @@ import dagre from "@dagrejs/dagre";
20
20
  import { MarkerType } from "@xyflow/react";
21
21
  import cloneDeep from "lodash-es/cloneDeep.js";
22
22
  import { FRONTMAN_SIZE_MULTIPLIER, NODE_HEIGHT, NODE_WIDTH } from "./AgentNode.js";
23
- import { BASE_RADIUS, DEFAULT_FRONTMAN_X_POS, DEFAULT_FRONTMAN_Y_POS, LEVEL_SPACING } from "./const.js";
24
- import { isEditableAgent } from "./TemporaryNetworks.js";
25
- import { cleanUpAgentName, KNOWN_MESSAGE_TYPES_FOR_PLASMA } from "../AgentChat/Common/Utils.js";
23
+ import { cleanUpAgentName, KNOWN_MESSAGE_TYPES_FOR_PLASMA } from "../../AgentChat/Common/Utils.js";
24
+ import { BASE_RADIUS, DEFAULT_FRONTMAN_X_POS, DEFAULT_FRONTMAN_Y_POS, LEVEL_SPACING } from "../const.js";
25
+ import { getFrontman, getParentAgents, getParents } from "./GraphStructure.js";
26
+ import { isEditableAgent } from "../TemporaryNetworks.js";
26
27
  export const MAX_GLOBAL_THOUGHT_BUBBLES = 5;
27
28
  const FRONTMAN_OFFSET_X = (NODE_WIDTH * (FRONTMAN_SIZE_MULTIPLIER - 1)) / 2;
28
29
  const FRONTMAN_OFFSET_Y = (NODE_HEIGHT * (FRONTMAN_SIZE_MULTIPLIER - 1)) / 2;
@@ -60,34 +61,6 @@ const areInSameConversation = (conversations, sourceAgent, targetAgent) => {
60
61
  // Name for custom node
61
62
  const AGENT_NODE_TYPE_NAME = "agentNode";
62
63
  // #endregion: Constants
63
- /**
64
- * Returns the "origins" (node names) of the _immediate_ parents of a node in the agent network. Grandparents and
65
- * higher are not included.
66
- *
67
- * @param node Node ID for which to find parents
68
- * @param parentAgents Full list of parent agents in the network
69
- * @returns The IDs of the immediate parent nodes for the given node or empty array if no parents are found (frontman)
70
- */
71
- const getParents = (node, parentAgents) => {
72
- return parentAgents.filter((agent) => agent.tools.includes(node)).map((parentNode) => parentNode.origin);
73
- };
74
- // "Parent agents" are those that have tools, aka "child agents"
75
- const getParentAgents = (agentsInNetwork) => agentsInNetwork.length === 1 ? agentsInNetwork : agentsInNetwork.filter((agent) => agent.tools?.length > 0);
76
- // "Child agents" are those that are declared as tools by other agents
77
- const getChildAgents = (parentAgents) => {
78
- const childAgentsSet = new Set();
79
- parentAgents.forEach((agent) => {
80
- agent?.tools?.forEach((tool) => {
81
- childAgentsSet.add(tool);
82
- });
83
- });
84
- return childAgentsSet;
85
- };
86
- // Frontman is defined as the agent that has no parent. There should be only one in the graph.
87
- // Frontman is one of the parent agents.
88
- // There is no explicit field that tells us which agent is the frontman, so we determine it by checking
89
- // which parent agent does not appear in the set of child agents.
90
- const getFrontman = (parentAgents, childAgents) => parentAgents.find((agent) => !childAgents.has(agent.origin));
91
64
  // Generates the properties for an edge in the graph.
92
65
  // Common for both radial and linear layouts.
93
66
  const getEdgeProperties = (sourceId, targetId, sourceHandle, targetHandle, isAnimated) => {
@@ -102,16 +75,14 @@ const getEdgeProperties = (sourceId, targetId, sourceHandle, targetHandle, isAni
102
75
  type: isAnimated ? "plasmaEdge" : undefined,
103
76
  };
104
77
  };
105
- export const layoutRadial = (agentCounts, agentsInNetwork, currentConversations, // For plasma edges (live) and node highlighting
106
- isAwaitingLlm, isAgentNetworkDesignerMode, thoughtBubbleEdges, agentIconSuggestions = null, isTemporaryNetwork = false) => {
78
+ export const layoutRadial = ({ agentCounts, agentIconSuggestions = null, agentsInNetwork, currentConversations, isAgentNetworkDesignerMode, isAwaitingLlm, isTemporaryNetwork = false, thoughtBubbleEdges, useNativeNames, }) => {
107
79
  const nodesInNetwork = [];
108
80
  const edgesInNetwork = [];
109
81
  // Compute depth of each node using breadth-first traversal
110
82
  const nodeDepths = new Map();
111
83
  const queue = [];
112
84
  const parentAgents = getParentAgents(agentsInNetwork);
113
- const childAgents = getChildAgents(parentAgents);
114
- const frontman = getFrontman(parentAgents, childAgents);
85
+ const frontman = getFrontman(agentsInNetwork);
115
86
  if (frontman) {
116
87
  // Add the frontman node to the network
117
88
  queue.push({ id: frontman.origin, depth: 0 });
@@ -186,7 +157,7 @@ isAwaitingLlm, isAgentNetworkDesignerMode, thoughtBubbleEdges, agentIconSuggesti
186
157
  type: AGENT_NODE_TYPE_NAME,
187
158
  data: {
188
159
  agentCounts,
189
- agentName: cleanUpAgentName(nodeId),
160
+ agentName: useNativeNames ? nodeId : cleanUpAgentName(nodeId),
190
161
  depth,
191
162
  displayAs: agentsInNetwork.find((a) => a.origin === nodeId)?.display_as,
192
163
  // Use current conversations for node highlighting (cleared at end)
@@ -216,14 +187,12 @@ isAwaitingLlm, isAgentNetworkDesignerMode, thoughtBubbleEdges, agentIconSuggesti
216
187
  edgesInNetwork.push(...thoughtBubbleEdgesToAdd);
217
188
  return { nodes: nodesInNetwork, edges: edgesInNetwork };
218
189
  };
219
- export const layoutLinear = (agentCounts, agentsInNetwork, currentConversations, // For plasma edges (live) and node highlighting
220
- isAwaitingLlm, isAgentNetworkDesignerMode, thoughtBubbleEdges, agentIconSuggestions = null, isTemporaryNetwork = false) => {
190
+ export const layoutLinear = ({ agentCounts, agentIconSuggestions = null, agentsInNetwork, currentConversations, isAgentNetworkDesignerMode, isAwaitingLlm, isTemporaryNetwork = false, thoughtBubbleEdges, useNativeNames, }) => {
221
191
  const nodesInNetwork = [];
222
192
  const edgesInNetwork = [];
223
193
  // Do these calculations outside the loop for efficiency
224
194
  const parentAgents = getParentAgents(agentsInNetwork);
225
- const childAgents = getChildAgents(parentAgents);
226
- const frontman = getFrontman(parentAgents, childAgents);
195
+ const frontman = getFrontman(agentsInNetwork);
227
196
  agentsInNetwork.forEach(({ origin: nodeId }) => {
228
197
  const parentIds = getParents(nodeId, parentAgents);
229
198
  const isFrontman = frontman?.origin === nodeId;
@@ -232,7 +201,7 @@ isAwaitingLlm, isAgentNetworkDesignerMode, thoughtBubbleEdges, agentIconSuggesti
232
201
  type: AGENT_NODE_TYPE_NAME,
233
202
  data: {
234
203
  agentCounts,
235
- agentName: cleanUpAgentName(nodeId),
204
+ agentName: useNativeNames ? nodeId : cleanUpAgentName(nodeId),
236
205
  displayAs: agentsInNetwork.find((a) => a.origin === nodeId)?.display_as,
237
206
  // Use current conversations for node highlighting (cleared at end)
238
207
  getConversations: () => currentConversations,
@@ -0,0 +1,21 @@
1
+ import { ConnectivityInfo } from "../../../generated/neuro-san/NeuroSanClient.js";
2
+ export declare const getParentAgents: <T extends ConnectivityInfo>(agents: readonly T[]) => readonly T[];
3
+ /**
4
+ * Determines the frontman of an agent network — the single source of truth for "who is the frontman".
5
+ *
6
+ * The frontman is the agent with no parent: a "parent" agent (one that has tools) that is not itself
7
+ * listed as a tool by any other agent. A lone single-agent network is its own frontman. Returns
8
+ * undefined when no agent qualifies (e.g. a fully cyclic network); callers decide how to fall back.
9
+ *
10
+ * Consumed by the graph layouts (to seed the depth-0 node) and by the network import flow.
11
+ */
12
+ export declare const getFrontman: <T extends ConnectivityInfo>(agents: readonly T[]) => T | undefined;
13
+ /**
14
+ * Returns the "origins" (node names) of the _immediate_ parents of a node in the agent network. Grandparents and
15
+ * higher are not included.
16
+ *
17
+ * @param node Node ID for which to find parents
18
+ * @param parentAgents Full list of parent agents in the network
19
+ * @returns The IDs of the immediate parent nodes for the given node or empty array if no parents are found (frontman)
20
+ */
21
+ export declare const getParents: (node: string, parentAgents: readonly ConnectivityInfo[]) => string[];