local-operator-ui 0.1.5 → 0.2.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.
@@ -1,4 +1,4 @@
1
- import { c as createStyled, r as resolveProps, u as useTheme, a as reactExports, b as clsx, d as useForkRef, j as jsxRuntimeExports, e as capitalize, f as composeClasses, g as createTheme, h as generateUtilityClass, i as generateUtilityClasses, k as useDefaultProps, s as styled$1, m as memoTheme, l as getDefaultExportFromCjs, n as requireReactDom, _ as _inheritsLoose, o as _objectWithoutPropertiesLoose$1, R as React, T as TransitionGroupContext, p as duration, q as useTheme$1, t as useTimeout, v as useId, B as ButtonBase, w as alpha, x as createSimplePaletteValueFilter, C as CircularProgress, P as Paper, y as darken, z as lighten, A as useEventCallback, D as setRef, E as useEnhancedEffect, F as useRtl, G as globalCss, H as formatMuiErrorMessage, I as rootShouldForwardProp, J as Typography, K as deepmerge, L as extendSxProp, M as resolveBreakpointValues, N as handleBreakpoints, O as getPath, Q as isFocusVisible, S as typographyClasses, U as slotShouldForwardProp, V as clamp, W as emphasize, X as Timeout, Y as React$1, Z as Box, $ as FontAwesomeIcon, a0 as faRobot, a1 as faSliders, a2 as Button, a3 as faExclamationTriangle, a4 as faCheck, a5 as faTimes, a6 as faEraser, a7 as faPen, a8 as faInfoCircle, a9 as faGear, aa as faServer, ab as faStar, ac as faTrash, ad as faCommentDots, ae as faCode, af as faHome, ag as faDownload, ah as faFileAlt, ai as faDesktop, aj as faImage, ak as faMusic, al as faVideo, am as faLaptop, an as faUsers, ao as faDatabase, ap as faBoxOpen, aq as faBook, ar as faHdd, as as faBox, at as faArchive, au as faNetworkWired, av as faFolder, aw as faFolderOpen, ax as faClock, ay as faChevronDown, az as faTools, aA as faArrowDown, aB as faPaperclip, aC as faStop, aD as faPaperPlane, aE as keyframes, aF as faChevronUp, aG as faQuestion, aH as faLightbulb, aI as faChevronRight, aJ as faFile, aK as faUser, aL as faEllipsisVertical, aM as faComment, aN as faChevronLeft, aO as faPlus, aP as faSearch, aQ as faCommentSlash, aR as faArrowRight, aS as faComments, aT as faTag, aU as faIdCard, aV as faCalendarAlt, aW as faCodeBranch, aX as faShieldAlt, aY as faArrowLeft, aZ as requireReact, a_ as LinearProgress, a$ as faShield, b0 as faSignOut, b1 as faMagicWandSparkles, b2 as faBrain, b3 as faWandMagicSparkles, b4 as faExternalLinkAlt, b5 as faKey, b6 as faGlobe, b7 as faLock, b8 as faEdit, b9 as faSave, ba as faEnvelope, bb as faCloudUploadAlt, bc as faHistory, bd as faListAlt, be as library$1, bf as icons$1, bg as CssBaseline, bh as ReactDOM$1, bi as ThemeProvider, bj as theme, bk as ErrorBoundary } from "./theme-CHCNkogj.js";
1
+ import { c as createStyled, r as resolveProps, u as useTheme, a as reactExports, b as clsx, d as useForkRef, j as jsxRuntimeExports, e as capitalize, f as composeClasses, g as createTheme, h as generateUtilityClass, i as generateUtilityClasses, k as useDefaultProps, s as styled$1, m as memoTheme, l as getDefaultExportFromCjs, n as requireReactDom, _ as _inheritsLoose, o as _objectWithoutPropertiesLoose$1, R as React, T as TransitionGroupContext, p as duration, q as useTheme$1, t as useTimeout, v as useId, B as ButtonBase, w as alpha, x as createSimplePaletteValueFilter, C as CircularProgress, P as Paper, y as darken, z as lighten, A as useEventCallback, D as setRef, E as useEnhancedEffect, F as useRtl, G as globalCss, H as formatMuiErrorMessage, I as rootShouldForwardProp, J as Typography, K as deepmerge, L as extendSxProp, M as resolveBreakpointValues, N as handleBreakpoints, O as getPath, Q as isFocusVisible, S as typographyClasses, U as slotShouldForwardProp, V as clamp, W as emphasize, X as Timeout, Y as React$1, Z as Box, $ as FontAwesomeIcon, a0 as faRobot, a1 as faSliders, a2 as Button, a3 as faExclamationTriangle, a4 as faCheck, a5 as faTimes, a6 as faEraser, a7 as faPen, a8 as faInfoCircle, a9 as faGear, aa as faServer, ab as faStar, ac as faTrash, ad as faCommentDots, ae as faCode, af as faHome, ag as faDownload, ah as faFileAlt, ai as faDesktop, aj as faImage, ak as faMusic, al as faVideo, am as faLaptop, an as faUsers, ao as faDatabase, ap as faBoxOpen, aq as faBook, ar as faHdd, as as faBox, at as faArchive, au as faNetworkWired, av as faFolder, aw as faFolderOpen, ax as faClock, ay as faChevronDown, az as faTools, aA as faArrowDown, aB as faPaperclip, aC as faStop, aD as faPaperPlane, aE as keyframes, aF as faChevronUp, aG as faQuestion, aH as faLightbulb, aI as faChevronRight, aJ as faFile, aK as faUser, aL as faEllipsisVertical, aM as faComment, aN as faFileExport, aO as faChevronLeft, aP as faFileImport, aQ as faUpload, aR as faPlus, aS as faSearch, aT as faCommentSlash, aU as faArrowRight, aV as faComments, aW as faTag, aX as faIdCard, aY as faCalendarAlt, aZ as faCodeBranch, a_ as faShieldAlt, a$ as faArrowLeft, b0 as requireReact, b1 as LinearProgress, b2 as faShield, b3 as faSignOut, b4 as faMagicWandSparkles, b5 as faBrain, b6 as faWandMagicSparkles, b7 as faExternalLinkAlt, b8 as faKey, b9 as faGlobe, ba as faLock, bb as faEdit, bc as faSave, bd as faEnvelope, be as faCloudUploadAlt, bf as faHistory, bg as faListAlt, bh as library$1, bi as icons$1, bj as CssBaseline, bk as ReactDOM$1, bl as ThemeProvider, bm as theme, bn as ErrorBoundary } from "./theme-CuzpzMRz.js";
2
2
  const styled = createStyled();
3
3
  function getThemeProps(params) {
4
4
  const {
@@ -25409,6 +25409,104 @@ const AgentsApi = {
25409
25409
  );
25410
25410
  }
25411
25411
  return response.json();
25412
+ },
25413
+ /**
25414
+ * Get agent system prompt
25415
+ * Retrieve the system prompt for a specific agent.
25416
+ *
25417
+ * @param baseUrl - The base URL of the Local Operator API
25418
+ * @param agentId - ID of the agent to get system prompt for
25419
+ * @returns Promise resolving to the agent system prompt response
25420
+ * @throws Error if the request fails
25421
+ */
25422
+ async getAgentSystemPrompt(baseUrl, agentId) {
25423
+ const response = await fetch(
25424
+ `${baseUrl}/v1/agents/${agentId}/system-prompt`,
25425
+ {
25426
+ method: "GET",
25427
+ headers: {
25428
+ Accept: "application/json"
25429
+ }
25430
+ }
25431
+ );
25432
+ if (!response.ok) {
25433
+ throw new Error(
25434
+ `Get agent system prompt request failed: ${response.status} ${response.statusText}`
25435
+ );
25436
+ }
25437
+ return response.json();
25438
+ },
25439
+ /**
25440
+ * Update agent system prompt
25441
+ * Update the system prompt for a specific agent.
25442
+ *
25443
+ * @param baseUrl - The base URL of the Local Operator API
25444
+ * @param agentId - ID of the agent to update system prompt for
25445
+ * @param systemPrompt - The new system prompt text
25446
+ * @returns Promise resolving to the update response
25447
+ * @throws Error if the request fails
25448
+ */
25449
+ async updateAgentSystemPrompt(baseUrl, agentId, systemPrompt) {
25450
+ const response = await fetch(
25451
+ `${baseUrl}/v1/agents/${agentId}/system-prompt`,
25452
+ {
25453
+ method: "PUT",
25454
+ headers: {
25455
+ "Content-Type": "application/json",
25456
+ Accept: "application/json"
25457
+ },
25458
+ body: JSON.stringify({ system_prompt: systemPrompt })
25459
+ }
25460
+ );
25461
+ if (!response.ok) {
25462
+ throw new Error(
25463
+ `Update agent system prompt request failed: ${response.status} ${response.statusText}`
25464
+ );
25465
+ }
25466
+ return response.json();
25467
+ },
25468
+ /**
25469
+ * Import an agent from a ZIP file
25470
+ * Import an agent from a ZIP file containing agent state files with an agent.yml file.
25471
+ *
25472
+ * @param baseUrl - The base URL of the Local Operator API
25473
+ * @param file - The ZIP file containing agent state files
25474
+ * @returns Promise resolving to the imported agent response
25475
+ * @throws Error if the request fails
25476
+ */
25477
+ async importAgent(baseUrl, file) {
25478
+ const formData = new FormData();
25479
+ formData.append("file", file);
25480
+ const response = await fetch(`${baseUrl}/v1/agents/import`, {
25481
+ method: "POST",
25482
+ body: formData
25483
+ });
25484
+ if (!response.ok) {
25485
+ throw new Error(
25486
+ `Import agent request failed: ${response.status} ${response.statusText}`
25487
+ );
25488
+ }
25489
+ return response.json();
25490
+ },
25491
+ /**
25492
+ * Export an agent as a ZIP file
25493
+ * Export an agent's state files as a ZIP file.
25494
+ *
25495
+ * @param baseUrl - The base URL of the Local Operator API
25496
+ * @param agentId - ID of the agent to export
25497
+ * @returns Promise resolving to a Blob containing the ZIP file
25498
+ * @throws Error if the request fails
25499
+ */
25500
+ async exportAgent(baseUrl, agentId) {
25501
+ const response = await fetch(`${baseUrl}/v1/agents/${agentId}/export`, {
25502
+ method: "GET"
25503
+ });
25504
+ if (!response.ok) {
25505
+ throw new Error(
25506
+ `Export agent request failed: ${response.status} ${response.statusText}`
25507
+ );
25508
+ }
25509
+ return response.blob();
25412
25510
  }
25413
25511
  };
25414
25512
  const ChatApi = {
@@ -30126,8 +30224,7 @@ const useServerHealth = (refetchInterval = 5e3) => {
30126
30224
  },
30127
30225
  // Refetch at specified interval
30128
30226
  refetchInterval,
30129
- // Enable background refetching
30130
- refetchIntervalInBackground: true,
30227
+ refetchIntervalInBackground: false,
30131
30228
  // Don't retry on failure as this is expected when server is down
30132
30229
  retry: false,
30133
30230
  // Always refetch on window focus to get latest status
@@ -30159,8 +30256,7 @@ const useInternetConnectivity = (refetchInterval = 5e3) => {
30159
30256
  },
30160
30257
  // Refetch at specified interval
30161
30258
  refetchInterval,
30162
- // Enable background refetching
30163
- refetchIntervalInBackground: true,
30259
+ refetchIntervalInBackground: false,
30164
30260
  // Don't retry on failure as this is expected when internet is down
30165
30261
  retry: false,
30166
30262
  // Always refetch on window focus to get latest status
@@ -30321,11 +30417,9 @@ const useAgents = (page = 1, perPage = 50, refetchInterval = 0, name2) => {
30321
30417
  }
30322
30418
  },
30323
30419
  // Prevent automatic refetches on window focus
30324
- refetchOnWindowFocus: false,
30420
+ refetchOnWindowFocus: true,
30325
30421
  // Set refetch interval if provided
30326
30422
  refetchInterval,
30327
- // Enable background refetching if interval is set
30328
- refetchIntervalInBackground: refetchInterval > 0,
30329
30423
  // Prevent stale time to avoid unnecessary refetches
30330
30424
  staleTime: 5e3
30331
30425
  });
@@ -30969,10 +31063,8 @@ const useJobPolling = ({
30969
31063
  enabled: shouldEnableQuery({ bypassInternetCheck: true }) && !!currentJobId,
30970
31064
  // Poll every 1 second while job is active
30971
31065
  refetchInterval: currentJobId ? 1e3 : false,
30972
- // Force refetch on interval regardless of window focus
30973
- refetchIntervalInBackground: true,
30974
- // Don't refetch on window focus while polling
30975
- refetchOnWindowFocus: false,
31066
+ refetchIntervalInBackground: false,
31067
+ refetchOnWindowFocus: true,
30976
31068
  // Don't cache the result
30977
31069
  gcTime: 0,
30978
31070
  staleTime: 0,
@@ -31825,7 +31917,7 @@ const SidebarContent = styled$1(Box)({
31825
31917
  borderRadius: "4px"
31826
31918
  }
31827
31919
  });
31828
- const SectionTitle$6 = styled$1(Typography)(({ theme: theme2 }) => ({
31920
+ const SectionTitle$7 = styled$1(Typography)(({ theme: theme2 }) => ({
31829
31921
  fontWeight: 600,
31830
31922
  marginBottom: theme2.spacing(2),
31831
31923
  marginTop: theme2.spacing(3),
@@ -31833,11 +31925,11 @@ const SectionTitle$6 = styled$1(Typography)(({ theme: theme2 }) => ({
31833
31925
  alignItems: "center",
31834
31926
  color: theme2.palette.text.primary
31835
31927
  }));
31836
- const TitleIcon$3 = styled$1(FontAwesomeIcon)({
31928
+ const TitleIcon$4 = styled$1(FontAwesomeIcon)({
31837
31929
  marginRight: 10,
31838
31930
  color: "#f2f2f3"
31839
31931
  });
31840
- const InfoButton$2 = styled$1(IconButton)(({ theme: theme2 }) => ({
31932
+ const InfoButton$3 = styled$1(IconButton)(({ theme: theme2 }) => ({
31841
31933
  marginLeft: theme2.spacing(1),
31842
31934
  color: theme2.palette.primary.main,
31843
31935
  "&:hover": {
@@ -32469,8 +32561,8 @@ const GenerationSettingsSection = ({
32469
32561
  updateAgentMutation
32470
32562
  }) => {
32471
32563
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
32472
- /* @__PURE__ */ jsxRuntimeExports.jsxs(SectionTitle$6, { variant: "subtitle1", children: [
32473
- /* @__PURE__ */ jsxRuntimeExports.jsx(TitleIcon$3, { icon: faSliders }),
32564
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SectionTitle$7, { variant: "subtitle1", children: [
32565
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TitleIcon$4, { icon: faSliders }),
32474
32566
  "Generation Settings",
32475
32567
  /* @__PURE__ */ jsxRuntimeExports.jsx(
32476
32568
  Tooltip,
@@ -32478,7 +32570,7 @@ const GenerationSettingsSection = ({
32478
32570
  title: "Settings that control how the agent generates responses",
32479
32571
  arrow: true,
32480
32572
  placement: "top",
32481
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(InfoButton$2, { size: "small", children: /* @__PURE__ */ jsxRuntimeExports.jsx(FontAwesomeIcon, { icon: faInfoCircle, size: "xs" }) })
32573
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(InfoButton$3, { size: "small", children: /* @__PURE__ */ jsxRuntimeExports.jsx(FontAwesomeIcon, { icon: faInfoCircle, size: "xs" }) })
32482
32574
  }
32483
32575
  )
32484
32576
  ] }),
@@ -33048,10 +33140,19 @@ const HostingSelect = ({
33048
33140
  const [inputValue, setInputValue] = reactExports.useState("");
33049
33141
  const [isSubmitting, setIsSubmitting] = reactExports.useState(false);
33050
33142
  const showAllOptions = reactExports.useRef(false);
33143
+ const credentialFetchAttemptsRef = reactExports.useRef(0);
33144
+ const MAX_CREDENTIAL_FETCH_ATTEMPTS = 3;
33051
33145
  const availableHostingProviders = reactExports.useMemo(() => {
33052
33146
  if (!filterByCredentials) {
33053
33147
  return getHostingProviders();
33054
33148
  }
33149
+ if (credentialFetchAttemptsRef.current >= MAX_CREDENTIAL_FETCH_ATTEMPTS) {
33150
+ console.warn(
33151
+ `Exceeded maximum credential fetch attempts (${MAX_CREDENTIAL_FETCH_ATTEMPTS})`
33152
+ );
33153
+ return getHostingProviders();
33154
+ }
33155
+ credentialFetchAttemptsRef.current += 1;
33055
33156
  return getAvailableHostingProviders(userCredentials);
33056
33157
  }, [userCredentials, filterByCredentials]);
33057
33158
  const hostingOptions = reactExports.useMemo(() => {
@@ -33113,6 +33214,9 @@ const HostingSelect = ({
33113
33214
  provider: void 0
33114
33215
  };
33115
33216
  }, [value, hostingOptions]);
33217
+ reactExports.useEffect(() => {
33218
+ credentialFetchAttemptsRef.current = 0;
33219
+ }, []);
33116
33220
  reactExports.useEffect(() => {
33117
33221
  if (selectedOption) {
33118
33222
  setInputValue(selectedOption.name);
@@ -33125,6 +33229,7 @@ const HostingSelect = ({
33125
33229
  try {
33126
33230
  setIsSubmitting(true);
33127
33231
  const option = newValue;
33232
+ credentialFetchAttemptsRef.current = 0;
33128
33233
  await onSave(option.id);
33129
33234
  setIsUserTyping(false);
33130
33235
  setInputValue(option.name);
@@ -43012,17 +43117,27 @@ const ModelSelect = ({
43012
43117
  const models = getModelsForHostingProvider(hostingId);
43013
43118
  return models;
43014
43119
  }, [hostingId]);
43015
- const { refreshModels } = useModels();
43120
+ const { refreshModels, isLoading: isModelsLoading } = useModels();
43016
43121
  const previousHostingIdRef = reactExports.useRef(null);
43017
43122
  const refreshTimeRef = reactExports.useRef(null);
43123
+ const refreshAttemptCountRef = reactExports.useRef(0);
43124
+ const MAX_REFRESH_ATTEMPTS = 3;
43018
43125
  reactExports.useEffect(() => {
43019
43126
  if (hostingId && previousHostingIdRef.current !== hostingId) {
43020
43127
  previousHostingIdRef.current = hostingId;
43128
+ refreshAttemptCountRef.current = 0;
43021
43129
  const timeoutId = setTimeout(() => {
43022
43130
  const now = Date.now();
43023
43131
  const lastRefreshTime = refreshTimeRef.current;
43024
43132
  const THROTTLE_DURATION = 2e3;
43025
- if (!lastRefreshTime || now - lastRefreshTime > THROTTLE_DURATION) {
43133
+ if (refreshAttemptCountRef.current >= MAX_REFRESH_ATTEMPTS) {
43134
+ console.warn(
43135
+ `Exceeded maximum model refresh attempts (${MAX_REFRESH_ATTEMPTS}) for hosting provider ${hostingId}`
43136
+ );
43137
+ return;
43138
+ }
43139
+ if ((!lastRefreshTime || now - lastRefreshTime > THROTTLE_DURATION) && !isModelsLoading) {
43140
+ refreshAttemptCountRef.current += 1;
43026
43141
  refreshModels();
43027
43142
  refreshTimeRef.current = now;
43028
43143
  }
@@ -43030,7 +43145,7 @@ const ModelSelect = ({
43030
43145
  return () => clearTimeout(timeoutId);
43031
43146
  }
43032
43147
  return void 0;
43033
- }, [hostingId, refreshModels]);
43148
+ }, [hostingId, refreshModels, isModelsLoading]);
43034
43149
  const modelOptions = reactExports.useMemo(() => {
43035
43150
  const options = [
43036
43151
  {
@@ -43269,8 +43384,8 @@ const ModelHostingSection = ({
43269
43384
  updateAgentMutation
43270
43385
  }) => {
43271
43386
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
43272
- /* @__PURE__ */ jsxRuntimeExports.jsxs(SectionTitle$6, { variant: "subtitle1", children: [
43273
- /* @__PURE__ */ jsxRuntimeExports.jsx(TitleIcon$3, { icon: faServer }),
43387
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SectionTitle$7, { variant: "subtitle1", children: [
43388
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TitleIcon$4, { icon: faServer }),
43274
43389
  "Model & Hosting",
43275
43390
  /* @__PURE__ */ jsxRuntimeExports.jsx(
43276
43391
  Tooltip,
@@ -43278,7 +43393,7 @@ const ModelHostingSection = ({
43278
43393
  title: "Configure which model and hosting provider to use",
43279
43394
  arrow: true,
43280
43395
  placement: "top",
43281
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(InfoButton$2, { size: "small", children: /* @__PURE__ */ jsxRuntimeExports.jsx(FontAwesomeIcon, { icon: faInfoCircle, size: "xs" }) })
43396
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(InfoButton$3, { size: "small", children: /* @__PURE__ */ jsxRuntimeExports.jsx(FontAwesomeIcon, { icon: faInfoCircle, size: "xs" }) })
43282
43397
  }
43283
43398
  )
43284
43399
  ] }),
@@ -82296,6 +82411,10 @@ const MarkdownContent = styled$1(Box, {
82296
82411
  })
82297
82412
  );
82298
82413
  const convertUrlsToMarkdownLinks = (text2) => {
82414
+ const markdownLinkRegex = /(?<!\]\()(https?:\/\/\S+?)([,.;:!?"'\)\]]+)?(?=\s|$)/g;
82415
+ if (markdownLinkRegex.test(text2)) {
82416
+ return text2;
82417
+ }
82299
82418
  const urlRegex = /(?<!\]\()(https?:\/\/\S+?)([,.;:!?"'\)\]]+)?(?=\s|$)/g;
82300
82419
  return text2.replace(
82301
82420
  urlRegex,
@@ -83395,6 +83514,53 @@ const useDeleteAgent = () => {
83395
83514
  }
83396
83515
  });
83397
83516
  };
83517
+ const useImportAgent = () => {
83518
+ const queryClient2 = useQueryClient();
83519
+ const client = createLocalOperatorClient(apiConfig.baseUrl);
83520
+ return useMutation({
83521
+ mutationFn: async (file) => {
83522
+ try {
83523
+ const response = await client.agents.importAgent(file);
83524
+ if (response.status >= 400) {
83525
+ throw new Error(response.message || "Failed to import agent");
83526
+ }
83527
+ return response.result;
83528
+ } catch (error) {
83529
+ const errorMessage = error instanceof Error ? error.message : "An unknown error occurred while importing the agent";
83530
+ y$1.error(errorMessage);
83531
+ throw error;
83532
+ }
83533
+ },
83534
+ onSuccess: (data) => {
83535
+ queryClient2.invalidateQueries({ queryKey: agentsQueryKey });
83536
+ y$1.success("Agent imported successfully");
83537
+ return data;
83538
+ },
83539
+ onError: (error) => {
83540
+ console.error("Error importing agent:", error);
83541
+ }
83542
+ });
83543
+ };
83544
+ const useExportAgent = () => {
83545
+ const client = createLocalOperatorClient(apiConfig.baseUrl);
83546
+ return useMutation({
83547
+ mutationFn: async (agentId) => {
83548
+ try {
83549
+ return await client.agents.exportAgent(agentId);
83550
+ } catch (error) {
83551
+ const errorMessage = error instanceof Error ? error.message : `An unknown error occurred while exporting agent ${agentId}`;
83552
+ y$1.error(errorMessage);
83553
+ throw error;
83554
+ }
83555
+ },
83556
+ onSuccess: () => {
83557
+ y$1.success("Agent exported successfully");
83558
+ },
83559
+ onError: (error) => {
83560
+ console.error("Error exporting agent:", error);
83561
+ }
83562
+ });
83563
+ };
83398
83564
  const OptionsIconButton = styled$1(IconButton)({
83399
83565
  opacity: 0,
83400
83566
  transition: "opacity 0.2s",
@@ -83434,7 +83600,8 @@ const AgentOptionsMenu = ({
83434
83600
  buttonSx = {},
83435
83601
  isAgentsPage = false,
83436
83602
  onViewAgentSettings,
83437
- onChatWithAgent
83603
+ onChatWithAgent,
83604
+ onExportAgent
83438
83605
  }) => {
83439
83606
  const [menuAnchorEl, setMenuAnchorEl] = reactExports.useState(null);
83440
83607
  const [isDeleteModalOpen, setIsDeleteModalOpen] = reactExports.useState(false);
@@ -83521,6 +83688,19 @@ const AgentOptionsMenu = ({
83521
83688
  ]
83522
83689
  }
83523
83690
  ),
83691
+ onExportAgent && /* @__PURE__ */ jsxRuntimeExports.jsxs(
83692
+ StyledMenuItem,
83693
+ {
83694
+ onClick: () => {
83695
+ onExportAgent();
83696
+ handleCloseMenu();
83697
+ },
83698
+ children: [
83699
+ /* @__PURE__ */ jsxRuntimeExports.jsx(MenuItemIcon, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(FontAwesomeIcon, { icon: faFileExport, size: "sm" }) }),
83700
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Typography, { variant: "body2", children: "Export Agent" })
83701
+ ]
83702
+ }
83703
+ ),
83524
83704
  /* @__PURE__ */ jsxRuntimeExports.jsxs(DeleteMenuItem, { onClick: handleOpenDeleteConfirmation, children: [
83525
83705
  /* @__PURE__ */ jsxRuntimeExports.jsx(MenuItemIcon, { color: "error", children: /* @__PURE__ */ jsxRuntimeExports.jsx(FontAwesomeIcon, { icon: faTrash, size: "sm" }) }),
83526
83706
  /* @__PURE__ */ jsxRuntimeExports.jsx(Typography, { variant: "body2", children: "Delete Agent" })
@@ -83631,7 +83811,7 @@ const CompactPagination = ({
83631
83811
  )
83632
83812
  ] });
83633
83813
  };
83634
- const StyledIcon = styled$1(FontAwesomeIcon)(({ theme: theme2 }) => ({
83814
+ const StyledIcon$1 = styled$1(FontAwesomeIcon)(({ theme: theme2 }) => ({
83635
83815
  color: theme2.palette.primary.main,
83636
83816
  fontSize: "1.2rem"
83637
83817
  }));
@@ -83665,7 +83845,7 @@ const NameField = styled$1(StyledTextField$1)({
83665
83845
  transform: "translate(14px, -9px) scale(0.75)"
83666
83846
  }
83667
83847
  });
83668
- const Subtitle = styled$1(Typography)(({ theme: theme2 }) => ({
83848
+ const Subtitle$1 = styled$1(Typography)(({ theme: theme2 }) => ({
83669
83849
  color: theme2.palette.text.secondary,
83670
83850
  fontSize: "0.875rem",
83671
83851
  marginTop: 4,
@@ -83703,7 +83883,7 @@ const CreateAgentDialog = ({
83703
83883
  const isLoading = createAgentMutation.isPending;
83704
83884
  const isSubmitDisabled = isLoading || !name2.trim();
83705
83885
  const dialogTitle = /* @__PURE__ */ jsxRuntimeExports.jsxs(TitleContainer, { children: [
83706
- /* @__PURE__ */ jsxRuntimeExports.jsx(StyledIcon, { icon: faRobot }),
83886
+ /* @__PURE__ */ jsxRuntimeExports.jsx(StyledIcon$1, { icon: faRobot }),
83707
83887
  "Create New Agent"
83708
83888
  ] });
83709
83889
  const dialogActions = /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
@@ -83736,7 +83916,7 @@ const CreateAgentDialog = ({
83736
83916
  actions: dialogActions,
83737
83917
  maxWidth: "sm",
83738
83918
  children: [
83739
- /* @__PURE__ */ jsxRuntimeExports.jsx(Subtitle, { children: "Configure your new AI assistant with a name and optional description" }),
83919
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Subtitle$1, { children: "Configure your new AI assistant with a name and optional description" }),
83740
83920
  /* @__PURE__ */ jsxRuntimeExports.jsx("form", { id: "create-agent-form", onSubmit: handleSubmit, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(FormContainer$2, { children: [
83741
83921
  /* @__PURE__ */ jsxRuntimeExports.jsx(
83742
83922
  NameField,
@@ -83777,6 +83957,199 @@ const CreateAgentDialog = ({
83777
83957
  }
83778
83958
  );
83779
83959
  };
83960
+ const StyledIcon = styled$1(FontAwesomeIcon)(({ theme: theme2 }) => ({
83961
+ color: theme2.palette.primary.main,
83962
+ fontSize: "1.2rem"
83963
+ }));
83964
+ const UploadArea = styled$1(Box)(({ theme: theme2 }) => ({
83965
+ border: `2px dashed ${theme2.palette.divider}`,
83966
+ borderRadius: theme2.shape.borderRadius * 2,
83967
+ padding: theme2.spacing(4),
83968
+ textAlign: "center",
83969
+ cursor: "pointer",
83970
+ transition: "all 0.2s ease-in-out",
83971
+ backgroundColor: alpha(theme2.palette.background.paper, 0.6),
83972
+ "&:hover": {
83973
+ borderColor: theme2.palette.primary.main,
83974
+ backgroundColor: alpha(theme2.palette.action.hover, 0.1)
83975
+ },
83976
+ "&.drag-active": {
83977
+ borderColor: theme2.palette.primary.main,
83978
+ backgroundColor: alpha(theme2.palette.primary.main, 0.05),
83979
+ boxShadow: `0 0 0 3px ${alpha(theme2.palette.primary.main, 0.15)}`
83980
+ }
83981
+ }));
83982
+ const UploadIcon = styled$1(FontAwesomeIcon)(({ theme: theme2 }) => ({
83983
+ fontSize: "2rem",
83984
+ color: theme2.palette.text.secondary,
83985
+ marginBottom: theme2.spacing(2)
83986
+ }));
83987
+ const FileInput = styled$1("input")({
83988
+ display: "none"
83989
+ });
83990
+ const Subtitle = styled$1(Typography)(({ theme: theme2 }) => ({
83991
+ color: theme2.palette.text.secondary,
83992
+ fontSize: "0.875rem",
83993
+ marginTop: 4,
83994
+ marginBottom: 16
83995
+ }));
83996
+ const ErrorMessage = styled$1(Typography)(({ theme: theme2 }) => ({
83997
+ color: theme2.palette.error.main,
83998
+ fontSize: "0.875rem",
83999
+ marginTop: theme2.spacing(2)
84000
+ }));
84001
+ const ImportAgentDialog = ({
84002
+ open,
84003
+ onClose,
84004
+ onAgentImported
84005
+ }) => {
84006
+ const [file, setFile] = reactExports.useState(null);
84007
+ const [isDragActive, setIsDragActive] = reactExports.useState(false);
84008
+ const [error, setError] = reactExports.useState(null);
84009
+ const fileInputRef = reactExports.useRef(null);
84010
+ const importAgentMutation = useImportAgent();
84011
+ const handleDragEnter = reactExports.useCallback((e) => {
84012
+ e.preventDefault();
84013
+ e.stopPropagation();
84014
+ setIsDragActive(true);
84015
+ }, []);
84016
+ const handleDragLeave = reactExports.useCallback((e) => {
84017
+ e.preventDefault();
84018
+ e.stopPropagation();
84019
+ setIsDragActive(false);
84020
+ }, []);
84021
+ const handleDragOver = reactExports.useCallback((e) => {
84022
+ e.preventDefault();
84023
+ e.stopPropagation();
84024
+ }, []);
84025
+ const validateFile = reactExports.useCallback((file2) => {
84026
+ if (!file2.name.toLowerCase().endsWith(".zip")) {
84027
+ setError("Only ZIP files are supported");
84028
+ return false;
84029
+ }
84030
+ setError(null);
84031
+ return true;
84032
+ }, []);
84033
+ const handleDrop = reactExports.useCallback(
84034
+ (e) => {
84035
+ e.preventDefault();
84036
+ e.stopPropagation();
84037
+ setIsDragActive(false);
84038
+ if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
84039
+ const droppedFile = e.dataTransfer.files[0];
84040
+ if (validateFile(droppedFile)) {
84041
+ setFile(droppedFile);
84042
+ }
84043
+ }
84044
+ },
84045
+ [validateFile]
84046
+ );
84047
+ const handleFileSelect = reactExports.useCallback(
84048
+ (e) => {
84049
+ if (e.target.files && e.target.files.length > 0) {
84050
+ const selectedFile = e.target.files[0];
84051
+ if (validateFile(selectedFile)) {
84052
+ setFile(selectedFile);
84053
+ }
84054
+ }
84055
+ },
84056
+ [validateFile]
84057
+ );
84058
+ const handleClickUpload = reactExports.useCallback(() => {
84059
+ fileInputRef.current?.click();
84060
+ }, []);
84061
+ const handleSubmit = async () => {
84062
+ if (!file) return;
84063
+ try {
84064
+ const result = await importAgentMutation.mutateAsync(file);
84065
+ setFile(null);
84066
+ setError(null);
84067
+ if (onAgentImported && result) {
84068
+ onAgentImported(result.id);
84069
+ }
84070
+ onClose();
84071
+ } catch (error2) {
84072
+ console.error("Failed to import agent:", error2);
84073
+ }
84074
+ };
84075
+ const handleClose = () => {
84076
+ setFile(null);
84077
+ setError(null);
84078
+ onClose();
84079
+ };
84080
+ const isLoading = importAgentMutation.isPending;
84081
+ const isSubmitDisabled = isLoading || !file;
84082
+ const dialogTitle = /* @__PURE__ */ jsxRuntimeExports.jsxs(TitleContainer, { children: [
84083
+ /* @__PURE__ */ jsxRuntimeExports.jsx(StyledIcon, { icon: faFileImport }),
84084
+ "Import Agent"
84085
+ ] });
84086
+ const dialogActions = /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
84087
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
84088
+ SecondaryButton,
84089
+ {
84090
+ onClick: handleClose,
84091
+ variant: "outlined",
84092
+ disabled: isLoading,
84093
+ children: "Cancel"
84094
+ }
84095
+ ),
84096
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
84097
+ PrimaryButton,
84098
+ {
84099
+ onClick: handleSubmit,
84100
+ disabled: isSubmitDisabled,
84101
+ startIcon: isLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx(CircularProgress, { size: 20, color: "inherit" }) : null,
84102
+ children: "Import Agent"
84103
+ }
84104
+ )
84105
+ ] });
84106
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
84107
+ BaseDialog,
84108
+ {
84109
+ open,
84110
+ onClose: handleClose,
84111
+ title: dialogTitle,
84112
+ actions: dialogActions,
84113
+ maxWidth: "sm",
84114
+ children: [
84115
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Subtitle, { children: "Import an agent from a ZIP file exported from Local Operator. The file should contain an agent.yml file." }),
84116
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(FormContainer$2, { children: [
84117
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
84118
+ FileInput,
84119
+ {
84120
+ ref: fileInputRef,
84121
+ type: "file",
84122
+ accept: ".zip",
84123
+ onChange: handleFileSelect,
84124
+ disabled: isLoading
84125
+ }
84126
+ ),
84127
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
84128
+ UploadArea,
84129
+ {
84130
+ onClick: handleClickUpload,
84131
+ onDragEnter: handleDragEnter,
84132
+ onDragLeave: handleDragLeave,
84133
+ onDragOver: handleDragOver,
84134
+ onDrop: handleDrop,
84135
+ className: isDragActive ? "drag-active" : "",
84136
+ children: file ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
84137
+ /* @__PURE__ */ jsxRuntimeExports.jsx(UploadIcon, { icon: faFileImport }),
84138
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Typography, { variant: "body1", gutterBottom: true, children: file.name }),
84139
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Typography, { variant: "body2", color: "text.secondary", children: "Click or drag to replace" })
84140
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
84141
+ /* @__PURE__ */ jsxRuntimeExports.jsx(UploadIcon, { icon: faUpload }),
84142
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Typography, { variant: "body1", gutterBottom: true, children: "Drag and drop a ZIP file here" }),
84143
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Typography, { variant: "body2", color: "text.secondary", children: "or click to select a file" })
84144
+ ] })
84145
+ }
84146
+ ),
84147
+ error && /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorMessage, { children: error })
84148
+ ] })
84149
+ ]
84150
+ }
84151
+ );
84152
+ };
83780
84153
  const SidebarHeaderContainer = styled$1(Box)(({ theme: theme2 }) => ({
83781
84154
  padding: theme2.spacing(3),
83782
84155
  borderBottom: "1px solid rgba(255,255,255,0.08)"
@@ -83787,25 +84160,28 @@ const HeaderRow = styled$1(Box)({
83787
84160
  alignItems: "center",
83788
84161
  marginBottom: 16
83789
84162
  });
84163
+ const ActionsContainer = styled$1(Box)({
84164
+ display: "flex",
84165
+ gap: 8
84166
+ });
83790
84167
  const SidebarTitle$1 = styled$1(Typography)({
83791
84168
  fontWeight: 600,
83792
84169
  fontSize: "1rem"
83793
84170
  });
83794
- const NewAgentButton = styled$1(Button)(({ theme: theme2 }) => ({
83795
- borderRadius: 8,
83796
- textTransform: "none",
83797
- fontWeight: 600,
83798
- paddingLeft: 16,
83799
- paddingRight: 16,
83800
- paddingTop: 6.4,
83801
- paddingBottom: 6.4,
84171
+ const ActionButton = styled$1(IconButton)(({ theme: theme2 }) => ({
84172
+ borderRadius: 10,
84173
+ padding: 8,
84174
+ backgroundColor: alpha(theme2.palette.primary.main, 0.1),
84175
+ color: theme2.palette.primary.main,
83802
84176
  transition: "all 0.2s ease-in-out",
83803
84177
  "&:hover": {
83804
- boxShadow: `0 2px 8px ${theme2.palette.primary.main}33`,
83805
- transform: "translateY(-1px)"
84178
+ backgroundColor: alpha(theme2.palette.primary.main, 0.2),
84179
+ transform: "translateY(-2px)",
84180
+ boxShadow: `0 4px 8px ${alpha(theme2.palette.primary.main, 0.25)}`
83806
84181
  },
83807
84182
  "&:active": {
83808
- transform: "translateY(0)"
84183
+ transform: "translateY(0)",
84184
+ boxShadow: `0 2px 4px ${alpha(theme2.palette.primary.main, 0.2)}`
83809
84185
  }
83810
84186
  }));
83811
84187
  const SearchField = styled$1(TextField)(({ theme: theme2 }) => ({
@@ -83837,22 +84213,33 @@ const SidebarHeader$1 = ({
83837
84213
  onSearchChange,
83838
84214
  onNewAgentClick,
83839
84215
  searchPlaceholder = "Search agents",
83840
- newAgentTooltip = "Create a new agent"
84216
+ newAgentTooltip = "Create a new agent",
84217
+ onImportAgentClick,
84218
+ importAgentTooltip = "Import an agent from a ZIP file"
83841
84219
  }) => {
83842
84220
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(SidebarHeaderContainer, { children: [
83843
84221
  /* @__PURE__ */ jsxRuntimeExports.jsxs(HeaderRow, { children: [
83844
84222
  /* @__PURE__ */ jsxRuntimeExports.jsx(SidebarTitle$1, { variant: "subtitle1", children: title }),
83845
- /* @__PURE__ */ jsxRuntimeExports.jsx(Tooltip, { title: newAgentTooltip, arrow: true, placement: "top", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
83846
- NewAgentButton,
83847
- {
83848
- variant: "outlined",
83849
- color: "primary",
83850
- size: "small",
83851
- startIcon: /* @__PURE__ */ jsxRuntimeExports.jsx(FontAwesomeIcon, { icon: faPlus }),
83852
- onClick: onNewAgentClick,
83853
- children: "New Agent"
83854
- }
83855
- ) })
84223
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(ActionsContainer, { children: [
84224
+ onImportAgentClick && /* @__PURE__ */ jsxRuntimeExports.jsx(Tooltip, { title: importAgentTooltip, arrow: true, placement: "top", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
84225
+ ActionButton,
84226
+ {
84227
+ onClick: onImportAgentClick,
84228
+ size: "small",
84229
+ "aria-label": "Import agent",
84230
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(FontAwesomeIcon, { icon: faFileImport })
84231
+ }
84232
+ ) }),
84233
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Tooltip, { title: newAgentTooltip, arrow: true, placement: "top", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
84234
+ ActionButton,
84235
+ {
84236
+ onClick: onNewAgentClick,
84237
+ size: "small",
84238
+ "aria-label": "New agent",
84239
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(FontAwesomeIcon, { icon: faPlus })
84240
+ }
84241
+ ) })
84242
+ ] })
83856
84243
  ] }),
83857
84244
  /* @__PURE__ */ jsxRuntimeExports.jsx(
83858
84245
  SearchField,
@@ -85460,7 +85847,9 @@ const ChatSidebar = ({
85460
85847
  }) => {
85461
85848
  const [searchQuery, setSearchQuery] = reactExports.useState("");
85462
85849
  const [isCreateDialogOpen, setIsCreateDialogOpen] = reactExports.useState(false);
85850
+ const [isImportDialogOpen, setIsImportDialogOpen] = reactExports.useState(false);
85463
85851
  const perPage = 50;
85852
+ const exportAgentMutation = useExportAgent();
85464
85853
  const { page, setPage } = usePaginationParams();
85465
85854
  const {
85466
85855
  data: agents = [],
@@ -85486,6 +85875,32 @@ const ChatSidebar = ({
85486
85875
  const handleCloseCreateDialog = reactExports.useCallback(() => {
85487
85876
  setIsCreateDialogOpen(false);
85488
85877
  }, []);
85878
+ const handleOpenImportDialog = reactExports.useCallback(() => {
85879
+ setIsImportDialogOpen(true);
85880
+ }, []);
85881
+ const handleCloseImportDialog = reactExports.useCallback(() => {
85882
+ setIsImportDialogOpen(false);
85883
+ }, []);
85884
+ const handleExportAgent = reactExports.useCallback(
85885
+ async (agentId) => {
85886
+ try {
85887
+ const blob = await exportAgentMutation.mutateAsync(agentId);
85888
+ const agent = agents.find((a2) => a2.id === agentId);
85889
+ const agentName = agent ? agent.name.replace(/\s+/g, "-").toLowerCase() : agentId;
85890
+ const url = URL.createObjectURL(blob);
85891
+ const a = document.createElement("a");
85892
+ a.href = url;
85893
+ a.download = `${agentName}-export.zip`;
85894
+ document.body.appendChild(a);
85895
+ a.click();
85896
+ URL.revokeObjectURL(url);
85897
+ document.body.removeChild(a);
85898
+ } catch (error) {
85899
+ console.error("Failed to export agent:", error);
85900
+ }
85901
+ },
85902
+ [agents, exportAgentMutation]
85903
+ );
85489
85904
  const handleAgentCreated = reactExports.useCallback(
85490
85905
  (agentId) => {
85491
85906
  const fetchAndSelectAgent = async () => {
@@ -85529,7 +85944,9 @@ const ChatSidebar = ({
85529
85944
  title: "Agents",
85530
85945
  searchQuery,
85531
85946
  onSearchChange: (query) => setSearchQuery(query),
85532
- onNewAgentClick: handleOpenCreateDialog
85947
+ onNewAgentClick: handleOpenCreateDialog,
85948
+ onImportAgentClick: handleOpenImportDialog,
85949
+ importAgentTooltip: "Import an agent from a ZIP file"
85533
85950
  }
85534
85951
  ),
85535
85952
  isLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoadingContainer$4, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CircularProgress, { size: 40, thickness: 4 }) }) : isError ? /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -85559,6 +85976,7 @@ const ChatSidebar = ({
85559
85976
  agentName: agent.name,
85560
85977
  isAgentsPage: false,
85561
85978
  onViewAgentSettings: onNavigateToAgentSettings ? () => onNavigateToAgentSettings(agent.id) : void 0,
85979
+ onExportAgent: () => handleExportAgent(agent.id),
85562
85980
  onAgentDeleted: (deletedAgentId) => {
85563
85981
  if (selectedConversation === deletedAgentId) {
85564
85982
  onSelectConversation("");
@@ -85655,6 +86073,14 @@ const ChatSidebar = ({
85655
86073
  onAgentCreated: handleAgentCreated
85656
86074
  }
85657
86075
  ),
86076
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
86077
+ ImportAgentDialog,
86078
+ {
86079
+ open: isImportDialogOpen,
86080
+ onClose: handleCloseImportDialog,
86081
+ onAgentImported: handleAgentCreated
86082
+ }
86083
+ ),
85658
86084
  /* @__PURE__ */ jsxRuntimeExports.jsx(
85659
86085
  CompactPagination,
85660
86086
  {
@@ -86089,18 +86515,18 @@ const PageHeader = ({
86089
86515
  children
86090
86516
  ] });
86091
86517
  };
86092
- const SectionTitle$5 = styled$1(Typography)(({ theme: theme2 }) => ({
86518
+ const SectionTitle$6 = styled$1(Typography)(({ theme: theme2 }) => ({
86093
86519
  fontWeight: 600,
86094
86520
  marginBottom: theme2.spacing(2),
86095
86521
  display: "flex",
86096
86522
  alignItems: "center",
86097
86523
  color: theme2.palette.text.primary
86098
86524
  }));
86099
- const TitleIcon$2 = styled$1(FontAwesomeIcon)({
86525
+ const TitleIcon$3 = styled$1(FontAwesomeIcon)({
86100
86526
  marginRight: 10,
86101
86527
  color: "#f2f2f3"
86102
86528
  });
86103
- const InfoButton$1 = styled$1(IconButton)(({ theme: theme2 }) => ({
86529
+ const InfoButton$2 = styled$1(IconButton)(({ theme: theme2 }) => ({
86104
86530
  marginLeft: theme2.spacing(1),
86105
86531
  color: theme2.palette.primary.main,
86106
86532
  "&:hover": {
@@ -86193,8 +86619,8 @@ const ChatSettings = ({
86193
86619
  setLocalAgent(selectedAgent);
86194
86620
  }, [selectedAgent]);
86195
86621
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { mt: 4, mb: 3 }, children: [
86196
- /* @__PURE__ */ jsxRuntimeExports.jsxs(SectionTitle$5, { variant: "subtitle1", children: [
86197
- /* @__PURE__ */ jsxRuntimeExports.jsx(TitleIcon$2, { icon: faComments }),
86622
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SectionTitle$6, { variant: "subtitle1", children: [
86623
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TitleIcon$3, { icon: faComments }),
86198
86624
  "Chat Settings",
86199
86625
  /* @__PURE__ */ jsxRuntimeExports.jsx(
86200
86626
  Tooltip,
@@ -86202,7 +86628,7 @@ const ChatSettings = ({
86202
86628
  title: "Settings that control how the agent generates responses",
86203
86629
  arrow: true,
86204
86630
  placement: "top",
86205
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(InfoButton$1, { size: "small", children: /* @__PURE__ */ jsxRuntimeExports.jsx(FontAwesomeIcon, { icon: faInfoCircle, size: "xs" }) })
86631
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(InfoButton$2, { size: "small", children: /* @__PURE__ */ jsxRuntimeExports.jsx(FontAwesomeIcon, { icon: faInfoCircle, size: "xs" }) })
86206
86632
  }
86207
86633
  )
86208
86634
  ] }),
@@ -86722,14 +87148,14 @@ const HeaderContainer = styled$1(Box)(({ theme: theme2 }) => ({
86722
87148
  marginBottom: theme2.spacing(2),
86723
87149
  gap: theme2.spacing(2)
86724
87150
  }));
86725
- const SectionTitle$4 = styled$1(Typography)(({ theme: theme2 }) => ({
87151
+ const SectionTitle$5 = styled$1(Typography)(({ theme: theme2 }) => ({
86726
87152
  fontWeight: 600,
86727
87153
  marginBottom: theme2.spacing(2),
86728
87154
  display: "flex",
86729
87155
  alignItems: "center",
86730
87156
  color: theme2.palette.text.primary
86731
87157
  }));
86732
- const TitleIcon$1 = styled$1(FontAwesomeIcon)({
87158
+ const TitleIcon$2 = styled$1(FontAwesomeIcon)({
86733
87159
  marginRight: 10,
86734
87160
  color: "#f2f2f3"
86735
87161
  });
@@ -86862,8 +87288,8 @@ const GeneralSettings = ({
86862
87288
  ] }),
86863
87289
  /* @__PURE__ */ jsxRuntimeExports.jsx(Divider, { sx: { mb: 3 } }),
86864
87290
  /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { mb: 3 }, children: [
86865
- /* @__PURE__ */ jsxRuntimeExports.jsxs(SectionTitle$4, { variant: "subtitle1", children: [
86866
- /* @__PURE__ */ jsxRuntimeExports.jsx(TitleIcon$1, { icon: faInfoCircle }),
87291
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SectionTitle$5, { variant: "subtitle1", children: [
87292
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TitleIcon$2, { icon: faInfoCircle }),
86867
87293
  "Agent Information"
86868
87294
  ] }),
86869
87295
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -86927,18 +87353,18 @@ const GeneralSettings = ({
86927
87353
  ] })
86928
87354
  ] });
86929
87355
  };
86930
- const SectionTitle$3 = styled$1(Typography)(({ theme: theme2 }) => ({
87356
+ const SectionTitle$4 = styled$1(Typography)(({ theme: theme2 }) => ({
86931
87357
  fontWeight: 600,
86932
87358
  marginBottom: theme2.spacing(2),
86933
87359
  display: "flex",
86934
87360
  alignItems: "center",
86935
87361
  color: theme2.palette.text.primary
86936
87362
  }));
86937
- const TitleIcon = styled$1(FontAwesomeIcon)({
87363
+ const TitleIcon$1 = styled$1(FontAwesomeIcon)({
86938
87364
  marginRight: 10,
86939
87365
  color: "#f2f2f3"
86940
87366
  });
86941
- const InfoButton = styled$1(IconButton)(({ theme: theme2 }) => ({
87367
+ const InfoButton$1 = styled$1(IconButton)(({ theme: theme2 }) => ({
86942
87368
  marginLeft: theme2.spacing(1),
86943
87369
  color: theme2.palette.primary.main,
86944
87370
  "&:hover": {
@@ -86954,8 +87380,8 @@ const SecuritySettings = ({
86954
87380
  initialSelectedAgentId
86955
87381
  }) => {
86956
87382
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { mt: 1 }, children: [
86957
- /* @__PURE__ */ jsxRuntimeExports.jsxs(SectionTitle$3, { variant: "subtitle1", children: [
86958
- /* @__PURE__ */ jsxRuntimeExports.jsx(TitleIcon, { icon: faShieldAlt }),
87383
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SectionTitle$4, { variant: "subtitle1", children: [
87384
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TitleIcon$1, { icon: faShieldAlt }),
86959
87385
  "Security Prompt",
86960
87386
  /* @__PURE__ */ jsxRuntimeExports.jsx(
86961
87387
  Tooltip,
@@ -86963,7 +87389,7 @@ const SecuritySettings = ({
86963
87389
  title: "Security instructions that guide the agent's behavior and limitations",
86964
87390
  arrow: true,
86965
87391
  placement: "top",
86966
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(InfoButton, { size: "small", children: /* @__PURE__ */ jsxRuntimeExports.jsx(FontAwesomeIcon, { icon: faInfoCircle, size: "xs" }) })
87392
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(InfoButton$1, { size: "small", children: /* @__PURE__ */ jsxRuntimeExports.jsx(FontAwesomeIcon, { icon: faInfoCircle, size: "xs" }) })
86967
87393
  }
86968
87394
  )
86969
87395
  ] }),
@@ -86996,6 +87422,147 @@ const SecuritySettings = ({
86996
87422
  )
86997
87423
  ] });
86998
87424
  };
87425
+ const agentSystemPromptQueryKey = (agentId) => [
87426
+ "agent-system-prompt",
87427
+ agentId
87428
+ ];
87429
+ const useAgentSystemPrompt = (agentId) => {
87430
+ const { shouldEnableQuery } = useConnectivityGate();
87431
+ return useQuery({
87432
+ // Only enable the query if server is online and agent ID is provided
87433
+ enabled: shouldEnableQuery({ bypassInternetCheck: true }) && !!agentId,
87434
+ queryKey: agentSystemPromptQueryKey(agentId),
87435
+ queryFn: async () => {
87436
+ try {
87437
+ const client = createLocalOperatorClient(apiConfig.baseUrl);
87438
+ const response = await client.agents.getAgentSystemPrompt(agentId);
87439
+ if (response.status >= 400) {
87440
+ throw new Error(
87441
+ response.message || "Failed to fetch agent system prompt"
87442
+ );
87443
+ }
87444
+ return response.result?.system_prompt || "";
87445
+ } catch (error) {
87446
+ const errorMessage = error instanceof Error ? error.message : "An unknown error occurred while fetching agent system prompt";
87447
+ showErrorToast(errorMessage);
87448
+ throw error;
87449
+ }
87450
+ },
87451
+ // Prevent automatic refetches on window focus
87452
+ refetchOnWindowFocus: false,
87453
+ // Prevent stale time to avoid unnecessary refetches
87454
+ staleTime: 5e3
87455
+ });
87456
+ };
87457
+ const useUpdateAgentSystemPrompt = (agentId) => {
87458
+ const queryClient2 = useQueryClient();
87459
+ const client = createLocalOperatorClient(apiConfig.baseUrl);
87460
+ return useMutation({
87461
+ mutationFn: async (systemPrompt) => {
87462
+ try {
87463
+ const response = await client.agents.updateAgentSystemPrompt(
87464
+ agentId,
87465
+ systemPrompt
87466
+ );
87467
+ if (response.status >= 400) {
87468
+ throw new Error(
87469
+ response.message || "Failed to update agent system prompt"
87470
+ );
87471
+ }
87472
+ return response;
87473
+ } catch (error) {
87474
+ const errorMessage = error instanceof Error ? error.message : "An unknown error occurred while updating agent system prompt";
87475
+ y$1.error(errorMessage);
87476
+ throw error;
87477
+ }
87478
+ },
87479
+ onSuccess: async () => {
87480
+ await queryClient2.invalidateQueries({
87481
+ queryKey: agentSystemPromptQueryKey(agentId)
87482
+ });
87483
+ y$1.success("Agent system prompt updated successfully");
87484
+ },
87485
+ onError: (error) => {
87486
+ console.error("Error updating agent system prompt:", error);
87487
+ }
87488
+ });
87489
+ };
87490
+ const SectionTitle$3 = styled$1(Typography)(({ theme: theme2 }) => ({
87491
+ fontWeight: 600,
87492
+ marginBottom: theme2.spacing(2),
87493
+ display: "flex",
87494
+ alignItems: "center",
87495
+ color: theme2.palette.text.primary
87496
+ }));
87497
+ const TitleIcon = styled$1(FontAwesomeIcon)({
87498
+ marginRight: 10,
87499
+ color: "#f2f2f3"
87500
+ });
87501
+ const InfoButton = styled$1(IconButton)(({ theme: theme2 }) => ({
87502
+ marginLeft: theme2.spacing(1),
87503
+ color: theme2.palette.primary.main,
87504
+ "&:hover": {
87505
+ backgroundColor: alpha(theme2.palette.primary.main, 0.08)
87506
+ }
87507
+ }));
87508
+ const SystemPromptSettings = ({
87509
+ selectedAgent,
87510
+ savingField,
87511
+ setSavingField,
87512
+ refetchAgent,
87513
+ initialSelectedAgentId
87514
+ }) => {
87515
+ const { data: systemPrompt, isLoading } = useAgentSystemPrompt(
87516
+ selectedAgent.id
87517
+ );
87518
+ const updateSystemPromptMutation = useUpdateAgentSystemPrompt(
87519
+ selectedAgent.id
87520
+ );
87521
+ const [localSystemPrompt, setLocalSystemPrompt] = reactExports.useState("");
87522
+ reactExports.useEffect(() => {
87523
+ if (systemPrompt !== void 0) {
87524
+ setLocalSystemPrompt(systemPrompt);
87525
+ }
87526
+ }, [systemPrompt]);
87527
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { mt: 4 }, children: [
87528
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SectionTitle$3, { variant: "subtitle1", children: [
87529
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TitleIcon, { icon: faRobot }),
87530
+ "System Prompt",
87531
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
87532
+ Tooltip,
87533
+ {
87534
+ title: "System instructions that define the agent's behavior and capabilities",
87535
+ arrow: true,
87536
+ placement: "top",
87537
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(InfoButton, { size: "small", children: /* @__PURE__ */ jsxRuntimeExports.jsx(FontAwesomeIcon, { icon: faInfoCircle, size: "xs" }) })
87538
+ }
87539
+ )
87540
+ ] }),
87541
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
87542
+ EditableField,
87543
+ {
87544
+ value: localSystemPrompt,
87545
+ label: "",
87546
+ placeholder: isLoading ? "Loading system prompt..." : "Enter system prompt...",
87547
+ multiline: true,
87548
+ rows: 6,
87549
+ isSaving: savingField === "system_prompt",
87550
+ onSave: async (value) => {
87551
+ setSavingField("system_prompt");
87552
+ try {
87553
+ await updateSystemPromptMutation.mutateAsync(value);
87554
+ if (selectedAgent.id === initialSelectedAgentId && refetchAgent) {
87555
+ await refetchAgent();
87556
+ }
87557
+ } catch (error) {
87558
+ } finally {
87559
+ setSavingField(null);
87560
+ }
87561
+ }
87562
+ }
87563
+ )
87564
+ ] });
87565
+ };
86999
87566
  const DetailsPaper = styled$1(Paper)(({ theme: theme2 }) => ({
87000
87567
  padding: theme2.spacing(4),
87001
87568
  height: "100%",
@@ -87090,6 +87657,16 @@ const AgentSettings = ({
87090
87657
  refetchAgent,
87091
87658
  initialSelectedAgentId
87092
87659
  }
87660
+ ),
87661
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
87662
+ SystemPromptSettings,
87663
+ {
87664
+ selectedAgent,
87665
+ savingField,
87666
+ setSavingField,
87667
+ refetchAgent,
87668
+ initialSelectedAgentId
87669
+ }
87093
87670
  )
87094
87671
  ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(EmptyStateContainer$2, { children: [
87095
87672
  /* @__PURE__ */ jsxRuntimeExports.jsx(PlaceholderIcon, { icon: faRobot }),
@@ -87198,7 +87775,9 @@ const AgentsSidebar = ({
87198
87775
  const navigate = useNavigate();
87199
87776
  const [searchQuery, setSearchQuery] = reactExports.useState("");
87200
87777
  const [isCreateDialogOpen, setIsCreateDialogOpen] = reactExports.useState(false);
87778
+ const [isImportDialogOpen, setIsImportDialogOpen] = reactExports.useState(false);
87201
87779
  const perPage = 50;
87780
+ const exportAgentMutation = useExportAgent();
87202
87781
  const { page, setPage } = usePaginationParams();
87203
87782
  const [stableAgents, setStableAgents] = reactExports.useState([]);
87204
87783
  const prevFetchingRef = reactExports.useRef(false);
@@ -87234,6 +87813,32 @@ const AgentsSidebar = ({
87234
87813
  const handleCloseCreateDialog = reactExports.useCallback(() => {
87235
87814
  setIsCreateDialogOpen(false);
87236
87815
  }, []);
87816
+ const handleOpenImportDialog = reactExports.useCallback(() => {
87817
+ setIsImportDialogOpen(true);
87818
+ }, []);
87819
+ const handleCloseImportDialog = reactExports.useCallback(() => {
87820
+ setIsImportDialogOpen(false);
87821
+ }, []);
87822
+ const handleExportAgent = reactExports.useCallback(
87823
+ async (agentId) => {
87824
+ try {
87825
+ const blob = await exportAgentMutation.mutateAsync(agentId);
87826
+ const agent = agents.find((a2) => a2.id === agentId);
87827
+ const agentName = agent ? agent.name.replace(/\s+/g, "-").toLowerCase() : agentId;
87828
+ const url = URL.createObjectURL(blob);
87829
+ const a = document.createElement("a");
87830
+ a.href = url;
87831
+ a.download = `${agentName}-export.zip`;
87832
+ document.body.appendChild(a);
87833
+ a.click();
87834
+ URL.revokeObjectURL(url);
87835
+ document.body.removeChild(a);
87836
+ } catch (error) {
87837
+ console.error("Failed to export agent:", error);
87838
+ }
87839
+ },
87840
+ [agents, exportAgentMutation]
87841
+ );
87237
87842
  const handleAgentCreated = reactExports.useCallback(
87238
87843
  (agentId) => {
87239
87844
  const fetchAndSelectAgent = async () => {
@@ -87275,7 +87880,9 @@ const AgentsSidebar = ({
87275
87880
  title: "Agents",
87276
87881
  searchQuery,
87277
87882
  onSearchChange: (query) => setSearchQuery(query),
87278
- onNewAgentClick: handleOpenCreateDialog
87883
+ onNewAgentClick: handleOpenCreateDialog,
87884
+ onImportAgentClick: handleOpenImportDialog,
87885
+ importAgentTooltip: "Import an agent from a ZIP file"
87279
87886
  }
87280
87887
  ),
87281
87888
  isLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoadingContainer$3, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CircularProgress, { size: 40, thickness: 4 }) }) : isError ? /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -87308,6 +87915,7 @@ const AgentsSidebar = ({
87308
87915
  refetch();
87309
87916
  },
87310
87917
  onChatWithAgent: () => navigate(`/chat/${agent.id}`),
87918
+ onExportAgent: () => handleExportAgent(agent.id),
87311
87919
  buttonSx: {
87312
87920
  mr: 0.5,
87313
87921
  width: 32,
@@ -87371,6 +87979,14 @@ const AgentsSidebar = ({
87371
87979
  onAgentCreated: handleAgentCreated
87372
87980
  }
87373
87981
  ),
87982
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
87983
+ ImportAgentDialog,
87984
+ {
87985
+ open: isImportDialogOpen,
87986
+ onClose: handleCloseImportDialog,
87987
+ onAgentImported: handleAgentCreated
87988
+ }
87989
+ ),
87374
87990
  /* @__PURE__ */ jsxRuntimeExports.jsx(
87375
87991
  CompactPagination,
87376
87992
  {
@@ -87419,6 +88035,7 @@ const AgentsPage = () => {
87419
88035
  const { agentId, navigateToAgent, clearAgentId } = useAgentRouteParam();
87420
88036
  const navigate = useNavigate();
87421
88037
  const prevAgentIdRef = reactExports.useRef(agentId);
88038
+ const exportAgentMutation = useExportAgent();
87422
88039
  const { setLastAgentsPageAgentId, getLastAgentId, clearLastAgentId } = useAgentSelectionStore();
87423
88040
  const { data: agents = [] } = useAgents();
87424
88041
  const effectiveAgentId = agentId || getLastAgentId("agents");
@@ -87443,6 +88060,22 @@ const AgentsPage = () => {
87443
88060
  setLastAgentsPageAgentId(agentId);
87444
88061
  }
87445
88062
  }, [agentId, setLastAgentsPageAgentId]);
88063
+ const handleExportAgent = async () => {
88064
+ if (!selectedAgent) return;
88065
+ try {
88066
+ const blob = await exportAgentMutation.mutateAsync(selectedAgent.id);
88067
+ const url = URL.createObjectURL(blob);
88068
+ const a = document.createElement("a");
88069
+ a.href = url;
88070
+ a.download = `${selectedAgent.name.replace(/\s+/g, "-").toLowerCase()}-lo-agent.zip`;
88071
+ document.body.appendChild(a);
88072
+ a.click();
88073
+ URL.revokeObjectURL(url);
88074
+ document.body.removeChild(a);
88075
+ } catch (error) {
88076
+ console.error("Failed to export agent:", error);
88077
+ }
88078
+ };
87446
88079
  reactExports.useEffect(() => {
87447
88080
  if (initialAgent) {
87448
88081
  if (agentId !== prevAgentIdRef.current) {
@@ -87479,33 +88112,69 @@ const AgentsPage = () => {
87479
88112
  subtitle: "View, configure and manage your AI agents from a central dashboard"
87480
88113
  }
87481
88114
  ),
87482
- selectedAgent && /* @__PURE__ */ jsxRuntimeExports.jsx(Box, { sx: { position: "absolute", top: 0, right: 0 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
87483
- Tooltip,
88115
+ selectedAgent && /* @__PURE__ */ jsxRuntimeExports.jsxs(
88116
+ Box,
87484
88117
  {
87485
- title: `Chat with ${selectedAgent?.name || "this Agent"}`,
87486
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(
87487
- Button,
87488
- {
87489
- variant: "outlined",
87490
- color: "primary",
87491
- size: "small",
87492
- startIcon: /* @__PURE__ */ jsxRuntimeExports.jsx(FontAwesomeIcon, { icon: faComment }),
87493
- onClick: () => navigate(`/chat/${selectedAgent.id}`),
87494
- sx: {
87495
- borderRadius: 2,
87496
- textTransform: "none",
87497
- fontWeight: 500,
87498
- transition: "all 0.2s ease-in-out",
87499
- "&:hover": {
87500
- backgroundColor: alpha("#38C96A", 0.08),
87501
- transform: "translateY(-1px)"
87502
- }
87503
- },
87504
- children: "Chat"
87505
- }
87506
- )
88118
+ sx: {
88119
+ position: "absolute",
88120
+ top: 0,
88121
+ right: 0,
88122
+ display: "flex",
88123
+ gap: 1
88124
+ },
88125
+ children: [
88126
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Tooltip, { title: "Export Agent", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
88127
+ Button,
88128
+ {
88129
+ variant: "outlined",
88130
+ color: "primary",
88131
+ size: "small",
88132
+ startIcon: /* @__PURE__ */ jsxRuntimeExports.jsx(FontAwesomeIcon, { icon: faFileExport }),
88133
+ onClick: handleExportAgent,
88134
+ disabled: exportAgentMutation.isPending,
88135
+ sx: {
88136
+ borderRadius: 2,
88137
+ textTransform: "none",
88138
+ fontWeight: 500,
88139
+ transition: "all 0.2s ease-in-out",
88140
+ "&:hover": {
88141
+ backgroundColor: alpha("#38C96A", 0.08),
88142
+ transform: "translateY(-1px)"
88143
+ }
88144
+ },
88145
+ children: "Export"
88146
+ }
88147
+ ) }),
88148
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
88149
+ Tooltip,
88150
+ {
88151
+ title: `Chat with ${selectedAgent?.name || "this Agent"}`,
88152
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
88153
+ Button,
88154
+ {
88155
+ variant: "outlined",
88156
+ color: "primary",
88157
+ size: "small",
88158
+ startIcon: /* @__PURE__ */ jsxRuntimeExports.jsx(FontAwesomeIcon, { icon: faComment }),
88159
+ onClick: () => navigate(`/chat/${selectedAgent.id}`),
88160
+ sx: {
88161
+ borderRadius: 2,
88162
+ textTransform: "none",
88163
+ fontWeight: 500,
88164
+ transition: "all 0.2s ease-in-out",
88165
+ "&:hover": {
88166
+ backgroundColor: alpha("#38C96A", 0.08),
88167
+ transform: "translateY(-1px)"
88168
+ }
88169
+ },
88170
+ children: "Chat"
88171
+ }
88172
+ )
88173
+ }
88174
+ )
88175
+ ]
87507
88176
  }
87508
- ) })
88177
+ )
87509
88178
  ] }),
87510
88179
  /* @__PURE__ */ jsxRuntimeExports.jsx(AgentDetailsContainer, { sx: { opacity: selectedAgent ? 1 : 0.7 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
87511
88180
  AgentSettings,