@cognizant-ai-lab/ui-common 1.9.0 → 1.10.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 (28) hide show
  1. package/dist/components/AgentChat/ChatCommon/ChatCommon.js +5 -4
  2. package/dist/components/AgentChat/ChatCommon/Thinking.js +2 -2
  3. package/dist/components/AgentChat/Common/Utils.d.ts +0 -11
  4. package/dist/components/AgentChat/Common/Utils.js +0 -16
  5. package/dist/components/Common/Footer.js +4 -0
  6. package/dist/components/MultiAgentAccelerator/AgentFlow/AgentFlow.js +56 -24
  7. package/dist/components/MultiAgentAccelerator/AgentFlow/AgentNode.js +7 -5
  8. package/dist/components/MultiAgentAccelerator/AgentFlow/GraphLayouts.js +10 -7
  9. package/dist/components/MultiAgentAccelerator/AgentFlow/GraphStructure.d.ts +1 -1
  10. package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.js +9 -56
  11. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.d.ts +2 -0
  12. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +23 -31
  13. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.d.ts +0 -2
  14. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.js +7 -6
  15. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.d.ts +2 -2
  16. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.js +13 -13
  17. package/dist/components/MultiAgentAccelerator/TemporaryNetworks.d.ts +3 -17
  18. package/dist/components/MultiAgentAccelerator/TemporaryNetworks.js +17 -40
  19. package/dist/components/MultiAgentAccelerator/Tour/MainTourSteps.js +2 -2
  20. package/dist/components/MultiAgentAccelerator/const.d.ts +11 -11
  21. package/dist/components/MultiAgentAccelerator/const.js +22 -22
  22. package/dist/state/TemporaryNetworks.js +12 -21
  23. package/dist/tsconfig.build.tsbuildinfo +1 -1
  24. package/dist/utils/AgentName.d.ts +17 -0
  25. package/dist/utils/AgentName.js +46 -0
  26. package/package.json +1 -1
  27. package/dist/components/MultiAgentAccelerator/Sidebar/ImportNetworkModal.d.ts +0 -67
  28. package/dist/components/MultiAgentAccelerator/Sidebar/ImportNetworkModal.js +0 -585
@@ -50,16 +50,17 @@ import { sendLlmRequest, StreamingUnit } from "../../../controller/llm/LlmChat.j
50
50
  import { ChatMessageType } from "../../../generated/neuro-san/NeuroSanClient.js";
51
51
  import { useAgentChatHistoryStore } from "../../../state/ChatHistory.js";
52
52
  import { useSettingsStore } from "../../../state/Settings.js";
53
+ import { toDisplayName } from "../../../utils/AgentName.js";
53
54
  import { downloadFile, toSafeFilename } from "../../../utils/File.js";
54
55
  import { hasOnlyWhitespace } from "../../../utils/text.js";
55
56
  import { getZIndex } from "../../../utils/zIndexLayers.js";
56
57
  import { ConfirmationModal } from "../../Common/ConfirmationModal.js";
57
58
  import { AGENT_NETWORK_DESIGNER_ID } from "../../MultiAgentAccelerator/const.js";
58
59
  import { givesFinalAnswer, isLegacyAgentType } from "../Common/Types.js";
59
- import { chatMessageFromChunk, checkError, cleanUpAgentName, removeTrailingUuid } from "../Common/Utils.js";
60
+ import { chatMessageFromChunk, checkError } from "../Common/Utils.js";
60
61
  import { MicrophoneButton } from "../VoiceChat/MicrophoneButton.js";
61
62
  import { cleanupAndStopSpeechRecognition, setupSpeechRecognition } from "../VoiceChat/VoiceChat.js";
62
- //#endregion
63
+ //#endregion Types and Interfaces
63
64
  //#region Constants
64
65
  // Define fancy EMPTY constant to avoid linter error about using object literals as default props
65
66
  const EMPTY = {};
@@ -81,7 +82,7 @@ const EXPORT_ROLE_LABELS = {
81
82
  [MessageRole.Warning]: "Warning",
82
83
  [MessageRole.Error]: "Error",
83
84
  };
84
- //#endregion
85
+ //#endregion Constants
85
86
  /**
86
87
  * Helper function to convert a message role to a label for exporting chat history.
87
88
  * @param role The message role to convert
@@ -163,7 +164,7 @@ export const ChatCommon = ({ ref, ...props }) => {
163
164
  };
164
165
  // Keeps track of whether the agent completed its task
165
166
  const succeeded = useRef(false);
166
- const networkDisplayName = useMemo(() => (useNativeNames ? selectedNetwork : cleanUpAgentName(removeTrailingUuid(selectedNetwork))), [selectedNetwork, useNativeNames]);
167
+ const networkDisplayName = useMemo(() => toDisplayName(selectedNetwork, useNativeNames), [selectedNetwork, useNativeNames]);
167
168
  useEffect(() => {
168
169
  // Set up speech recognition
169
170
  const handlers = setupSpeechRecognition(setChatInput, setVoiceInputState, speechRecognitionRef);
@@ -5,8 +5,8 @@ import { useMemo } from "react";
5
5
  import ReactMarkdown from "react-markdown";
6
6
  import { MessageRole } from "./ConversationTurn.js";
7
7
  import { ChatMessageType } from "../../../generated/neuro-san/NeuroSanClient.js";
8
+ import { toDisplayName } from "../../../utils/AgentName.js";
8
9
  import { AccordionLite } from "../../Common/AccordionLite.js";
9
- import { cleanUpAgentName, removeTrailingUuid } from "../Common/Utils.js";
10
10
  /**
11
11
  * Set of message types that should be included in the "thinking" section.
12
12
  */
@@ -19,7 +19,7 @@ const THINKING_MESSAGE_TYPES = new Set([
19
19
  const DEFAULT_AGENT_NAME = "agent";
20
20
  const formatTurn = (turn, useNativeNames) => {
21
21
  const agentName = turn.agentName ?? DEFAULT_AGENT_NAME;
22
- const displayName = useNativeNames ? agentName : cleanUpAgentName(removeTrailingUuid(agentName));
22
+ const displayName = toDisplayName(agentName, useNativeNames);
23
23
  const headerLine = `**${displayName}**: ${turn.text || ""}`;
24
24
  const structureLine = turn.structure == null ? "" : `\n\`${JSON.stringify(turn.structure, null, 2)}\``;
25
25
  return `${headerLine}${structureLine}`;
@@ -3,14 +3,3 @@ export declare const KNOWN_MESSAGE_TYPES: ChatMessageType[];
3
3
  export declare const KNOWN_MESSAGE_TYPES_FOR_PLASMA: ChatMessageType[];
4
4
  export declare const chatMessageFromChunk: (chunk: string) => ChatMessage | null;
5
5
  export declare const checkError: (chatMessageJson: Record<string, unknown>) => string | null;
6
- /**
7
- * Strip a trailing canonical (hyphen-delimited) UUID appended to an agent name or
8
- * reservation_id, e.g. `copy_cat-hello_world-14ecb260-4389-44f3-afad-ea315dfa1966`.
9
- */
10
- export declare const removeTrailingUuid: (agentName: string) => string;
11
- /**
12
- * Convert FOO_BAR to more human "Foo Bar".
13
- * @param agentName Agent name in SNAKE_CASE format.
14
- * @returns User-friendly agent name.
15
- */
16
- export declare const cleanUpAgentName: (agentName: string) => string;
@@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */
16
16
  import { isEmpty } from "lodash-es";
17
- import startCase from "lodash-es/startCase";
18
17
  import { ChatMessageType } from "../../../generated/neuro-san/NeuroSanClient.js";
19
18
  // We ignore any messages that are not of these types
20
19
  export const KNOWN_MESSAGE_TYPES = [
@@ -60,18 +59,3 @@ export const checkError = (chatMessageJson) => {
60
59
  const { error, traceback, tool } = chatMessageJson;
61
60
  return `Error occurred. Error: "${error}", traceback: "${traceback}", tool: "${tool}"`;
62
61
  };
63
- /**
64
- * Strip a trailing canonical (hyphen-delimited) UUID appended to an agent name or
65
- * reservation_id, e.g. `copy_cat-hello_world-14ecb260-4389-44f3-afad-ea315dfa1966`.
66
- */
67
- export const removeTrailingUuid = (agentName) => {
68
- return agentName?.replace(/-[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/u, "");
69
- };
70
- /**
71
- * Convert FOO_BAR to more human "Foo Bar".
72
- * @param agentName Agent name in SNAKE_CASE format.
73
- * @returns User-friendly agent name.
74
- */
75
- export const cleanUpAgentName = (agentName) => {
76
- return startCase(agentName);
77
- };
@@ -7,6 +7,8 @@ import { navigateToUrl } from "../../utils/BrowserNavigation.js";
7
7
  const HeaderLine = styled("h6")({
8
8
  fontWeight: "bold",
9
9
  fontSize: "smaller",
10
+ margin: 0,
11
+ marginBottom: "0.9rem",
10
12
  });
11
13
  const LinkDivider = styled("div")({
12
14
  border: "1px solid var(--bs-accent3-medium)",
@@ -16,6 +18,7 @@ const LinkDivider = styled("div")({
16
18
  const FooterSectionHeader = styled("div")({
17
19
  display: "inline-flex",
18
20
  flexDirection: "column",
21
+ alignItems: "flex-start",
19
22
  marginTop: "0.1rem",
20
23
  });
21
24
  const FooterItemLink = styled("a")(({ theme }) => ({
@@ -31,6 +34,7 @@ const FooterItemLink = styled("a")(({ theme }) => ({
31
34
  const FooterContainer = styled("footer")({
32
35
  backgroundColor: "var(--bs-primary)",
33
36
  borderTop: "var(--bs-border-width) var(--bs-border-style) var(--bs-gray-lighter)",
37
+ alignItems: "flex-start",
34
38
  columnGap: "60px",
35
39
  display: "flex",
36
40
  justifyContent: "center",
@@ -33,7 +33,7 @@ import ToggleButtonGroup from "@mui/material/ToggleButtonGroup";
33
33
  import Tooltip from "@mui/material/Tooltip";
34
34
  import Typography from "@mui/material/Typography";
35
35
  import { applyNodeChanges, Background, ConnectionMode, ControlButton, Controls, ReactFlow, useReactFlow, useStore, } from "@xyflow/react";
36
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
36
+ import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
37
37
  import { AgentNode, NODE_HEIGHT, NODE_WIDTH } from "./AgentNode.js";
38
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";
39
39
  import { addThoughtBubbleEdge, layoutLinear, layoutRadial } from "./GraphLayouts.js";
@@ -53,7 +53,8 @@ import { ThoughtBubbleEdge } from "../ThoughtBubbles/ThoughtBubbleEdge.js";
53
53
  import { ThoughtBubbleOverlay } from "../ThoughtBubbles/ThoughtBubbleOverlay.js";
54
54
  //#endregion: Types
55
55
  //#region: Constants
56
- // Timeout for thought bubbles is set to 10 seconds
56
+ const AGENT_SAVE_TIMEOUT_MS = 60_000;
57
+ const DOCK_STREAM_TIMEOUT_MS = 120_000;
57
58
  const THOUGHT_BUBBLE_TIMEOUT_MS = 10_000;
58
59
  // How long the dock's status banner stays visible before auto-dismissing. Error banners persist until dismissed.
59
60
  // Exported for tests.
@@ -65,15 +66,17 @@ const DOCK_PROMPT_PLACEHOLDER = "Describe a change to the network";
65
66
  * Streams the Agent Network Designer endpoint with a natural-language prompt and the current
66
67
  * network definition, collecting any returned reservations.
67
68
  */
68
- const streamNetworkDesignerPrompt = async (neuroSanURL, signal, userPrompt, currentDefinition, agentNetworkName, currentUser) => {
69
+ const applyNetworkDesignerChanges = async (neuroSanURL, signal, userPrompt, currentDefinition, agentNetworkName, currentUser) => {
69
70
  let newNetworks = [];
70
71
  await sendChatQuery(neuroSanURL, signal, userPrompt, AGENT_NETWORK_DESIGNER_ID, (chunk) => {
71
72
  const chatMessage = chatMessageFromChunk(chunk);
72
- if (!chatMessage)
73
+ if (!chatMessage) {
73
74
  return;
75
+ }
74
76
  const reservations = extractReservations(chatMessage);
75
- if (reservations.length === 0)
77
+ if (reservations.length === 0) {
76
78
  return;
79
+ }
77
80
  const networkHocon = extractNetworkHocon(chatMessage);
78
81
  const agentNetworkNameFromMessage = chatMessage.sly_data?.[AGENT_NETWORK_NAME_KEY];
79
82
  const networkName = agentNetworkName ?? agentNetworkNameFromMessage;
@@ -319,26 +322,40 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
319
322
  detail: "Nothing was changed. Your prompt is restored below.",
320
323
  });
321
324
  }, [showDockBanner]);
322
- const handleNodeClick = useCallback((_event, node) => {
325
+ const openNodeEditor = useCallback((node) => {
323
326
  // Popup is only available for temporary networks.
324
327
  if (!isTemporaryNetwork)
325
328
  return;
326
329
  // Only llm_agent nodes support instructions/description editing.
327
330
  if (!isEditableAgent(node.data.displayAs))
328
331
  return;
329
- // Find the clicked agent's existing instructions and description from the temp network definition.
332
+ // Find the agent's existing instructions and description from the temp network definition.
330
333
  const currentTempNetwork = networkId
331
334
  ? tempNetworks.find((n) => n.agentInfo.agent_name === networkId)
332
335
  : undefined;
333
- const found = (currentTempNetwork?.agentNetworkDefinition ?? []).find((e) => e.origin === node.id);
336
+ const currentAgentDefinition = currentTempNetwork?.agentNetworkDefinition?.find((e) => e.origin === node.id);
334
337
  setSelectedAgent({
335
338
  agentId: node.id,
336
339
  agentName: node.data.agentName,
337
- initialInstructions: found?.instructions ?? "",
338
- initialDescription: found?.description ?? "",
340
+ initialInstructions: currentAgentDefinition?.instructions ?? "",
341
+ initialDescription: currentAgentDefinition?.description ?? "",
339
342
  });
340
343
  setIsPopupOpen(true);
341
344
  }, [tempNetworks, isTemporaryNetwork, networkId]);
345
+ const handleNodeClick = useCallback((_event, node) => openNodeEditor(node), [openNodeEditor]);
346
+ // ReactFlow makes nodes focusable and selects them on Enter, but it never fires onNodeClick from the keyboard.
347
+ // Route Enter on a focused node to the editor so keyboard users can open a node the same way a click does.
348
+ const handleNodeKeyDown = useCallback((event) => {
349
+ if (event.key !== "Enter")
350
+ return;
351
+ const target = event.target;
352
+ if (!(target instanceof HTMLElement))
353
+ return;
354
+ const nodeId = target.closest(".react-flow__node")?.dataset["id"];
355
+ const node = nodeId ? nodes.find((n) => n.id === nodeId) : undefined;
356
+ if (node)
357
+ openNodeEditor(node);
358
+ }, [nodes, openNodeEditor]);
342
359
  const handlePopupClose = useCallback(() => {
343
360
  // If a save is in-flight, abort it immediately so the stream doesn't hang.
344
361
  saveAbortControllerRef.current?.abort();
@@ -350,7 +367,7 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
350
367
  * Applies the networks returned by the designer: upserts them and triggers navigation if needed.
351
368
  * Returns true when a matching reservation was applied, false (and surfaces an error banner) otherwise.
352
369
  */
353
- const applyNetworkSaveResult = useCallback((newNetworksFromSave, currentAgentNetworkName) => {
370
+ const saveUpdates = useCallback((newNetworksFromSave, currentAgentNetworkName) => {
354
371
  if (newNetworksFromSave.length === 0) {
355
372
  showDockBanner({
356
373
  severity: "error",
@@ -359,6 +376,9 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
359
376
  });
360
377
  return false;
361
378
  }
379
+ // Find the returned reservation that stands in for the current network (matched by name). When
380
+ // present, persist every returned network, then carry the open network's chat history over to the
381
+ // replacement and notify the parent so it can navigate to the new reservation.
362
382
  const replacement = newNetworksFromSave.find((n) => n.agentNetworkName === currentAgentNetworkName);
363
383
  if (replacement) {
364
384
  useTempNetworksStore.getState().upsertTempNetworks(newNetworksFromSave);
@@ -377,7 +397,8 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
377
397
  return false;
378
398
  }, [networkId, onNetworkReplaced, showDockBanner]);
379
399
  const handleDockApply = useCallback(async () => {
380
- if (!dockPrompt.trim() || !neuroSanURL || !currentUser)
400
+ const readyToApplyEdit = Boolean(dockPrompt.trim() && neuroSanURL && currentUser);
401
+ if (!readyToApplyEdit)
381
402
  return;
382
403
  const currentTempNetwork = networkId
383
404
  ? tempNetworks.find((n) => n.agentInfo.agent_name === networkId)
@@ -390,11 +411,11 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
390
411
  const timeoutId = setTimeout(() => {
391
412
  hasTimedOut = true;
392
413
  controller.abort();
393
- }, 120_000); // 2 min timeout
414
+ }, DOCK_STREAM_TIMEOUT_MS);
394
415
  try {
395
- const newNetworks = await streamNetworkDesignerPrompt(neuroSanURL, controller.signal, dockPrompt, currentDefinition, currentTempNetwork?.agentNetworkName, currentUser);
396
- const applied = applyNetworkSaveResult(newNetworks, currentTempNetwork?.agentNetworkName);
397
- if (applied) {
416
+ const newNetworks = await applyNetworkDesignerChanges(neuroSanURL, controller.signal, dockPrompt, currentDefinition, currentTempNetwork?.agentNetworkName, currentUser);
417
+ const appliedSuccess = saveUpdates(newNetworks, currentTempNetwork?.agentNetworkName);
418
+ if (appliedSuccess) {
398
419
  setDockPrompt("");
399
420
  showDockBanner({
400
421
  severity: "success",
@@ -423,7 +444,7 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
423
444
  dockAbortControllerRef.current = null;
424
445
  setIsDockStreaming(false);
425
446
  }
426
- }, [applyNetworkSaveResult, currentUser, dockPrompt, networkId, neuroSanURL, showDockBanner, tempNetworks]);
447
+ }, [saveUpdates, currentUser, dockPrompt, networkId, neuroSanURL, showDockBanner, tempNetworks]);
427
448
  const handleExitEditMode = useCallback(() => {
428
449
  if (isDockStreaming) {
429
450
  dockAbortControllerRef.current?.abort();
@@ -432,6 +453,18 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
432
453
  }
433
454
  onExitEditMode?.();
434
455
  }, [isDockStreaming, onExitEditMode]);
456
+ // Pressing Escape exits edit mode, mirroring the explicit exit button. Skip while the
457
+ // node popup is open so Escape closes the popup first rather than the whole edit mode.
458
+ useEffect(() => {
459
+ if (!isEditMode || isPopupOpen)
460
+ return undefined;
461
+ const handleEscape = (e) => {
462
+ if (e.key === "Escape")
463
+ handleExitEditMode();
464
+ };
465
+ document.addEventListener("keydown", handleEscape);
466
+ return () => document.removeEventListener("keydown", handleEscape);
467
+ }, [isEditMode, isPopupOpen, handleExitEditMode]);
435
468
  const handlePopupSave = useCallback(async (agentName, instructionsText, descriptionText) => {
436
469
  if (!selectedAgent)
437
470
  return;
@@ -441,11 +474,11 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
441
474
  : undefined;
442
475
  // Produce a new array with the saved agent's fields updated; all other entries pass through unchanged.
443
476
  const currentDefinitions = currentTempNetwork?.agentNetworkDefinition ?? [];
444
- const updated = currentDefinitions.map((entry) => entry.origin === selectedAgent.agentId
477
+ const updatedDefinitions = currentDefinitions.map((entry) => entry.origin === selectedAgent.agentId
445
478
  ? { ...entry, instructions: instructionsText, description: descriptionText }
446
479
  : entry);
447
480
  if (networkId) {
448
- updateTempNetworkDefinition(networkId, updated);
481
+ updateTempNetworkDefinition(networkId, updatedDefinitions);
449
482
  }
450
483
  if (!onSaveAgent) {
451
484
  setIsPopupOpen(false);
@@ -454,10 +487,9 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
454
487
  setIsSavingAgent(true);
455
488
  const saveController = new AbortController();
456
489
  saveAbortControllerRef.current = saveController;
457
- const saveTimeoutId = setTimeout(() => saveController.abort(new DOMException("Save timed out", "TimeoutError")), 60_000 // 1 min timeout
458
- );
490
+ const saveTimeoutId = setTimeout(() => saveController.abort(new DOMException("Save timed out", "TimeoutError")), AGENT_SAVE_TIMEOUT_MS);
459
491
  try {
460
- await onSaveAgent(agentName, updated, currentTempNetwork?.agentNetworkName, saveController.signal);
492
+ await onSaveAgent(agentName, updatedDefinitions, currentTempNetwork?.agentNetworkName, saveController.signal);
461
493
  }
462
494
  catch (e) {
463
495
  console.error(`Error saving network ${agentName}. See onSaveAgent implementation for details.`, e);
@@ -643,7 +675,7 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
643
675
  borderBottom: "1px solid divider",
644
676
  fill: theme.palette.text.primary,
645
677
  },
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: {
678
+ }, 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, onKeyDown: handleNodeKeyDown, 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
679
  backdropFilter: "blur(8px)",
648
680
  backgroundColor: alpha(theme.palette.background.paper, 0.2),
649
681
  borderTop: `2px solid ${theme.palette.primary.main}`,
@@ -681,7 +713,7 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
681
713
  px: 1,
682
714
  py: 0.5,
683
715
  alignItems: "center",
684
- }, children: [_jsx(TextField, { fullWidth: true, placeholder: DOCK_PROMPT_PLACEHOLDER, variant: "outlined", size: "small", value: dockPrompt, onChange: (e) => setDockPrompt(e.target.value), onKeyDown: (e) => {
716
+ }, children: [_jsx(TextField, { autoFocus: true, fullWidth: true, placeholder: DOCK_PROMPT_PLACEHOLDER, variant: "outlined", size: "small", value: dockPrompt, onChange: (e) => setDockPrompt(e.target.value), onKeyDown: (e) => {
685
717
  if (e.key === "Enter" && !e.shiftKey) {
686
718
  e.preventDefault();
687
719
  void handleDockApply();
@@ -27,7 +27,6 @@ import { keyframes, styled, useTheme } from "@mui/material/styles";
27
27
  import Tooltip from "@mui/material/Tooltip";
28
28
  import Typography from "@mui/material/Typography";
29
29
  import { Handle, Position } from "@xyflow/react";
30
- import { useState } from "react";
31
30
  import { usePalette, useSettingsStore } from "../../../state/Settings.js";
32
31
  import { isLightColor } from "../../../Theme/Theme.js";
33
32
  import { getZIndex } from "../../../utils/zIndexLayers.js";
@@ -154,7 +153,6 @@ export const AgentNode = (props) => {
154
153
  const glowColor = isLightColor(theme.palette.background.default)
155
154
  ? theme.palette.common.black
156
155
  : theme.palette.common.white;
157
- const [isHovered, setIsHovered] = useState(false);
158
156
  // Insert zero-width spaces into the agent name to give the browser hints where to wrap.
159
157
  const wrappedAgentName = agentName
160
158
  // Allow wrap after underscore
@@ -165,7 +163,7 @@ export const AgentNode = (props) => {
165
163
  .replaceAll(/(?<abbr>[A-Z])(?<word>[A-Z][a-z])/gu, `$<abbr>${ZERO_WIDTH_SPACE}$<word>`);
166
164
  const height = NODE_HEIGHT * (isFrontman ? FRONTMAN_SIZE_MULTIPLIER : 1.0);
167
165
  const width = height;
168
- const getEditButton = () => (_jsx(Tooltip, { title: "Edit", placement: "top", disableInteractive: true, children: _jsx(IconButton, { sx: {
166
+ const getEditButton = () => (_jsx(Tooltip, { title: "Edit", placement: "top", disableInteractive: true, children: _jsx(IconButton, { className: "agent-node-edit-icon", "aria-hidden": true, tabIndex: -1, sx: {
169
167
  position: "absolute",
170
168
  top: 0,
171
169
  right: 0,
@@ -197,13 +195,17 @@ export const AgentNode = (props) => {
197
195
  width,
198
196
  zIndex: getZIndex(1, theme),
199
197
  }, children: wrappedAgentName }) }));
200
- const getNode = () => (_jsxs(AnimatedNode, { id: agentId, "data-testid": agentId, glowColor: glowColor, isActive: isActiveAgent, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), sx: {
198
+ const getNode = () => (_jsxs(AnimatedNode, { id: agentId, "data-testid": agentId, glowColor: glowColor, isActive: isActiveAgent, sx: {
201
199
  backgroundColor,
202
200
  color,
203
201
  cursor: isEditable ? "pointer" : "grab",
204
202
  height,
205
203
  width,
206
204
  zIndex: getZIndex(1, theme),
207
- }, children: [getDisplayAsIcon(), isHovered && isEditable && getEditButton(), _jsx(Handle, { id: `${agentId}-left-handle`, position: Position.Left, type: "source", style: { visibility: handleVisibility } }), _jsx(Handle, { id: `${agentId}-right-handle`, position: Position.Right, type: "source", style: { visibility: handleVisibility } }), _jsx(Handle, { id: `${agentId}-top-handle`, position: Position.Top, type: "source", style: { visibility: handleVisibility } }), _jsx(Handle, { id: `${agentId}-bottom-handle`, position: Position.Bottom, type: "source", style: { visibility: handleVisibility } })] }));
205
+ // Reveal the edit icon on hover, or when a keyboard user tabs to the node.
206
+ "& .agent-node-edit-icon": { opacity: 0, transition: "opacity 0.15s ease-in-out" },
207
+ "&:hover .agent-node-edit-icon": { opacity: 1 },
208
+ ".react-flow__node:focus-visible & .agent-node-edit-icon": { opacity: 1 },
209
+ }, children: [getDisplayAsIcon(), isEditable && getEditButton(), _jsx(Handle, { id: `${agentId}-left-handle`, position: Position.Left, type: "source", style: { visibility: handleVisibility } }), _jsx(Handle, { id: `${agentId}-right-handle`, position: Position.Right, type: "source", style: { visibility: handleVisibility } }), _jsx(Handle, { id: `${agentId}-top-handle`, position: Position.Top, type: "source", style: { visibility: handleVisibility } }), _jsx(Handle, { id: `${agentId}-bottom-handle`, position: Position.Bottom, type: "source", style: { visibility: handleVisibility } })] }));
208
210
  return (_jsxs(_Fragment, { children: [getNode(), getNodeName()] }));
209
211
  };
@@ -20,7 +20,8 @@ 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 { cleanUpAgentName, KNOWN_MESSAGE_TYPES_FOR_PLASMA } from "../../AgentChat/Common/Utils.js";
23
+ import { cleanUpAgentName } from "../../../utils/AgentName.js";
24
+ import { KNOWN_MESSAGE_TYPES_FOR_PLASMA } from "../../AgentChat/Common/Utils.js";
24
25
  import { BASE_RADIUS, DEFAULT_FRONTMAN_X_POS, DEFAULT_FRONTMAN_Y_POS, LEVEL_SPACING } from "../const.js";
25
26
  import { getFrontman, getParentAgents, getParents } from "./GraphStructure.js";
26
27
  import { isEditableAgent } from "../TemporaryNetworks.js";
@@ -75,6 +76,8 @@ const getEdgeProperties = (sourceId, targetId, sourceHandle, targetHandle, isAni
75
76
  type: isAnimated ? "plasmaEdge" : undefined,
76
77
  };
77
78
  };
79
+ // An agent is editable if it belongs to a temporary network and is of an editable type.
80
+ const isNodeEditable = (displayAs, isTemporaryNetwork) => isTemporaryNetwork && isEditableAgent(displayAs);
78
81
  export const layoutRadial = ({ agentCounts, agentIconSuggestions = null, agentsInNetwork, currentConversations, isAgentNetworkDesignerMode, isAwaitingLlm, isTemporaryNetwork = false, thoughtBubbleEdges, useNativeNames, }) => {
79
82
  const nodesInNetwork = [];
80
83
  const edgesInNetwork = [];
@@ -119,6 +122,7 @@ export const layoutRadial = ({ agentCounts, agentIconSuggestions = null, agentsI
119
122
  const x = DEFAULT_FRONTMAN_X_POS + radius * Math.cos(angle);
120
123
  const y = DEFAULT_FRONTMAN_Y_POS + radius * Math.sin(angle);
121
124
  const isFrontman = frontman?.origin === nodeId;
125
+ const displayAs = agentsInNetwork.find((a) => a.origin === nodeId)?.display_as;
122
126
  const parentNodes = getParents(nodeId, parentAgents);
123
127
  // Create an edge from each parent node to this node
124
128
  for (const parentNode of parentNodes) {
@@ -159,13 +163,12 @@ export const layoutRadial = ({ agentCounts, agentIconSuggestions = null, agentsI
159
163
  agentCounts,
160
164
  agentName: useNativeNames ? nodeId : cleanUpAgentName(nodeId),
161
165
  depth,
162
- displayAs: agentsInNetwork.find((a) => a.origin === nodeId)?.display_as,
166
+ displayAs,
163
167
  // Use current conversations for node highlighting (cleared at end)
164
168
  getConversations: () => currentConversations,
165
169
  isAwaitingLlm,
166
170
  agentIconSuggestion: agentIconSuggestions?.[nodeId],
167
- isEditable: isTemporaryNetwork &&
168
- isEditableAgent(agentsInNetwork.find((a) => a.origin === nodeId)?.display_as),
171
+ isEditable: isNodeEditable(displayAs, isTemporaryNetwork),
169
172
  },
170
173
  // position: allow for Frontman being larger
171
174
  position: isFrontman
@@ -193,7 +196,7 @@ export const layoutLinear = ({ agentCounts, agentIconSuggestions = null, agentsI
193
196
  // Do these calculations outside the loop for efficiency
194
197
  const parentAgents = getParentAgents(agentsInNetwork);
195
198
  const frontman = getFrontman(agentsInNetwork);
196
- agentsInNetwork.forEach(({ origin: nodeId }) => {
199
+ agentsInNetwork.forEach(({ display_as: displayAs, origin: nodeId }) => {
197
200
  const parentIds = getParents(nodeId, parentAgents);
198
201
  const isFrontman = frontman?.origin === nodeId;
199
202
  nodesInNetwork.push({
@@ -202,13 +205,13 @@ export const layoutLinear = ({ agentCounts, agentIconSuggestions = null, agentsI
202
205
  data: {
203
206
  agentCounts,
204
207
  agentName: useNativeNames ? nodeId : cleanUpAgentName(nodeId),
205
- displayAs: agentsInNetwork.find((a) => a.origin === nodeId)?.display_as,
208
+ displayAs,
206
209
  // Use current conversations for node highlighting (cleared at end)
207
210
  getConversations: () => currentConversations,
208
211
  isAwaitingLlm,
209
212
  depth: undefined, // Depth will be computed later,
210
213
  agentIconSuggestion: agentIconSuggestions?.[nodeId],
211
- isEditable: isTemporaryNetwork && isEditableAgent(agentsInNetwork.find((a) => a.origin === nodeId)?.display_as),
214
+ isEditable: isNodeEditable(displayAs, isTemporaryNetwork),
212
215
  },
213
216
  position: isFrontman ? { x: DEFAULT_FRONTMAN_X_POS, y: DEFAULT_FRONTMAN_Y_POS } : { x: 0, y: 0 },
214
217
  style: {
@@ -9,7 +9,7 @@ export declare const getParentAgents: <T extends ConnectivityInfo>(agents: reado
9
9
  *
10
10
  * Consumed by the graph layouts (to seed the depth-0 node) and by the network import flow.
11
11
  */
12
- export declare const getFrontman: <T extends ConnectivityInfo>(agents: readonly T[]) => T | undefined;
12
+ export declare const getFrontman: (agents: readonly ConnectivityInfo[]) => ConnectivityInfo | undefined;
13
13
  /**
14
14
  * Returns the "origins" (node names) of the _immediate_ parents of a node in the agent network. Grandparents and
15
15
  * higher are not included.
@@ -15,11 +15,8 @@ See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  */
17
17
  import StopCircle from "@mui/icons-material/StopCircle";
18
- import Backdrop from "@mui/material/Backdrop";
19
18
  import Box from "@mui/material/Box";
20
- import CircularProgress from "@mui/material/CircularProgress";
21
19
  import Grid from "@mui/material/Grid";
22
- import Paper from "@mui/material/Paper";
23
20
  import Slide from "@mui/material/Slide";
24
21
  import { useTheme } from "@mui/material/styles";
25
22
  import Tooltip from "@mui/material/Tooltip";
@@ -32,20 +29,20 @@ import { getUpdatedAgentCounts } from "./AgentCounts.js";
32
29
  import { AgentFlow } from "./AgentFlow/AgentFlow.js";
33
30
  import { extractAgentNetworkDesignerProgress } from "./AgentNetworkDesigner.js";
34
31
  import { AGENT_NETWORK_DEFINITION_KEY, AGENT_NETWORK_DESIGNER_ID, AGENT_NETWORK_HOCON, AGENT_NETWORK_NAME_KEY, EXPIRED_NETWORKS_CHECK_INTERVAL_MS, GRACE_PERIOD_MS, SHOW_TOUR_DELAY_MS, TRIGGER_APP_TOUR_EVENT_NAME, } from "./const.js";
35
- import { ImportNetworkModal } from "./Sidebar/ImportNetworkModal.js";
36
32
  import { Sidebar } from "./Sidebar/Sidebar.js";
37
- import { extractTemporaryNetworksFromMessage, IMPORT_FAILURE_DETAIL, importNetworkFromJson, isTemporaryNetwork, notifySaveError, streamNetworkDesignerUpsert, } from "./TemporaryNetworks.js";
33
+ import { extractTemporaryNetworksFromMessage, isTemporaryNetwork, notifySaveError, streamNetworkDesignerUpsert, } from "./TemporaryNetworks.js";
38
34
  import { MAIN_TOUR_STEPS } from "./Tour/MainTourSteps.js";
39
35
  import { getAgentFunction, getAgentNetworks, getConnectivity } from "../../controller/agent/Agent.js";
40
36
  import { getAgentIconSuggestions, getNetworkIconSuggestions } from "../../controller/agent/IconSuggestions.js";
41
37
  import { useAgentChatHistoryStore } from "../../state/ChatHistory.js";
42
38
  import { useTempNetworksStore } from "../../state/TemporaryNetworks.js";
43
39
  import { TourPromptState, useTourStore } from "../../state/Tour.js";
40
+ import { toDisplayName } from "../../utils/AgentName.js";
44
41
  import { getZIndex } from "../../utils/zIndexLayers.js";
45
42
  import { ChatCommon } from "../AgentChat/ChatCommon/ChatCommon.js";
46
43
  import { LlmChatButton } from "../AgentChat/Common/LlmChatButton.js";
47
44
  import { isLegacyAgentType } from "../AgentChat/Common/Types.js";
48
- import { chatMessageFromChunk, cleanUpAgentName, removeTrailingUuid } from "../AgentChat/Common/Utils.js";
45
+ import { chatMessageFromChunk } from "../AgentChat/Common/Utils.js";
49
46
  import { ConfirmationModal, StyledButton } from "../Common/ConfirmationModal.js";
50
47
  import { MUIAlert } from "../Common/MUIAlert.js";
51
48
  import { MUIDialog } from "../Common/MUIDialog.js";
@@ -79,8 +76,6 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
79
76
  const temporaryNetworks = useTempNetworksStore((state) => state.tempNetworks);
80
77
  // Track newly added temp networks so we can highlight them
81
78
  const [newlyAddedTemporaryNetworks, setNewlyAddedTemporaryNetworks] = useState(new Set());
82
- // True while a file import is in-flight (after modal confirm, before the new network appears)
83
- const [isImporting, setIsImporting] = useState(false);
84
79
  const [networkIconSuggestions, setNetworkIconSuggestions] = useState({});
85
80
  const [agentsInNetwork, setAgentsInNetwork] = useState([]);
86
81
  const [sampleQueries, setSampleQueries] = useState([]);
@@ -90,7 +85,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
90
85
  const [agentIconSuggestions, setAgentIconSuggestions] = useState(null);
91
86
  const [selectedNetwork, setSelectedNetwork] = useState(null);
92
87
  const [networkDescription, setNetworkDescription] = useState("");
93
- const networkDisplayName = useMemo(() => (useNativeNames ? selectedNetwork : cleanUpAgentName(removeTrailingUuid(selectedNetwork))), [selectedNetwork, useNativeNames]);
88
+ const networkDisplayName = useMemo(() => toDisplayName(selectedNetwork, useNativeNames), [selectedNetwork, useNativeNames]);
94
89
  // LLM providers for which BYOK keys are supported by the current network
95
90
  const [supportedByokProviders, setSupportedByokProviders] = useState(new Set());
96
91
  const neuroSanURL = useSettingsStore((state) => state.settings.externalServices.neuroSanUrl) ?? defaultNeuroSanUrl;
@@ -107,7 +102,6 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
107
102
  // State to hold thought bubble edges - avoids duplicates across layout recalculations
108
103
  const [thoughtBubbleEdges, setThoughtBubbleEdges] = useState(new Map());
109
104
  const [confirmationModalOpen, setConfirmationModalOpen] = useState(false);
110
- const [importModalOpen, setImportModalOpen] = useState(false);
111
105
  const [tourModalOpen, setTourModalOpen] = useState(false);
112
106
  const [haveShownTourModal, setHaveShownTourModal] = useState(false);
113
107
  // Memoized key for agent names to trigger icon suggestion updates when the set of agents changes, not just
@@ -167,9 +161,8 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
167
161
  // progresses. We read it back here to find the matching temp network and override agent_network_definition in the
168
162
  // outgoing request — leaving what's in IndexedDB untouched.
169
163
  const designerSlyData = useAgentChatHistoryStore((state) => state.history[AGENT_NETWORK_DESIGNER_ID]?.slyData);
170
- const designerNetworkName = isNetworkDesignerMode
171
- ? designerSlyData?.[AGENT_NETWORK_NAME_KEY]
172
- : undefined;
164
+ const designerNetworkNameValue = designerSlyData?.[AGENT_NETWORK_NAME_KEY];
165
+ const designerNetworkName = isNetworkDesignerMode && typeof designerNetworkNameValue === "string" ? designerNetworkNameValue : undefined;
173
166
  const designerTempNetwork = designerNetworkName
174
167
  ? temporaryNetworks.find((n) => n.agentNetworkName === designerNetworkName)
175
168
  : undefined;
@@ -480,35 +473,6 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
480
473
  notifySaveError(agentName, e);
481
474
  }
482
475
  }, [neuroSanURL, username, selectedNetwork]);
483
- /**
484
- * Handles an import from the ImportNetworkModal: converts the JSON content to a
485
- * network definition, streams it through the network designer to obtain a reservation,
486
- * then upserts the result into the temporary-networks store and navigates to the new network.
487
- */
488
- const handleImportNetwork = useCallback(async (agentNetworkName, content) => {
489
- setIsImporting(true);
490
- try {
491
- const result = await importNetworkFromJson(content, agentNetworkName, neuroSanURL, username);
492
- if ("failure" in result) {
493
- sendNotification(NotificationType.error, `Failed to import "${agentNetworkName}".`, IMPORT_FAILURE_DETAIL[result.failure]);
494
- return;
495
- }
496
- useTempNetworksStore.getState().upsertTempNetworks(result.networks);
497
- // An import defines a single network, so the designer returns one reservation. Highlight and
498
- // navigate to it; any further entries (none expected) are still upserted above.
499
- const newNetworkName = result.networks[0]?.agentInfo.agent_name;
500
- if (newNetworkName) {
501
- setNewlyAddedTemporaryNetworks(new Set([newNetworkName]));
502
- changeSelectedNetwork(newNetworkName);
503
- }
504
- }
505
- catch (e) {
506
- notifySaveError(agentNetworkName, e);
507
- }
508
- finally {
509
- setIsImporting(false);
510
- }
511
- }, [changeSelectedNetwork, neuroSanURL, username]);
512
476
  const onStreamingStarted = useCallback(() => {
513
477
  // Reset agent counts
514
478
  setAgentCounts(new Map());
@@ -582,7 +546,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
582
546
  setIsStreaming(true);
583
547
  }, children: _jsx(Grid, { id: "multi-agent-accelerator-grid-sidebar", size: enableZenMode && isStreaming ? 0 : 3.25, sx: {
584
548
  height: "100%",
585
- }, children: _jsx(Sidebar, { id: "multi-agent-accelerator-sidebar", isAwaitingLlm: isAwaitingLlm, isImporting: isImporting, networkIconSuggestions: networkIconSuggestions, networks: networks, neuroSanServerURL: neuroSanURL, newlyAddedTemporaryNetworks: newlyAddedTemporaryNetworks, onDeleteNetwork: handleDeleteNetwork, onEditNetwork: handleEditNetwork, onImportClick: () => setImportModalOpen(true), setSelectedNetwork: (newNetwork) => changeSelectedNetwork(newNetwork), temporaryNetworks: temporaryNetworks }) }) }));
549
+ }, children: _jsx(Sidebar, { id: "multi-agent-accelerator-sidebar", isAwaitingLlm: isAwaitingLlm, networkIconSuggestions: networkIconSuggestions, networks: networks, neuroSanServerURL: neuroSanURL, newlyAddedTemporaryNetworks: newlyAddedTemporaryNetworks, onDeleteNetwork: handleDeleteNetwork, onEditNetwork: handleEditNetwork, setSelectedNetwork: (newNetwork) => changeSelectedNetwork(newNetwork), temporaryNetworks: temporaryNetworks }) }) }));
586
550
  };
587
551
  const getCenterPanel = () => {
588
552
  return (_jsx(Grid, { id: "multi-agent-accelerator-grid-agent-flow", size: enableZenMode && isStreaming ? 18 : 8.25, sx: {
@@ -620,7 +584,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
620
584
  },
621
585
  }, children: _jsx(StopCircle, { fontSize: "large", id: "stop-button-icon" }) }) }) })) }));
622
586
  };
623
- const getDeleteNetworkConfirmationModal = () => confirmationModalOpen ? (_jsx(ConfirmationModal, { id: "delete-network-confirmation-modal", content: `The network "${cleanUpAgentName(removeTrailingUuid(networkToBeDeleted))}" will be deleted. ` +
587
+ const getDeleteNetworkConfirmationModal = () => confirmationModalOpen ? (_jsx(ConfirmationModal, { id: "delete-network-confirmation-modal", content: `The network "${toDisplayName(networkToBeDeleted)}" will be deleted. ` +
624
588
  "This action cannot be undone. Are you sure you want to proceed?", handleCancel: () => {
625
589
  setConfirmationModalOpen(false);
626
590
  setNetworkToBeDeleted(null);
@@ -635,17 +599,6 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
635
599
  setNetworkToBeDeleted(null);
636
600
  setConfirmationModalOpen(false);
637
601
  }, title: "Delete Network" })) : null;
638
- const getImportNetworkModal = () => (_jsx(ImportNetworkModal, { existingNetworkNames: temporaryNetworks.map((n) => n.agentNetworkName), isOpen: importModalOpen, onClose: () => setImportModalOpen(false), onImport: handleImportNetwork }));
639
- // Blocking overlay shown while an imported network is being created on the backend.
640
- const getImportBackdrop = () => (_jsx(Backdrop, { id: "multi-agent-accelerator-import-backdrop", "data-testid": "multi-agent-accelerator-import-backdrop", open: isImporting, sx: { zIndex: getZIndex(3, theme) }, children: _jsxs(Paper, { elevation: 6, role: "status", "aria-live": "polite", sx: {
641
- display: "flex",
642
- alignItems: "center",
643
- gap: 2,
644
- px: 4,
645
- py: 2.5,
646
- borderRadius: 2,
647
- maxWidth: 480,
648
- }, children: [_jsx(CircularProgress, { id: "multi-agent-accelerator-import-spinner", size: 24 }), _jsx(Typography, { id: "multi-agent-accelerator-import-title", variant: "body1", sx: { fontWeight: "bold" }, children: "Importing network..." })] }) }));
649
602
  const getTourModal = () => tourModalOpen && (_jsx(MUIDialog, { contentSx: { fontSize: "0.8rem", minWidth: "550px", paddingTop: "0" }, footer: _jsxs(_Fragment, { children: [_jsx(StyledButton, { id: "tour-dont-show-again", onClick: () => {
650
603
  setTourStatus(TourPromptState.DontShowAgain);
651
604
  dismissTourModal();
@@ -694,7 +647,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
694
647
  textAlign: "center",
695
648
  },
696
649
  }, children: _jsxs(Typography, { sx: { fontSize: "larger" }, children: ["API key(s) required for at least one of these providers:", " ", _jsx("strong", { children: [...getMissingApiKeys()].join(", ") }), ". Please add the required key(s) in \"Settings\" to use this Network."] }) }));
697
- return (_jsxs(_Fragment, { children: [getMissingApiKeysAlert(), Tour, getTourModal(), getProgressPopper(), getDeleteNetworkConfirmationModal(), getImportNetworkModal(), getImportBackdrop(), _jsxs(Grid, { id: "multi-agent-accelerator-grid", container: true, columns: 18, sx: {
650
+ return (_jsxs(_Fragment, { children: [getMissingApiKeysAlert(), Tour, getTourModal(), getProgressPopper(), getDeleteNetworkConfirmationModal(), _jsxs(Grid, { id: "multi-agent-accelerator-grid", container: true, columns: 18, sx: {
698
651
  display: "flex",
699
652
  flex: 1,
700
653
  height: "85%",
@@ -1,5 +1,7 @@
1
1
  import { TreeItemProps } from "@mui/x-tree-view/TreeItem";
2
2
  import { FC } from "react";
3
+ export declare const EDIT_NETWORK_LABEL = "Edit this network";
4
+ export declare const DOWNLOAD_NETWORK_DEFINITION_LABEL = "Download network definition";
3
5
  export interface AgentNetworkNodeProps extends TreeItemProps {
4
6
  readonly onDeleteNetwork?: (network: string, isExpired: boolean) => void;
5
7
  readonly onEditNetwork?: (network: string) => void;