@cognizant-ai-lab/ui-common 1.4.2 → 1.5.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 (67) hide show
  1. package/dist/components/AgentChat/ChatCommon/AgentConnectivity.d.ts +14 -0
  2. package/dist/components/AgentChat/ChatCommon/AgentConnectivity.js +23 -0
  3. package/dist/components/AgentChat/{ChatCommon.d.ts → ChatCommon/ChatCommon.d.ts} +8 -4
  4. package/dist/components/AgentChat/{ChatCommon.js → ChatCommon/ChatCommon.js} +318 -307
  5. package/dist/components/AgentChat/ChatCommon/ChatHistory.d.ts +17 -0
  6. package/dist/components/AgentChat/ChatCommon/ChatHistory.js +27 -0
  7. package/dist/components/AgentChat/{ControlButtons.d.ts → ChatCommon/ControlButtons.d.ts} +1 -1
  8. package/dist/components/AgentChat/ChatCommon/ControlButtons.js +26 -0
  9. package/dist/components/AgentChat/{FormattedMarkdown.js → ChatCommon/FormattedMarkdown.js} +1 -1
  10. package/dist/components/AgentChat/ChatCommon/SampleQueries.d.ts +16 -0
  11. package/dist/components/AgentChat/ChatCommon/SampleQueries.js +29 -0
  12. package/dist/components/AgentChat/{SendButton.js → ChatCommon/SendButton.js} +1 -1
  13. package/dist/components/AgentChat/ChatCommon/UserQueryDisplay.d.ts +7 -0
  14. package/dist/components/AgentChat/{UserQueryDisplay.js → ChatCommon/UserQueryDisplay.js} +4 -3
  15. package/dist/components/AgentChat/{LlmChatButton.d.ts → Common/LlmChatButton.d.ts} +2 -2
  16. package/dist/components/AgentChat/{Utils.d.ts → Common/Utils.d.ts} +1 -1
  17. package/dist/components/AgentChat/{Utils.js → Common/Utils.js} +2 -1
  18. package/dist/components/AgentChat/VoiceChat/MicrophoneButton.js +1 -1
  19. package/dist/components/ChatBot/ChatBot.js +2 -2
  20. package/dist/components/Common/CustomerLogo.js +1 -1
  21. package/dist/components/Common/LlmChatOptionsButton.d.ts +1 -1
  22. package/dist/components/Common/MUIDialog.d.ts +1 -0
  23. package/dist/components/Common/MUIDialog.js +2 -2
  24. package/dist/components/MultiAgentAccelerator/AgentCounts.d.ts +2 -2
  25. package/dist/components/MultiAgentAccelerator/AgentFlow.d.ts +13 -1
  26. package/dist/components/MultiAgentAccelerator/AgentFlow.js +193 -20
  27. package/dist/components/MultiAgentAccelerator/AgentNetworkDesigner.d.ts +10 -0
  28. package/dist/components/MultiAgentAccelerator/AgentNetworkDesigner.js +20 -0
  29. package/dist/components/MultiAgentAccelerator/AgentNode.d.ts +1 -0
  30. package/dist/components/MultiAgentAccelerator/AgentNode.js +9 -4
  31. package/dist/components/MultiAgentAccelerator/AgentNodePopup.d.ts +33 -0
  32. package/dist/components/MultiAgentAccelerator/AgentNodePopup.js +81 -0
  33. package/dist/components/MultiAgentAccelerator/GraphLayouts.d.ts +4 -4
  34. package/dist/components/MultiAgentAccelerator/GraphLayouts.js +12 -8
  35. package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.d.ts +1 -0
  36. package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.js +101 -44
  37. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +4 -4
  38. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.d.ts +1 -0
  39. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.js +29 -23
  40. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.js +1 -1
  41. package/dist/components/MultiAgentAccelerator/TemporaryNetworks.d.ts +14 -0
  42. package/dist/components/MultiAgentAccelerator/TemporaryNetworks.js +26 -1
  43. package/dist/components/MultiAgentAccelerator/ThoughtBubbleOverlay.js +8 -7
  44. package/dist/components/MultiAgentAccelerator/const.d.ts +24 -0
  45. package/dist/components/MultiAgentAccelerator/const.js +19 -0
  46. package/dist/controller/llm/LlmChat.js +1 -1
  47. package/dist/index.d.ts +8 -7
  48. package/dist/index.js +8 -7
  49. package/dist/state/ChatHistory.d.ts +50 -0
  50. package/dist/state/ChatHistory.js +98 -0
  51. package/dist/state/IndexedDBStorage.d.ts +14 -0
  52. package/dist/state/IndexedDBStorage.js +65 -0
  53. package/dist/state/TemporaryNetworks.d.ts +23 -0
  54. package/dist/state/TemporaryNetworks.js +43 -0
  55. package/dist/tsconfig.build.tsbuildinfo +1 -1
  56. package/package.json +8 -2
  57. package/dist/components/AgentChat/ControlButtons.js +0 -26
  58. package/dist/components/AgentChat/UserQueryDisplay.d.ts +0 -5
  59. /package/dist/components/AgentChat/{FormattedMarkdown.d.ts → ChatCommon/FormattedMarkdown.d.ts} +0 -0
  60. /package/dist/components/AgentChat/{Greetings.d.ts → ChatCommon/Greetings.d.ts} +0 -0
  61. /package/dist/components/AgentChat/{Greetings.js → ChatCommon/Greetings.js} +0 -0
  62. /package/dist/components/AgentChat/{SendButton.d.ts → ChatCommon/SendButton.d.ts} +0 -0
  63. /package/dist/components/AgentChat/{SyntaxHighlighterThemes.d.ts → ChatCommon/SyntaxHighlighterThemes.d.ts} +0 -0
  64. /package/dist/components/AgentChat/{SyntaxHighlighterThemes.js → ChatCommon/SyntaxHighlighterThemes.js} +0 -0
  65. /package/dist/components/AgentChat/{LlmChatButton.js → Common/LlmChatButton.js} +0 -0
  66. /package/dist/components/AgentChat/{Types.d.ts → Common/Types.d.ts} +0 -0
  67. /package/dist/components/AgentChat/{Types.js → Common/Types.js} +0 -0
package/dist/index.d.ts CHANGED
@@ -2,13 +2,13 @@
2
2
  * Export modules so they can be imported with simpler paths in the consumer
3
3
  */
4
4
  export * from "./Theme/Theme.js";
5
- export * from "./components/AgentChat/ChatCommon.js";
6
- export * from "./components/AgentChat/ControlButtons.js";
7
- export * from "./components/AgentChat/LlmChatButton.js";
8
- export * from "./components/AgentChat/SendButton.js";
9
- export * from "./components/AgentChat/SyntaxHighlighterThemes.js";
10
- export * from "./components/AgentChat/Types.js";
11
- export * from "./components/AgentChat/Utils.js";
5
+ export * from "./components/AgentChat/ChatCommon/ChatCommon.js";
6
+ export * from "./components/AgentChat/ChatCommon/ControlButtons.js";
7
+ export * from "./components/AgentChat/ChatCommon/SendButton.js";
8
+ export * from "./components/AgentChat/ChatCommon/SyntaxHighlighterThemes.js";
9
+ export * from "./components/AgentChat/Common/Types.js";
10
+ export * from "./components/AgentChat/Common/Utils.js";
11
+ export * from "./components/AgentChat/Common/LlmChatButton.js";
12
12
  export * from "./components/Authentication/Auth.js";
13
13
  export * from "./components/Common/Breadcrumbs.js";
14
14
  export * from "./components/Common/ConfirmationModal.js";
@@ -36,6 +36,7 @@ export * from "./state/Environment.js";
36
36
  export * from "./state/Settings.js";
37
37
  export * from "./state/UserInfo.js";
38
38
  export * from "./utils/Authentication.js";
39
+ export * from "./utils/File.js";
39
40
  export * from "./utils/text.js";
40
41
  export * from "./utils/title.js";
41
42
  export * from "./utils/useLocalStorage.js";
package/dist/index.js CHANGED
@@ -17,13 +17,13 @@ limitations under the License.
17
17
  * Export modules so they can be imported with simpler paths in the consumer
18
18
  */
19
19
  export * from "./Theme/Theme.js";
20
- export * from "./components/AgentChat/ChatCommon.js";
21
- export * from "./components/AgentChat/ControlButtons.js";
22
- export * from "./components/AgentChat/LlmChatButton.js";
23
- export * from "./components/AgentChat/SendButton.js";
24
- export * from "./components/AgentChat/SyntaxHighlighterThemes.js";
25
- export * from "./components/AgentChat/Types.js";
26
- export * from "./components/AgentChat/Utils.js";
20
+ export * from "./components/AgentChat/ChatCommon/ChatCommon.js";
21
+ export * from "./components/AgentChat/ChatCommon/ControlButtons.js";
22
+ export * from "./components/AgentChat/ChatCommon/SendButton.js";
23
+ export * from "./components/AgentChat/ChatCommon/SyntaxHighlighterThemes.js";
24
+ export * from "./components/AgentChat/Common/Types.js";
25
+ export * from "./components/AgentChat/Common/Utils.js";
26
+ export * from "./components/AgentChat/Common/LlmChatButton.js";
27
27
  export * from "./components/Authentication/Auth.js";
28
28
  export * from "./components/Common/Breadcrumbs.js";
29
29
  export * from "./components/Common/ConfirmationModal.js";
@@ -51,6 +51,7 @@ export * from "./state/Environment.js";
51
51
  export * from "./state/Settings.js";
52
52
  export * from "./state/UserInfo.js";
53
53
  export * from "./utils/Authentication.js";
54
+ export * from "./utils/File.js";
54
55
  export * from "./utils/text.js";
55
56
  export * from "./utils/title.js";
56
57
  export * from "./utils/useLocalStorage.js";
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Zustand state store for agent chat conversation history
3
+ *
4
+ */
5
+ import { BaseMessage, mapChatMessagesToStoredMessages } from "@langchain/core/messages";
6
+ import { ChatContext } from "../generated/neuro-san/NeuroSanClient.js";
7
+ type SlyData = Record<string, unknown>;
8
+ export declare const MAX_CHAT_HISTORY_ITEMS = 50;
9
+ interface AgentChatHistory<T = BaseMessage[] | ReturnType<typeof mapChatMessagesToStoredMessages>> {
10
+ readonly chatHistory: T;
11
+ readonly chatContext: ChatContext;
12
+ readonly slyData: SlyData;
13
+ }
14
+ /**
15
+ * State store interface
16
+ */
17
+ interface ChatHistoryStore {
18
+ readonly history: Record<string, AgentChatHistory<BaseMessage[]>>;
19
+ resetHistory: (agentId: string) => void;
20
+ updateChatContext: (agentId: string, chatContext: ChatContext) => void;
21
+ updateChatHistory: (agentId: string, messages: BaseMessage[]) => void;
22
+ updateSlyData: (agentId: string, slyData: SlyData) => void;
23
+ /**
24
+ * Copies the full history entry from `fromId` to `toId`. Used when a temporary network is replaced
25
+ * by a new reservation so that conversation history is not lost on the transition.
26
+ */
27
+ copyHistory: (fromId: string, toId: string) => void;
28
+ }
29
+ /**
30
+ * The hook that lets apps use the store.
31
+ * Structure:
32
+ * <pre>
33
+ * IndexedDB database: DB_NAME
34
+ * └── object store: OBJECT_STORE_NAME
35
+ * └── key: STORE_KEY
36
+ * └── value: {state: {history: {[agentId]: {chatHistory, chatContext, slyData}}}}
37
+ * </pre>
38
+ */
39
+ export declare const useAgentChatHistoryStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<ChatHistoryStore>, "persist"> & {
40
+ persist: {
41
+ setOptions: (options: Partial<import("zustand/middleware").PersistOptions<ChatHistoryStore, ChatHistoryStore>>) => void;
42
+ clearStorage: () => void;
43
+ rehydrate: () => Promise<void> | void;
44
+ hasHydrated: () => boolean;
45
+ onHydrate: (fn: (state: ChatHistoryStore) => void) => () => void;
46
+ onFinishHydration: (fn: (state: ChatHistoryStore) => void) => () => void;
47
+ getOptions: () => Partial<import("zustand/middleware").PersistOptions<ChatHistoryStore, ChatHistoryStore>>;
48
+ };
49
+ }>;
50
+ export {};
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Zustand state store for agent chat conversation history
3
+ *
4
+ */
5
+ import { mapChatMessagesToStoredMessages, mapStoredMessagesToChatMessages } from "@langchain/core/messages";
6
+ import { create } from "zustand";
7
+ import { persist } from "zustand/middleware";
8
+ import { indexedDBStorage } from "./IndexedDBStorage.js";
9
+ // Maximum number of messages to keep in the chat history for each agent. Once we reach this limit, we will start
10
+ // dropping old messages.
11
+ export const MAX_CHAT_HISTORY_ITEMS = 50;
12
+ /**
13
+ * Custom storage implementation for persisting the chat history in IndexedDB. We need to do some custom
14
+ * serialization and deserialization here because the chat history contains complex objects
15
+ * (langchain BaseMessage and ChatContext) that can't be directly serialized to JSON. We use the langchain API
16
+ * to help with the serialization and deserialization.
17
+ */
18
+ const chatHistoryStorage = {
19
+ getItem: async (itemName) => {
20
+ const stored = await indexedDBStorage.getItem(itemName);
21
+ if (!stored)
22
+ return null;
23
+ const parsed = stored;
24
+ const rehydratedHistory = Object.fromEntries(Object.entries(parsed?.state?.history ?? {}).map(([agentId, entry]) => [
25
+ agentId,
26
+ {
27
+ ...entry,
28
+ chatHistory: entry.chatHistory ? mapStoredMessagesToChatMessages(entry.chatHistory) : [],
29
+ },
30
+ ]));
31
+ return { ...parsed, state: { ...parsed.state, history: rehydratedHistory } };
32
+ },
33
+ setItem: async (itemName, value) => {
34
+ const serializedHistory = Object.fromEntries(Object.entries(value?.state?.history ?? {}).map(([agentId, entry]) => [
35
+ agentId,
36
+ {
37
+ ...entry,
38
+ chatHistory: entry.chatHistory ? mapChatMessagesToStoredMessages(entry.chatHistory) : [],
39
+ },
40
+ ]));
41
+ const toStore = { state: { history: serializedHistory }, version: value.version };
42
+ await indexedDBStorage.setItem(itemName, toStore);
43
+ },
44
+ removeItem: async (itemName) => {
45
+ await indexedDBStorage.removeItem(itemName);
46
+ },
47
+ };
48
+ // Key to use for storing the chat history in IndexedDB
49
+ const STORE_KEY = "agent-chat-history";
50
+ /**
51
+ * The hook that lets apps use the store.
52
+ * Structure:
53
+ * <pre>
54
+ * IndexedDB database: DB_NAME
55
+ * └── object store: OBJECT_STORE_NAME
56
+ * └── key: STORE_KEY
57
+ * └── value: {state: {history: {[agentId]: {chatHistory, chatContext, slyData}}}}
58
+ * </pre>
59
+ */
60
+ export const useAgentChatHistoryStore = create()(persist((set) => ({
61
+ history: {},
62
+ updateChatContext: (agentId, chatContext) => set((state) => {
63
+ const existing = state.history[agentId];
64
+ const newHistory = { ...state.history, [agentId]: { ...existing, chatContext } };
65
+ return { history: newHistory };
66
+ }),
67
+ updateChatHistory: (agentId, messages) => set((state) => {
68
+ const existing = state.history[agentId];
69
+ const updated = [...(existing?.chatHistory ?? []), ...messages];
70
+ const truncated = updated.length > MAX_CHAT_HISTORY_ITEMS ? updated.slice(-MAX_CHAT_HISTORY_ITEMS) : updated;
71
+ return { history: { ...state.history, [agentId]: { ...existing, chatHistory: truncated } } };
72
+ }),
73
+ updateSlyData: (agentId, slyData) => set((state) => {
74
+ const existing = state.history[agentId];
75
+ const mergedSlyData = { ...existing?.slyData, ...slyData };
76
+ const newHistory = { ...state.history, [agentId]: { ...existing, slyData: mergedSlyData } };
77
+ return { history: newHistory };
78
+ }),
79
+ resetHistory: (agentId) => set((state) => {
80
+ const { [agentId]: _, ...rest } = state.history;
81
+ return { history: rest };
82
+ }),
83
+ copyHistory: (fromId, toId) => set((state) => {
84
+ const existing = state.history[fromId];
85
+ if (!existing)
86
+ return state;
87
+ // Strip agent_reservations from the copied slyData. That field encodes the old network's
88
+ // reservation ID and would be bounced back to the backend on the next chat request,
89
+ // causing the backend to echo the old reservation — which then overwrites the new network
90
+ // in the temp-networks store via onChunkReceived.
91
+ const { agent_reservations: _, ...slyDataWithoutReservations } = existing.slyData ?? {};
92
+ const sanitized = { ...existing, slyData: slyDataWithoutReservations };
93
+ return { history: { ...state.history, [toId]: sanitized } };
94
+ }),
95
+ }), {
96
+ name: STORE_KEY,
97
+ storage: chatHistoryStorage,
98
+ }));
@@ -0,0 +1,14 @@
1
+ /**
2
+ * IndexedDB storage adapter for Zustand persist middleware.
3
+ *
4
+ */
5
+ export declare const DB_NAME = "zustand-store";
6
+ /**
7
+ * StateStorage implementation using IndexedDB. Allows us to persist Zustand state in the browser's IndexedDB,
8
+ * which is more robust and has larger storage limits than localStorage.
9
+ */
10
+ export declare const indexedDBStorage: {
11
+ getItem: (itemName: string) => Promise<unknown>;
12
+ setItem: (itemName: string, value: unknown) => Promise<void>;
13
+ removeItem: (itemName: string) => Promise<void>;
14
+ };
@@ -0,0 +1,65 @@
1
+ /**
2
+ * IndexedDB storage adapter for Zustand persist middleware.
3
+ *
4
+ */
5
+ export const DB_NAME = "zustand-store";
6
+ const OBJECT_STORE_NAME = "neuro-san-ui";
7
+ // Not exactly a sophisticated upgrade function, since it merely creates the object store if it doesn't exist.
8
+ // But good enough for our purposes for now.
9
+ const upgradeDB = (request) => () => {
10
+ const db = request.result;
11
+ if (!db.objectStoreNames.contains(OBJECT_STORE_NAME)) {
12
+ return db.createObjectStore(OBJECT_STORE_NAME);
13
+ }
14
+ return undefined;
15
+ };
16
+ /**
17
+ * StateStorage implementation using IndexedDB. Allows us to persist Zustand state in the browser's IndexedDB,
18
+ * which is more robust and has larger storage limits than localStorage.
19
+ */
20
+ /* eslint-disable unicorn/prefer-add-event-listener -- only applies to DOM event listeners which is not the case here */
21
+ export const indexedDBStorage = {
22
+ getItem: (itemName) => new Promise((resolve, reject) => {
23
+ const request = indexedDB.open(DB_NAME);
24
+ request.onupgradeneeded = upgradeDB(request);
25
+ request.onsuccess = () => {
26
+ const db = request.result;
27
+ const tx = request.result.transaction(OBJECT_STORE_NAME, "readonly");
28
+ const store = tx.objectStore(OBJECT_STORE_NAME);
29
+ const get = store.get(itemName);
30
+ get.onsuccess = () => resolve(get.result ?? null);
31
+ get.onerror = () => reject(get.error);
32
+ tx.oncomplete = () => db.close();
33
+ };
34
+ request.onerror = () => reject(request.error);
35
+ }),
36
+ setItem: (itemName, value) => new Promise((resolve, reject) => {
37
+ const request = indexedDB.open(DB_NAME);
38
+ request.onupgradeneeded = upgradeDB(request);
39
+ request.onsuccess = () => {
40
+ const db = request.result;
41
+ const tx = db.transaction(OBJECT_STORE_NAME, "readwrite");
42
+ const store = tx.objectStore(OBJECT_STORE_NAME);
43
+ const put = store.put(value, itemName);
44
+ put.onsuccess = () => resolve();
45
+ put.onerror = () => reject(put.error);
46
+ tx.oncomplete = () => db.close();
47
+ };
48
+ request.onerror = () => reject(request.error);
49
+ }),
50
+ removeItem: (itemName) => new Promise((resolve, reject) => {
51
+ const request = indexedDB.open(DB_NAME);
52
+ request.onupgradeneeded = upgradeDB(request);
53
+ request.onsuccess = () => {
54
+ const db = request.result;
55
+ const tx = request.result.transaction(OBJECT_STORE_NAME, "readwrite");
56
+ const store = tx.objectStore(OBJECT_STORE_NAME);
57
+ const del = store.delete(itemName);
58
+ del.onsuccess = () => resolve();
59
+ del.onerror = () => reject(del.error);
60
+ tx.oncomplete = () => db.close();
61
+ };
62
+ request.onerror = () => reject(request.error);
63
+ }),
64
+ };
65
+ /* eslint-enable unicorn/prefer-add-event-listener */
@@ -1,3 +1,4 @@
1
+ import { AgentNetworkDefinitionEntry } from "../components/MultiAgentAccelerator/const.js";
1
2
  import { AgentInfo } from "../generated/neuro-san/NeuroSanClient.js";
2
3
  type AgentReservation = {
3
4
  readonly reservation_id: string;
@@ -7,7 +8,10 @@ type AgentReservation = {
7
8
  export type TemporaryNetwork = {
8
9
  readonly reservation: AgentReservation;
9
10
  readonly agentInfo: AgentInfo;
11
+ /** The agent_network_name as sent by the backend — used when bouncing sly_data back to the backend. */
12
+ readonly agentNetworkName?: string;
10
13
  readonly networkHocon?: string | null;
14
+ readonly agentNetworkDefinition?: AgentNetworkDefinitionEntry[];
11
15
  };
12
16
  /**
13
17
  * Zustand state store for temporary networks, such as vibe coded networks created by the user.
@@ -15,7 +19,26 @@ export type TemporaryNetwork = {
15
19
  interface TempNetworksStore {
16
20
  readonly tempNetworks: TemporaryNetwork[];
17
21
  readonly setTempNetworks: (tempNetworks: TemporaryNetwork[]) => void;
22
+ /**
23
+ * Upsert new networks into the store. Networks are matched by the UUID-stripped portion of
24
+ * their `reservation_id` (e.g. `"travel_agency_ops"` from `"travel_agency_ops-{uuid}"`), falling
25
+ * back to `agentNetworkName` when the reservation_id has no UUID suffix. If an incoming network
26
+ * matches an existing one, the existing entry is replaced. Returns the final list of upserted
27
+ * networks (those that were added or replaced).
28
+ */
29
+ readonly upsertTempNetworks: (newNetworks: TemporaryNetwork[]) => TemporaryNetwork[];
30
+ readonly updateTempNetworkDefinition: (networkName: string, definition: AgentNetworkDefinitionEntry[]) => void;
18
31
  }
32
+ /**
33
+ * Derives the canonical network name from a reservation ID.
34
+ *
35
+ * The backend encodes the network name as a prefix in the reservation ID, followed by a UUID suffix:
36
+ * `{network_name}-{uuid}`, e.g. `travel_agency_ops-7876642e-fe75-4d44-a61e-300688a1a6c5`.
37
+ *
38
+ * Stripping the UUID suffix gives the stable name that can be used for deduplication across reservations.
39
+ * Returns `undefined` when the reservation ID doesn't match the expected format.
40
+ */
41
+ export declare const extractNetworkNameFromReservationId: (reservationId: string) => string | undefined;
19
42
  /**
20
43
  * The hook that lets apps use the store.
21
44
  */
@@ -15,12 +15,55 @@ limitations under the License.
15
15
  */
16
16
  import { create } from "zustand";
17
17
  import { persist } from "zustand/middleware";
18
+ // UUID v4 suffix pattern: 8-4-4-4-12 hex chars separated by dashes
19
+ const UUID_SUFFIX_RE = /-[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}$/iu;
20
+ /**
21
+ * Derives the canonical network name from a reservation ID.
22
+ *
23
+ * The backend encodes the network name as a prefix in the reservation ID, followed by a UUID suffix:
24
+ * `{network_name}-{uuid}`, e.g. `travel_agency_ops-7876642e-fe75-4d44-a61e-300688a1a6c5`.
25
+ *
26
+ * Stripping the UUID suffix gives the stable name that can be used for deduplication across reservations.
27
+ * Returns `undefined` when the reservation ID doesn't match the expected format.
28
+ */
29
+ export const extractNetworkNameFromReservationId = (reservationId) => {
30
+ const stripped = reservationId.replace(UUID_SUFFIX_RE, "");
31
+ return stripped !== reservationId ? stripped : undefined;
32
+ };
33
+ /**
34
+ * Returns the best available canonical name for a network, used as the dedup key in upsert.
35
+ * The UUID-stripped reservation_id is preferred because it is always consistent regardless of
36
+ * what prefix the backend may place in `agentNetworkName` (e.g. `"generated/travel_agency_ops"`
37
+ * vs `"travel_agency_ops"`). Falls back to `agentNetworkName` when the reservation_id has no
38
+ * UUID suffix (legacy reservations with static IDs).
39
+ */
40
+ const effectiveNetworkName = (n) => extractNetworkNameFromReservationId(n.reservation.reservation_id) ?? n.agentNetworkName;
18
41
  /**
19
42
  * The hook that lets apps use the store.
20
43
  */
21
44
  export const useTempNetworksStore = create()(persist((set) => ({
22
45
  tempNetworks: [],
23
46
  setTempNetworks: (tempNetworks) => set({ tempNetworks }),
47
+ upsertTempNetworks: (newNetworks) => {
48
+ set((state) => {
49
+ const updated = [...state.tempNetworks];
50
+ for (const newNetwork of newNetworks) {
51
+ const newName = effectiveNetworkName(newNetwork);
52
+ const existingIdx = newName ? updated.findIndex((n) => effectiveNetworkName(n) === newName) : -1;
53
+ if (existingIdx >= 0) {
54
+ updated[existingIdx] = newNetwork;
55
+ }
56
+ else {
57
+ updated.push(newNetwork);
58
+ }
59
+ }
60
+ return { tempNetworks: updated };
61
+ });
62
+ return newNetworks;
63
+ },
64
+ updateTempNetworkDefinition: (networkName, definition) => set((state) => ({
65
+ tempNetworks: state.tempNetworks.map((n) => n.agentInfo.agent_name === networkName ? { ...n, agentNetworkDefinition: definition } : n),
66
+ })),
24
67
  }), {
25
68
  name: "temp-networks",
26
69
  }));