@shuffleio/shuffle-mcps 0.0.39 → 0.0.45

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.
package/dist/index.js CHANGED
@@ -30,7 +30,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
30
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
31
31
 
32
32
  // fetchBreaker.ts
33
- var FAIL_THRESHOLD, ROLLING_WINDOW_MS, COOLDOWN_MS, PROBE_AFTER_MS, MAX_PROBE_BACKOFF_MS, HARD_BURST_THRESHOLD, HARD_BURST_WINDOW_MS, state, installed, allowedOrigins, getEntry, trip, reset, prune, keyFor, withStoredApiKey, synthetic503, registerProtectedOrigin, installFetchBreaker;
33
+ var FAIL_THRESHOLD, ROLLING_WINDOW_MS, COOLDOWN_MS, PROBE_AFTER_MS, MAX_PROBE_BACKOFF_MS, HARD_BURST_THRESHOLD, HARD_BURST_WINDOW_MS, state, installed, allowedOrigins, getEntry, trip, reset, prune, keyFor, withStoredAuth, synthetic503, registerProtectedOrigin, installFetchBreaker;
34
34
  var init_fetchBreaker = __esm({
35
35
  "fetchBreaker.ts"() {
36
36
  FAIL_THRESHOLD = 12;
@@ -84,7 +84,7 @@ var init_fetchBreaker = __esm({
84
84
  return null;
85
85
  }
86
86
  };
87
- withStoredApiKey = (input, init) => {
87
+ withStoredAuth = (input, init) => {
88
88
  if (typeof window === "undefined") return init;
89
89
  let urlStr;
90
90
  if (typeof input === "string") urlStr = input;
@@ -93,17 +93,35 @@ var init_fetchBreaker = __esm({
93
93
  else return init;
94
94
  try {
95
95
  const url = new URL(urlStr, window.location.href);
96
- const isKnownShuffleOrigin = allowedOrigins.has(url.origin) || url.origin === window.location.origin || url.hostname === "shuffler.io" || url.hostname.endsWith(".shuffler.io") || url.hostname === "tunnel.schemaless.org";
96
+ const isKnownShuffleOrigin = allowedOrigins.has(url.origin) || url.origin === window.location.origin || url.hostname === "shuffler.io" || url.hostname.endsWith(".shuffler.io") || url.hostname === "tunnel.schemaless.org" || url.hostname === "shuffle.security" || url.hostname.endsWith(".shuffle.security");
97
97
  if (!url.pathname.startsWith("/api/") || !isKnownShuffleOrigin) return init;
98
- const apiKey = window.localStorage.getItem("shuffle_api_key");
99
- if (!apiKey) return init;
98
+ if (url.pathname === "/api/v1/login" || url.pathname === "/api/v1/users/register") return init;
99
+ const sessionToken = window.localStorage.getItem("session_token");
100
+ const token = sessionToken && sessionToken.trim().length > 0 ? sessionToken.trim() : null;
100
101
  const requestHeaders = typeof Request !== "undefined" && input instanceof Request ? input.headers : void 0;
101
102
  const headers = new Headers(requestHeaders);
102
103
  if (init?.headers) {
103
104
  new Headers(init.headers).forEach((value, name) => headers.set(name, value));
104
105
  }
105
- if (!headers.has("Authorization")) headers.set("Authorization", `Bearer ${apiKey}`);
106
- return { ...init, headers };
106
+ if (token && !headers.has("Authorization")) {
107
+ headers.set("Authorization", `Bearer ${token}`);
108
+ }
109
+ if (!headers.has("Org-Id")) {
110
+ try {
111
+ const raw = window.localStorage.getItem("shuffle_user_info");
112
+ if (raw) {
113
+ const parsed = JSON.parse(raw);
114
+ const orgId = parsed?.active_org?.id;
115
+ if (orgId) headers.set("Org-Id", orgId);
116
+ }
117
+ } catch {
118
+ }
119
+ }
120
+ return {
121
+ ...init,
122
+ credentials: init?.credentials || "include",
123
+ headers
124
+ };
107
125
  } catch {
108
126
  return init;
109
127
  }
@@ -133,7 +151,7 @@ var init_fetchBreaker = __esm({
133
151
  installed = true;
134
152
  const original = window.fetch.bind(window);
135
153
  window.fetch = (async (input, init) => {
136
- const authenticatedInit = withStoredApiKey(input, init);
154
+ const authenticatedInit = withStoredAuth(input, init);
137
155
  const key = keyFor(input, authenticatedInit);
138
156
  if (!key) return original(input, authenticatedInit);
139
157
  const entry = getEntry(key);
@@ -211,6 +229,89 @@ var init_fetchBreaker = __esm({
211
229
  }
212
230
  });
213
231
 
232
+ // ../lib/shuffleUrls.ts
233
+ var readEnv, isManagedDomain, getShuffleCoreBaseUrl, getShuffleCoreUrl;
234
+ var init_shuffleUrls = __esm({
235
+ "../lib/shuffleUrls.ts"() {
236
+ readEnv = (key) => {
237
+ try {
238
+ const meta = new Function("try { return import.meta } catch { return undefined }")();
239
+ const val = meta?.env?.[key];
240
+ if (val && typeof val === "string" && val.trim().length > 0) {
241
+ return val.trim();
242
+ }
243
+ } catch {
244
+ }
245
+ if (typeof window !== "undefined") {
246
+ const win = window;
247
+ const runtimeConfig = win.__SHUFFLE_CONFIG__ || win._env_ || win.__ENV__;
248
+ if (runtimeConfig && typeof runtimeConfig === "object") {
249
+ const val = runtimeConfig[key] || runtimeConfig[key.replace(/^VITE_/, "")];
250
+ if (val && typeof val === "string" && val.trim().length > 0) {
251
+ return val.trim();
252
+ }
253
+ }
254
+ }
255
+ try {
256
+ if (typeof process !== "undefined" && process.env) {
257
+ const val = process.env[key] || process.env[key.replace(/^VITE_/, "")];
258
+ if (val && typeof val === "string" && val.trim().length > 0) {
259
+ return val.trim();
260
+ }
261
+ }
262
+ } catch {
263
+ }
264
+ return void 0;
265
+ };
266
+ isManagedDomain = () => {
267
+ if (typeof window === "undefined") return false;
268
+ const hostname = window.location.hostname.toLowerCase();
269
+ return hostname.includes("lovable") || hostname.includes("shuffler.io") || hostname.includes("shutdown.no") || hostname.includes("shuffle.security");
270
+ };
271
+ getShuffleCoreBaseUrl = () => {
272
+ const envUrl = readEnv("VITE_SHUFFLE_CORE_URL") || readEnv("SHUFFLE_CORE_URL") || readEnv("VITE_SHUFFLE_AUTOMATION_URL") || readEnv("VITE_SHUFFLE_URL");
273
+ if (envUrl) {
274
+ return envUrl.replace(/\/+$/, "");
275
+ }
276
+ if (typeof window !== "undefined") {
277
+ try {
278
+ const savedCore = localStorage.getItem("shuffle_core_url");
279
+ if (savedCore && savedCore.trim().length > 0) {
280
+ return savedCore.trim().replace(/\/+$/, "");
281
+ }
282
+ const customHost = localStorage.getItem("shuffle_custom_host_url");
283
+ if (customHost && customHost.trim().length > 0) {
284
+ const cleaned = customHost.trim().replace(/\/+$/, "");
285
+ if (cleaned.includes(":5001")) {
286
+ return cleaned.replace(":5001", ":3001");
287
+ }
288
+ return cleaned;
289
+ }
290
+ } catch {
291
+ }
292
+ }
293
+ if (typeof window !== "undefined") {
294
+ const hostname = window.location.hostname;
295
+ const isManaged = isManagedDomain();
296
+ if (isManaged) {
297
+ return "https://shuffler.io";
298
+ }
299
+ if (window.location.port === "3001") {
300
+ return window.location.origin;
301
+ }
302
+ return `${window.location.protocol}//${hostname}:3001`;
303
+ }
304
+ return "https://shuffler.io";
305
+ };
306
+ getShuffleCoreUrl = (path) => {
307
+ const base3 = getShuffleCoreBaseUrl();
308
+ if (!path) return base3;
309
+ const cleanPath = path.startsWith("/") ? path : `/${path}`;
310
+ return `${base3}${cleanPath}`;
311
+ };
312
+ }
313
+ });
314
+
214
315
  // ../lib/platform.ts
215
316
  var getPlatform, isCapacitorNative, isIosWebView, isAndroidWebView;
216
317
  var init_platform = __esm({
@@ -271,14 +372,16 @@ var init_platform = __esm({
271
372
  });
272
373
 
273
374
  // api.ts
274
- var DEV_BACKEND, PROD_BACKEND, CLOUD_DOMAINS, getEnvVar, isDevEnvironment, isCloudDomain, isCloud, isOnprem, getDefaultBaseUrl, REGION_STORAGE_KEY, readCachedRegion, cached, _regionUrl, readCachedCustomHost, _trackedOrgId, _hostBaseUrl, REGION_EVENT, _lastBroadcastUrl, persistRegion, isShufflerSubdomain, setRegionUrl, applyRegionFromPayload, resetRegionUrl, SHUFFLE_HOST_BASE_URL_EVENT, setHostBaseUrl, getHostBaseUrl, getTrackedOrgId, API_CONFIG, getApiUrl, shuffleFetch, getAuthHeader, hasShuffleAuth;
375
+ var DEV_BACKEND, PROD_BACKEND, SHUFFLE_AUTOMATION_URL, CLOUD_DOMAINS, getEnvVar, isDevEnvironment, isCloudDomain, isCloud, isOnprem, getDefaultBaseUrl, REGION_STORAGE_KEY, readCachedRegion, cached, _regionUrl, readCachedCustomHost, _trackedOrgId, _hostBaseUrl, REGION_EVENT, _lastBroadcastUrl, persistRegion, isShufflerSubdomain, setRegionUrl, applyRegionFromPayload, resetRegionUrl, SHUFFLE_HOST_BASE_URL_EVENT, setHostBaseUrl, getHostBaseUrl, getTrackedOrgId, LEGACY_API_KEY_STORAGE_KEY, clearAuthTokens, getSessionToken, setSessionToken, API_CONFIG, getApiUrl, shuffleFetch, getAuthHeader, hasShuffleAuth;
275
376
  var init_api = __esm({
276
377
  "api.ts"() {
277
378
  init_fetchBreaker();
379
+ init_shuffleUrls();
278
380
  init_platform();
279
381
  installFetchBreaker();
280
382
  DEV_BACKEND = "https://tunnel.schemaless.org";
281
- PROD_BACKEND = "https://shuffler.io";
383
+ PROD_BACKEND = "https://uk.shuffler.io";
384
+ SHUFFLE_AUTOMATION_URL = getShuffleCoreUrl("/new-dashboard");
282
385
  CLOUD_DOMAINS = ["shuffle.security", "www.shuffle.security", "security.shuffler.io", "shutdown.no", "www.shutdown.no"];
283
386
  getEnvVar = (key) => {
284
387
  try {
@@ -320,7 +423,11 @@ var init_api = __esm({
320
423
  const raw = localStorage.getItem(REGION_STORAGE_KEY);
321
424
  if (!raw) return { url: null, orgId: null };
322
425
  const parsed = JSON.parse(raw);
323
- return { url: parsed?.url || null, orgId: parsed?.orgId || null };
426
+ const url = parsed?.url ? parsed.url.replace(/\/+$/, "") : null;
427
+ if (url === "https://shuffler.io" || url === PROD_BACKEND) {
428
+ return { url: null, orgId: parsed?.orgId || null };
429
+ }
430
+ return { url, orgId: parsed?.orgId || null };
324
431
  } catch {
325
432
  return { url: null, orgId: null };
326
433
  }
@@ -330,8 +437,20 @@ var init_api = __esm({
330
437
  readCachedCustomHost = () => {
331
438
  if (typeof window === "undefined") return null;
332
439
  try {
440
+ const mode = localStorage.getItem("shuffle_selected_server_mode");
441
+ if (mode && mode !== "self-hosted") return null;
333
442
  const raw = localStorage.getItem("shuffle_custom_host_url");
334
- return raw ? raw.trim().replace(/\/+$/, "") : null;
443
+ const cleaned = raw ? raw.trim().replace(/\/+$/, "") : null;
444
+ if (!cleaned) return null;
445
+ if (cleaned === DEV_BACKEND && !isDevEnvironment() && mode !== "self-hosted") {
446
+ try {
447
+ localStorage.removeItem("shuffle_custom_host_url");
448
+ localStorage.removeItem("shuffle_selected_server_mode");
449
+ } catch {
450
+ }
451
+ return null;
452
+ }
453
+ return cleaned;
335
454
  } catch {
336
455
  return null;
337
456
  }
@@ -382,7 +501,8 @@ var init_api = __esm({
382
501
  _trackedOrgId = orgId || null;
383
502
  if (regionUrl && isShufflerSubdomain(regionUrl)) {
384
503
  const normalized = regionUrl.replace(/\/+$/, "");
385
- if (normalized !== PROD_BACKEND) {
504
+ const isDefaultCloud = normalized === PROD_BACKEND || normalized === "https://shuffler.io";
505
+ if (!isDefaultCloud) {
386
506
  _regionUrl = normalized;
387
507
  persistRegion(_regionUrl, _trackedOrgId);
388
508
  console.log(`[API] Region URL set to: ${_regionUrl}`);
@@ -435,6 +555,38 @@ var init_api = __esm({
435
555
  }
436
556
  getHostBaseUrl = () => _hostBaseUrl;
437
557
  getTrackedOrgId = () => _trackedOrgId;
558
+ LEGACY_API_KEY_STORAGE_KEY = "shuffle_api_key";
559
+ clearAuthTokens = () => {
560
+ try {
561
+ localStorage.removeItem("session_token");
562
+ localStorage.removeItem(LEGACY_API_KEY_STORAGE_KEY);
563
+ } catch {
564
+ }
565
+ };
566
+ getSessionToken = () => {
567
+ try {
568
+ if (typeof localStorage === "undefined") return null;
569
+ if (localStorage.getItem(LEGACY_API_KEY_STORAGE_KEY) !== null) {
570
+ localStorage.removeItem(LEGACY_API_KEY_STORAGE_KEY);
571
+ }
572
+ const stored = localStorage.getItem("session_token");
573
+ if (stored && stored.trim().length > 0 && stored !== "null" && stored !== "undefined") {
574
+ return stored.trim();
575
+ }
576
+ return null;
577
+ } catch {
578
+ return null;
579
+ }
580
+ };
581
+ setSessionToken = (token) => {
582
+ clearAuthTokens();
583
+ if (token && token.trim().length > 0) {
584
+ try {
585
+ localStorage.setItem("session_token", token.trim());
586
+ } catch {
587
+ }
588
+ }
589
+ };
438
590
  API_CONFIG = {
439
591
  // Shuffle backend URL — host override beats region URL beats default.
440
592
  get baseUrl() {
@@ -447,17 +599,16 @@ var init_api = __esm({
447
599
  },
448
600
  // API version
449
601
  version: "v1",
450
- // Get API key from localStorage (for local development)
602
+ /**
603
+ * DEPRECATED alias for the single session token. There is no separate
604
+ * API-key auth any more: exactly one session token may exist at a time.
605
+ */
451
606
  get apiKey() {
452
- return localStorage.getItem("shuffle_api_key");
607
+ return getSessionToken();
453
608
  },
454
- // Set API key in localStorage
609
+ /** DEPRECATED: writes/clears the single session token. */
455
610
  setApiKey(key) {
456
- if (key) {
457
- localStorage.setItem("shuffle_api_key", key);
458
- } else {
459
- localStorage.removeItem("shuffle_api_key");
460
- }
611
+ setSessionToken(key);
461
612
  }
462
613
  };
463
614
  getApiUrl = (endpoint) => {
@@ -476,11 +627,21 @@ var init_api = __esm({
476
627
  };
477
628
  getAuthHeader = (overrideOrgId) => {
478
629
  const headers = {};
479
- const apiKey = API_CONFIG.apiKey;
480
- if (apiKey) {
481
- headers["Authorization"] = `Bearer ${apiKey}`;
630
+ const token = getSessionToken();
631
+ if (token) {
632
+ headers["Authorization"] = `Bearer ${token}`;
633
+ }
634
+ let orgId = overrideOrgId ?? _trackedOrgId;
635
+ if (!orgId && typeof localStorage !== "undefined") {
636
+ try {
637
+ const raw = localStorage.getItem("shuffle_user_info");
638
+ if (raw) {
639
+ const parsed = JSON.parse(raw);
640
+ orgId = parsed?.active_org?.id || parsed?.org_id || null;
641
+ }
642
+ } catch {
643
+ }
482
644
  }
483
- const orgId = overrideOrgId ?? _trackedOrgId;
484
645
  if (orgId) {
485
646
  headers["Org-Id"] = orgId;
486
647
  }
@@ -488,8 +649,10 @@ var init_api = __esm({
488
649
  };
489
650
  hasShuffleAuth = () => {
490
651
  try {
491
- if (API_CONFIG.apiKey) return true;
492
- return !!(localStorage.getItem("session_token") || localStorage.getItem("shuffle_user_info"));
652
+ if (getSessionToken()) return true;
653
+ const info = localStorage.getItem("shuffle_user_info");
654
+ if (info && info.trim().length > 0 && info !== "null" && info !== "undefined") return true;
655
+ return false;
493
656
  } catch {
494
657
  return false;
495
658
  }
@@ -513,7 +676,9 @@ var init_appsCache = __esm({
513
676
  cache = /* @__PURE__ */ new Map();
514
677
  cacheKey = (o) => `${o.baseUrl}|${o.path || "/api/v1/apps"}|${o.apiKey || ""}|${o.orgId || ""}`;
515
678
  doFetch = async (o) => {
516
- const headers = {};
679
+ const headers = {
680
+ ...getAuthHeader(o.orgId || void 0)
681
+ };
517
682
  if (o.apiKey) headers["Authorization"] = `Bearer ${o.apiKey}`;
518
683
  if (o.orgId) headers["Org-Id"] = o.orgId;
519
684
  const response = await fetch(`${o.baseUrl}${o.path || "/api/v1/apps"}`, {
@@ -585,9 +750,14 @@ __export(index_exports, {
585
750
  AppDetailProvider: () => AppDetailProvider,
586
751
  AppSearchDrawer: () => AppSearchDrawer2,
587
752
  AppTitleHeader: () => AppTitleHeader2,
753
+ AskAiButton: () => AskAiButton2,
754
+ AskAiDrawer: () => AskAiDrawer2,
755
+ AskAiSidePanel: () => AskAiSidePanel2,
756
+ AskAiWidget: () => AskAiWidget2,
588
757
  CASES_PATTERNS: () => CASES_PATTERNS,
589
758
  COMMUNICATION_PATTERNS_NAMES: () => COMMUNICATION_PATTERNS_NAMES,
590
759
  DATASTORE_CATEGORIES: () => DATASTORE_CATEGORIES,
760
+ DEFAULT_AGENT_CONTEXT_RULES: () => DEFAULT_AGENT_CONTEXT_RULES,
591
761
  DEFAULT_AGENT_PROMPT_PREFIX: () => DEFAULT_AGENT_PROMPT_PREFIX,
592
762
  EDR_PATTERNS: () => EDR_PATTERNS,
593
763
  EMAIL_APP_PATTERNS: () => EMAIL_APP_PATTERNS,
@@ -609,6 +779,7 @@ __export(index_exports, {
609
779
  VULN_SCANNER_PATTERNS: () => VULN_SCANNER_PATTERNS,
610
780
  VideoEmbed: () => VideoEmbed,
611
781
  applyRegionFromPayload: () => applyRegionFromPayload,
782
+ clearPageContextChoice: () => clearPageContextChoice,
612
783
  default: () => index_default,
613
784
  deleteDatastoreItem: () => deleteDatastoreItem,
614
785
  deleteDatastoreItems: () => deleteDatastoreItems,
@@ -621,6 +792,8 @@ __export(index_exports, {
621
792
  fetchAppsViaApiConfig: () => fetchAppsViaApiConfig,
622
793
  findForwardTicketsWorkflow: () => findForwardTicketsWorkflow,
623
794
  findIngestTicketsWorkflow: () => findIngestTicketsWorkflow,
795
+ getActivePageEntityName: () => getActivePageEntityName,
796
+ getAgentContextRules: () => getAgentContextRules,
624
797
  getApiUrl: () => getApiUrl,
625
798
  getAuthHeader: () => getAuthHeader,
626
799
  getDatastoreByCategory: () => getDatastoreByCategory,
@@ -630,10 +803,12 @@ __export(index_exports, {
630
803
  getFailureInfo: () => getFailureInfo,
631
804
  getHostBaseUrl: () => getHostBaseUrl,
632
805
  getIngestionCategory: () => getIngestionCategory,
806
+ getPageContextChoice: () => getPageContextChoice,
633
807
  hasOutputWarning: () => hasOutputWarning,
634
808
  installFetchBreaker: () => installFetchBreaker,
635
809
  invalidateAppsCache: () => invalidateAppsCache,
636
810
  invalidateResolvedApps: () => invalidateResolvedApps,
811
+ isAgentRoute: () => isAgentRoute,
637
812
  isCloud: () => isCloud,
638
813
  isCloudDomain: () => isCloudDomain,
639
814
  isEmailApp: () => isEmailApp,
@@ -642,11 +817,14 @@ __export(index_exports, {
642
817
  isThreatIntelApp: () => isThreatIntelApp,
643
818
  isVulnScannerApp: () => isVulnScannerApp,
644
819
  isWorkflowScheduleStopped: () => isWorkflowScheduleStopped,
820
+ matchRoutePattern: () => matchRoutePattern,
645
821
  normalizeAppName: () => normalizeAppName,
646
822
  parseRunResult: () => parseRunResult,
647
823
  refreshAllIntegrationStatus: () => refreshAllIntegrationStatus,
824
+ registerAgentContextRule: () => registerAgentContextRule,
648
825
  registerProtectedOrigin: () => registerProtectedOrigin,
649
826
  resetRegionUrl: () => resetRegionUrl,
827
+ resolveAgentContext: () => resolveAgentContext,
650
828
  resolveApp: () => resolveApp,
651
829
  resolveApps: () => resolveApps,
652
830
  resolveVideoUrl: () => resolveVideoUrl,
@@ -656,6 +834,7 @@ __export(index_exports, {
656
834
  setDatastoreItem: () => setDatastoreItem,
657
835
  setDatastoreItems: () => setDatastoreItems,
658
836
  setHostBaseUrl: () => setHostBaseUrl,
837
+ setPageContextChoice: () => setPageContextChoice,
659
838
  setRegionUrl: () => setRegionUrl,
660
839
  setRuntimeOrgId: () => setRuntimeOrgId,
661
840
  setToastImpl: () => setToastImpl,
@@ -667,24 +846,26 @@ __export(index_exports, {
667
846
  useAppDetail: () => useAppDetail,
668
847
  useAppDetailOptional: () => useAppDetailOptional,
669
848
  useAppLookup: () => useAppLookup,
849
+ useContextAwareAgent: () => useContextAwareAgent,
670
850
  useSyncHostBaseUrl: () => useSyncHostBaseUrl
671
851
  });
672
852
  module.exports = __toCommonJS(index_exports);
673
- var import_shuffle_mcp3 = require("./shuffle-mcp-3U5AFQDK.css");
674
- var import_react33 = __toESM(require("react"));
853
+ var import_shuffle_mcp3 = require("./shuffle-mcp-S6QOJJG4.css");
854
+ var import_react36 = __toESM(require("react"));
675
855
 
676
856
  // ShuffleMcpThemeProvider.tsx
677
857
  var import_react = __toESM(require("react"));
678
858
  var import_material = require("@mui/material");
679
- var import_shuffle_mcp = require("./shuffle-mcp-3U5AFQDK.css");
859
+ var import_shuffle_mcp = require("./shuffle-mcp-S6QOJJG4.css");
680
860
  var import_jsx_runtime = require("react/jsx-runtime");
681
861
  var readHtmlDarkClass = () => {
682
862
  if (typeof document === "undefined") return false;
683
- return document.documentElement.classList.contains("dark");
863
+ return document.documentElement.classList.contains("dark") || Boolean(document.body) && document.body.classList.contains("dark");
684
864
  };
685
865
  var readAncestorDark = (anchor) => {
686
866
  if (!anchor || typeof document === "undefined") return null;
687
- const start = anchor.parentElement;
867
+ const ownRoot = anchor.closest("[data-shuffle-core-root], [data-shuffle-mcp-root]");
868
+ const start = ownRoot ? ownRoot.parentElement : anchor.parentElement;
688
869
  if (!start) return null;
689
870
  const scoped = start.closest('[data-shuffle-mode="dark"], [data-shuffle-mode="light"]');
690
871
  if (scoped) return scoped.getAttribute("data-shuffle-mode") === "dark";
@@ -703,12 +884,20 @@ var useAutoDarkClass = (enabled, anchorRef) => {
703
884
  recompute();
704
885
  const observer = new MutationObserver(recompute);
705
886
  observer.observe(document.documentElement, { attributes: true, attributeFilter: ["class"] });
706
- let node = anchorRef.current?.parentElement ?? null;
887
+ if (document.body) {
888
+ observer.observe(document.body, { attributes: true, attributeFilter: ["class"] });
889
+ }
890
+ const ownRoot = anchorRef.current?.closest("[data-shuffle-core-root], [data-shuffle-mcp-root]");
891
+ let node = ownRoot ? ownRoot.parentElement : anchorRef.current?.parentElement ?? null;
707
892
  while (node) {
708
893
  observer.observe(node, { attributes: true, attributeFilter: ["class", "data-shuffle-mode"] });
709
894
  node = node.parentElement;
710
895
  }
711
- return () => observer.disconnect();
896
+ window.addEventListener("shuffle:theme-change", recompute);
897
+ return () => {
898
+ observer.disconnect();
899
+ window.removeEventListener("shuffle:theme-change", recompute);
900
+ };
712
901
  }, [enabled, anchorRef]);
713
902
  return isDark;
714
903
  };
@@ -763,11 +952,26 @@ var buildComponentOverrides = (scopeClassName) => ({
763
952
  },
764
953
  // ---- Portaled surfaces: stamp scopeClassName so HSL tokens resolve ----
765
954
  MuiDrawer: {
766
- defaultProps: { slotProps: { paper: { className: scopeClassName } } }
955
+ defaultProps: { slotProps: { paper: { className: scopeClassName } } },
956
+ styleOverrides: {
957
+ paper: {
958
+ backgroundColor: "hsl(var(--sidebar-background, var(--card)))",
959
+ color: "hsl(var(--sidebar-foreground, var(--foreground)))",
960
+ borderRight: "1px solid hsl(var(--sidebar-border, var(--border)))"
961
+ }
962
+ }
767
963
  },
768
964
  MuiDialog: {
769
965
  defaultProps: { slotProps: { paper: { className: scopeClassName } } },
770
- styleOverrides: { root: { zIndex: 10010 } }
966
+ styleOverrides: {
967
+ root: { zIndex: 10010 },
968
+ paper: {
969
+ backgroundColor: "hsl(var(--card))",
970
+ color: "hsl(var(--card-foreground))",
971
+ border: "1px solid hsl(var(--border))",
972
+ boxShadow: "0 12px 32px rgba(0, 0, 0, 0.2)"
973
+ }
974
+ }
771
975
  },
772
976
  MuiMenu: {
773
977
  defaultProps: { slotProps: { paper: { className: scopeClassName } } },
@@ -776,7 +980,8 @@ var buildComponentOverrides = (scopeClassName) => ({
776
980
  paper: {
777
981
  backgroundColor: "hsl(var(--popover))",
778
982
  color: "hsl(var(--popover-foreground))",
779
- border: "1px solid hsl(var(--border))"
983
+ border: "1px solid hsl(var(--border))",
984
+ boxShadow: "0 4px 14px rgba(0, 0, 0, 0.12)"
780
985
  }
781
986
  }
782
987
  },
@@ -787,7 +992,8 @@ var buildComponentOverrides = (scopeClassName) => ({
787
992
  paper: {
788
993
  backgroundColor: "hsl(var(--popover))",
789
994
  color: "hsl(var(--popover-foreground))",
790
- border: "1px solid hsl(var(--border))"
995
+ border: "1px solid hsl(var(--border))",
996
+ boxShadow: "0 4px 14px rgba(0, 0, 0, 0.12)"
791
997
  }
792
998
  }
793
999
  },
@@ -798,6 +1004,23 @@ var buildComponentOverrides = (scopeClassName) => ({
798
1004
  // Render above app drawers, dialogs, menus, and popovers.
799
1005
  popper: { sx: { zIndex: 10030 } }
800
1006
  }
1007
+ },
1008
+ styleOverrides: {
1009
+ tooltip: {
1010
+ backgroundColor: "hsl(var(--popover))",
1011
+ color: "hsl(var(--popover-foreground))",
1012
+ border: "1px solid hsl(var(--border))",
1013
+ boxShadow: "0 4px 14px rgba(0, 0, 0, 0.12)",
1014
+ fontSize: "0.75rem",
1015
+ borderRadius: 8
1016
+ },
1017
+ arrow: {
1018
+ color: "hsl(var(--popover))",
1019
+ "&::before": {
1020
+ border: "1px solid hsl(var(--border))",
1021
+ boxSizing: "border-box"
1022
+ }
1023
+ }
801
1024
  }
802
1025
  },
803
1026
  MuiDivider: {
@@ -814,7 +1037,7 @@ var ShuffleMcpThemeProvider = ({
814
1037
  const autoIsDark = useAutoDarkClass(mode === "auto", anchorRef);
815
1038
  const effectiveDark = mode === "auto" ? autoIsDark : mode === "dark";
816
1039
  const sameAsParent = parentCtx !== null && parentCtx.isDark === effectiveDark;
817
- const scopeClassName = effectiveDark ? "shuffle-mcp-scope dark" : "shuffle-mcp-scope";
1040
+ const scopeClassName = effectiveDark ? "shuffle-mcp-scope dark" : "shuffle-mcp-scope light";
818
1041
  const resolvedModeAttr = effectiveDark ? "dark" : "light";
819
1042
  const merged = import_react.default.useMemo(
820
1043
  () => (0, import_material.createTheme)({
@@ -1221,7 +1444,7 @@ var keyFor2 = (input, init) => {
1221
1444
  return null;
1222
1445
  }
1223
1446
  };
1224
- var withStoredApiKey2 = (input, init) => {
1447
+ var withStoredAuth2 = (input, init) => {
1225
1448
  if (typeof window === "undefined") return init;
1226
1449
  let urlStr;
1227
1450
  if (typeof input === "string") urlStr = input;
@@ -1230,17 +1453,35 @@ var withStoredApiKey2 = (input, init) => {
1230
1453
  else return init;
1231
1454
  try {
1232
1455
  const url = new URL(urlStr, window.location.href);
1233
- const isKnownShuffleOrigin = allowedOrigins2.has(url.origin) || url.origin === window.location.origin || url.hostname === "shuffler.io" || url.hostname.endsWith(".shuffler.io") || url.hostname === "tunnel.schemaless.org";
1456
+ const isKnownShuffleOrigin = allowedOrigins2.has(url.origin) || url.origin === window.location.origin || url.hostname === "shuffler.io" || url.hostname.endsWith(".shuffler.io") || url.hostname === "tunnel.schemaless.org" || url.hostname === "shuffle.security" || url.hostname.endsWith(".shuffle.security");
1234
1457
  if (!url.pathname.startsWith("/api/") || !isKnownShuffleOrigin) return init;
1235
- const apiKey = window.localStorage.getItem("shuffle_api_key");
1236
- if (!apiKey) return init;
1458
+ if (url.pathname === "/api/v1/login" || url.pathname === "/api/v1/users/register") return init;
1459
+ const sessionToken = window.localStorage.getItem("session_token");
1460
+ const token = sessionToken && sessionToken.trim().length > 0 ? sessionToken.trim() : null;
1237
1461
  const requestHeaders = typeof Request !== "undefined" && input instanceof Request ? input.headers : void 0;
1238
1462
  const headers = new Headers(requestHeaders);
1239
1463
  if (init?.headers) {
1240
1464
  new Headers(init.headers).forEach((value, name) => headers.set(name, value));
1241
1465
  }
1242
- if (!headers.has("Authorization")) headers.set("Authorization", `Bearer ${apiKey}`);
1243
- return { ...init, headers };
1466
+ if (token && !headers.has("Authorization")) {
1467
+ headers.set("Authorization", `Bearer ${token}`);
1468
+ }
1469
+ if (!headers.has("Org-Id")) {
1470
+ try {
1471
+ const raw = window.localStorage.getItem("shuffle_user_info");
1472
+ if (raw) {
1473
+ const parsed = JSON.parse(raw);
1474
+ const orgId = parsed?.active_org?.id;
1475
+ if (orgId) headers.set("Org-Id", orgId);
1476
+ }
1477
+ } catch {
1478
+ }
1479
+ }
1480
+ return {
1481
+ ...init,
1482
+ credentials: init?.credentials || "include",
1483
+ headers
1484
+ };
1244
1485
  } catch {
1245
1486
  return init;
1246
1487
  }
@@ -1279,7 +1520,7 @@ var installFetchBreaker2 = () => {
1279
1520
  installed2 = true;
1280
1521
  const original = window.fetch.bind(window);
1281
1522
  window.fetch = (async (input, init) => {
1282
- const authenticatedInit = withStoredApiKey2(input, init);
1523
+ const authenticatedInit = withStoredAuth2(input, init);
1283
1524
  const key = keyFor2(input, authenticatedInit);
1284
1525
  if (!key) return original(input, authenticatedInit);
1285
1526
  const entry = getEntry2(key);
@@ -2545,10 +2786,10 @@ async function backfillAppImages(dedupedApps) {
2545
2786
  }
2546
2787
 
2547
2788
  // ingestionDetection.ts
2548
- var EMAIL_APP_PATTERNS = ["gmail", "outlook", "email", "microsoft_graph", "office365", "exchange", "imap", "smtp"];
2789
+ var EMAIL_APP_PATTERNS = ["gmail", "outlook", "email", "microsoft_graph", "office365", "exchange", "imap", "smtp", "proofpoint", "mimecast", "phishing"];
2549
2790
  var CASES_PATTERNS = ["jira", "servicenow", "zendesk", "freshdesk", "pagerduty", "opsgenie", "ticket", "itsm", "salesforce", "thehive", "cortex"];
2550
- var EDR_PATTERNS = ["crowdstrike", "sentinelone", "carbon black", "defender", "cylance", "sophos", "trellix", "vmware", "tanium", "falcon", "edr"];
2551
- var SIEM_PATTERNS = ["splunk", "elastic", "qradar", "sentinel", "chronicle", "logrhythm", "sumo logic", "graylog", "wazuh", "siem", "arcsight"];
2791
+ var EDR_PATTERNS = ["crowdstrike", "sentinelone", "carbon black", "cybereason", "microsoft defender", "defender for endpoint", "defender", "cylance", "sophos", "trellix", "tanium", "crowdstrike falcon", "falcon", "vmware", "edr", "xdr"];
2792
+ var SIEM_PATTERNS = ["splunk", "elastic", "qradar", "microsoft sentinel", "azure sentinel", "sentinel", "chronicle", "logrhythm", "sumo logic", "graylog", "wazuh", "siem", "arcsight"];
2552
2793
  var THREAT_INTEL_PATTERNS = ["virustotal", "shodan", "alienvault", "otx", "threatcrowd", "urlscan", "hybrid-analysis", "abuseipdb", "greynoise", "urlhaus", "malwarebazaar", "threatfox", "misp", "opencti", "recorded future", "mandiant", "crowdstrike intel", "intel471", "flashpoint", "domaintools"];
2553
2794
  var COMMUNICATION_PATTERNS_NAMES = ["slack", "teams", "discord", "mattermost", "telegram", "webhook"];
2554
2795
  var VULN_SCANNER_PATTERNS = ["qualys", "tenable", "nessus", "rapid7", "nexpose", "snyk", "sonarqube", "trivy", "grype", "anchore", "dependabot", "aws_inspector", "azure_defender", "gcp_scc", "scout", "prowler", "checkov", "wiz", "orca", "lacework", "prisma"];
@@ -2745,6 +2986,67 @@ var fetchAppConfig = (appId) => {
2745
2986
 
2746
2987
  // authenticatedApps.ts
2747
2988
  init_api();
2989
+
2990
+ // ../Shuffle-Core/views/appsFetchCache.ts
2991
+ var JSON_CACHE_TTL_MS = 12e4;
2992
+ var WORKFLOWS_TTL_MS = 5 * 60 * 1e3;
2993
+ var _jsonCache = /* @__PURE__ */ new Map();
2994
+ var _inFlightPromises = /* @__PURE__ */ new Map();
2995
+ function normalizeEndpointKey(url, init) {
2996
+ let pathname = url;
2997
+ let search = "";
2998
+ try {
2999
+ const parsed = new URL(url, "https://shuffle.internal");
3000
+ pathname = parsed.pathname;
3001
+ search = parsed.search;
3002
+ } catch {
3003
+ }
3004
+ let orgId = "";
3005
+ if (init?.headers) {
3006
+ if (init.headers instanceof Headers) {
3007
+ orgId = init.headers.get("Org-Id") || "";
3008
+ } else if (Array.isArray(init.headers)) {
3009
+ const match = init.headers.find(([k]) => k.toLowerCase() === "org-id");
3010
+ if (match) orgId = match[1];
3011
+ } else if (typeof init.headers === "object") {
3012
+ orgId = init.headers["Org-Id"] || init.headers["org-id"] || "";
3013
+ }
3014
+ }
3015
+ return `${pathname}${search}${orgId ? `::org:${orgId}` : ""}`;
3016
+ }
3017
+ async function fetchJsonCached(url, init, ttlMs = JSON_CACHE_TTL_MS, force = false) {
3018
+ const cacheKey3 = normalizeEndpointKey(url, init);
3019
+ const now = Date.now();
3020
+ if (!force) {
3021
+ const cached2 = _jsonCache.get(cacheKey3);
3022
+ if (cached2 && now - cached2.ts < ttlMs) {
3023
+ return cached2.data;
3024
+ }
3025
+ const inFlight = _inFlightPromises.get(cacheKey3);
3026
+ if (inFlight) {
3027
+ return inFlight;
3028
+ }
3029
+ }
3030
+ const p = (async () => {
3031
+ try {
3032
+ const res = await fetch(url, init);
3033
+ if (!res.ok) {
3034
+ const prev = _jsonCache.get(cacheKey3);
3035
+ if (prev) return prev.data;
3036
+ throw new Error(`HTTP ${res.status}`);
3037
+ }
3038
+ const data = await res.json();
3039
+ _jsonCache.set(cacheKey3, { ts: Date.now(), data });
3040
+ return data;
3041
+ } finally {
3042
+ _inFlightPromises.delete(cacheKey3);
3043
+ }
3044
+ })();
3045
+ _inFlightPromises.set(cacheKey3, p);
3046
+ return p;
3047
+ }
3048
+
3049
+ // authenticatedApps.ts
2748
3050
  var TTL_MS2 = 3e4;
2749
3051
  var cache3 = /* @__PURE__ */ new Map();
2750
3052
  var cacheKey2 = (crossOrgId) => crossOrgId || "";
@@ -2767,14 +3069,16 @@ var doFetch3 = async (crossOrgId) => {
2767
3069
  const headers = {
2768
3070
  ...getAuthHeader(crossOrgId ?? void 0)
2769
3071
  };
2770
- const response = await fetch(getApiUrl("/api/v1/apps/authentication"), {
2771
- credentials: "include",
2772
- headers
2773
- });
2774
- if (!response.ok) return [];
2775
- const result = await response.json();
2776
- const data = result?.data || result;
2777
- return Array.isArray(data) ? applyValidationStaleness(data) : [];
3072
+ try {
3073
+ const result = await fetchJsonCached(getApiUrl("/api/v1/apps/authentication"), {
3074
+ credentials: "include",
3075
+ headers
3076
+ });
3077
+ const data = result?.data || result;
3078
+ return Array.isArray(data) ? applyValidationStaleness(data) : [];
3079
+ } catch {
3080
+ return [];
3081
+ }
2778
3082
  };
2779
3083
  var fetchAuthenticatedApps = (crossOrgId) => {
2780
3084
  const key = cacheKey2(crossOrgId);
@@ -4775,6 +5079,14 @@ var NO_AUTH_APPS = /* @__PURE__ */ new Set([
4775
5079
  "shuffle_files",
4776
5080
  "shuffles_app_management",
4777
5081
  "shuffle_tools",
5082
+ "shuffle_assets",
5083
+ "shuffle_software",
5084
+ "shuffle_packages",
5085
+ "shuffle_vulnerabilities",
5086
+ "assets",
5087
+ "software",
5088
+ "packages",
5089
+ "vulnerabilities",
4778
5090
  "tools",
4779
5091
  "http",
4780
5092
  "singul",
@@ -7686,7 +7998,7 @@ function AppDetailDrawer({
7686
7998
  }
7687
7999
 
7688
8000
  // views/ShuffleMCP.tsx
7689
- var import_shuffle_mcp2 = require("./shuffle-mcp-3U5AFQDK.css");
8001
+ var import_shuffle_mcp2 = require("./shuffle-mcp-S6QOJJG4.css");
7690
8002
  init_appsCache();
7691
8003
 
7692
8004
  // components/SegmentedControl.tsx
@@ -8713,11 +9025,90 @@ var import_react14 = require("react");
8713
9025
  var import_material14 = require("@mui/material");
8714
9026
  var import_lucide_react7 = require("lucide-react");
8715
9027
 
9028
+ // ../Shuffle-Core/lib/shuffleUrls.ts
9029
+ var readEnv2 = (key) => {
9030
+ try {
9031
+ const meta = new Function("try { return import.meta } catch { return undefined }")();
9032
+ const val = meta?.env?.[key];
9033
+ if (val && typeof val === "string" && val.trim().length > 0) {
9034
+ return val.trim();
9035
+ }
9036
+ } catch {
9037
+ }
9038
+ if (typeof window !== "undefined") {
9039
+ const win = window;
9040
+ const runtimeConfig = win.__SHUFFLE_CONFIG__ || win._env_ || win.__ENV__;
9041
+ if (runtimeConfig && typeof runtimeConfig === "object") {
9042
+ const val = runtimeConfig[key] || runtimeConfig[key.replace(/^VITE_/, "")];
9043
+ if (val && typeof val === "string" && val.trim().length > 0) {
9044
+ return val.trim();
9045
+ }
9046
+ }
9047
+ }
9048
+ try {
9049
+ if (typeof process !== "undefined" && process.env) {
9050
+ const val = process.env[key] || process.env[key.replace(/^VITE_/, "")];
9051
+ if (val && typeof val === "string" && val.trim().length > 0) {
9052
+ return val.trim();
9053
+ }
9054
+ }
9055
+ } catch {
9056
+ }
9057
+ return void 0;
9058
+ };
9059
+ var isManagedDomain2 = () => {
9060
+ if (typeof window === "undefined") return false;
9061
+ const hostname = window.location.hostname.toLowerCase();
9062
+ return hostname.includes("lovable") || hostname.includes("shuffler.io") || hostname.includes("shutdown.no") || hostname.includes("shuffle.security");
9063
+ };
9064
+ var getShuffleCoreBaseUrl2 = () => {
9065
+ const envUrl = readEnv2("VITE_SHUFFLE_CORE_URL") || readEnv2("SHUFFLE_CORE_URL") || readEnv2("VITE_SHUFFLE_AUTOMATION_URL") || readEnv2("VITE_SHUFFLE_URL");
9066
+ if (envUrl) {
9067
+ return envUrl.replace(/\/+$/, "");
9068
+ }
9069
+ if (typeof window !== "undefined") {
9070
+ try {
9071
+ const savedCore = localStorage.getItem("shuffle_core_url");
9072
+ if (savedCore && savedCore.trim().length > 0) {
9073
+ return savedCore.trim().replace(/\/+$/, "");
9074
+ }
9075
+ const customHost = localStorage.getItem("shuffle_custom_host_url");
9076
+ if (customHost && customHost.trim().length > 0) {
9077
+ const cleaned = customHost.trim().replace(/\/+$/, "");
9078
+ if (cleaned.includes(":5001")) {
9079
+ return cleaned.replace(":5001", ":3001");
9080
+ }
9081
+ return cleaned;
9082
+ }
9083
+ } catch {
9084
+ }
9085
+ }
9086
+ if (typeof window !== "undefined") {
9087
+ const hostname = window.location.hostname;
9088
+ const isManaged = isManagedDomain2();
9089
+ if (isManaged) {
9090
+ return "https://shuffler.io";
9091
+ }
9092
+ if (window.location.port === "3001") {
9093
+ return window.location.origin;
9094
+ }
9095
+ return `${window.location.protocol}//${hostname}:3001`;
9096
+ }
9097
+ return "https://shuffler.io";
9098
+ };
9099
+ var getShuffleCoreUrl2 = (path) => {
9100
+ const base3 = getShuffleCoreBaseUrl2();
9101
+ if (!path) return base3;
9102
+ const cleanPath = path.startsWith("/") ? path : `/${path}`;
9103
+ return `${base3}${cleanPath}`;
9104
+ };
9105
+
8716
9106
  // ../Shuffle-Core/api.ts
8717
9107
  init_platform();
8718
9108
  installFetchBreaker2();
8719
9109
  var DEV_BACKEND2 = "https://tunnel.schemaless.org";
8720
- var PROD_BACKEND2 = "https://shuffler.io";
9110
+ var PROD_BACKEND2 = "https://uk.shuffler.io";
9111
+ var SHUFFLE_AUTOMATION_URL2 = getShuffleCoreUrl2("/new-dashboard");
8721
9112
  var CLOUD_DOMAINS2 = ["shuffle.security", "www.shuffle.security", "security.shuffler.io", "shutdown.no", "www.shutdown.no"];
8722
9113
  var getEnvVar2 = (key) => {
8723
9114
  try {
@@ -8753,7 +9144,11 @@ var _cachedRegion = (() => {
8753
9144
  const raw = localStorage.getItem(REGION_STORAGE_KEY2);
8754
9145
  if (!raw) return { url: null, orgId: null };
8755
9146
  const parsed = JSON.parse(raw);
8756
- return { url: parsed?.url || null, orgId: parsed?.orgId || null };
9147
+ const url = parsed?.url ? parsed.url.replace(/\/+$/, "") : null;
9148
+ if (url === "https://shuffler.io" || url === PROD_BACKEND2) {
9149
+ return { url: null, orgId: parsed?.orgId || null };
9150
+ }
9151
+ return { url, orgId: parsed?.orgId || null };
8757
9152
  } catch {
8758
9153
  return { url: null, orgId: null };
8759
9154
  }
@@ -8761,8 +9156,20 @@ var _cachedRegion = (() => {
8761
9156
  var _readCachedCustomHost = () => {
8762
9157
  if (typeof window === "undefined") return null;
8763
9158
  try {
9159
+ const mode = localStorage.getItem("shuffle_selected_server_mode");
9160
+ if (mode && mode !== "self-hosted") return null;
8764
9161
  const raw = localStorage.getItem("shuffle_custom_host_url");
8765
- return raw ? raw.trim().replace(/\/+$/, "") : null;
9162
+ const cleaned = raw ? raw.trim().replace(/\/+$/, "") : null;
9163
+ if (!cleaned) return null;
9164
+ if (cleaned === DEV_BACKEND2 && !isDevEnvironment3() && mode !== "self-hosted") {
9165
+ try {
9166
+ localStorage.removeItem("shuffle_custom_host_url");
9167
+ localStorage.removeItem("shuffle_selected_server_mode");
9168
+ } catch {
9169
+ }
9170
+ return null;
9171
+ }
9172
+ return cleaned;
8766
9173
  } catch {
8767
9174
  return null;
8768
9175
  }
@@ -8815,6 +9222,38 @@ if (typeof window !== "undefined") {
8815
9222
  } catch {
8816
9223
  }
8817
9224
  }
9225
+ var LEGACY_API_KEY_STORAGE_KEY2 = "shuffle_api_key";
9226
+ var clearAuthTokens2 = () => {
9227
+ try {
9228
+ localStorage.removeItem("session_token");
9229
+ localStorage.removeItem(LEGACY_API_KEY_STORAGE_KEY2);
9230
+ } catch {
9231
+ }
9232
+ };
9233
+ var getSessionToken2 = () => {
9234
+ try {
9235
+ if (typeof localStorage === "undefined") return null;
9236
+ if (localStorage.getItem(LEGACY_API_KEY_STORAGE_KEY2) !== null) {
9237
+ localStorage.removeItem(LEGACY_API_KEY_STORAGE_KEY2);
9238
+ }
9239
+ const stored = localStorage.getItem("session_token");
9240
+ if (stored && stored.trim().length > 0 && stored !== "null" && stored !== "undefined") {
9241
+ return stored.trim();
9242
+ }
9243
+ return null;
9244
+ } catch {
9245
+ return null;
9246
+ }
9247
+ };
9248
+ var setSessionToken2 = (token) => {
9249
+ clearAuthTokens2();
9250
+ if (token && token.trim().length > 0) {
9251
+ try {
9252
+ localStorage.setItem("session_token", token.trim());
9253
+ } catch {
9254
+ }
9255
+ }
9256
+ };
8818
9257
  var API_CONFIG5 = {
8819
9258
  get baseUrl() {
8820
9259
  const url = _hostBaseUrl2 || (isDevEnvironment3() || getEnvVar2("VITE_SHUFFLE_API_URL") ? getDefaultBaseUrl2() : _regionUrl2 || getDefaultBaseUrl2());
@@ -8825,28 +9264,36 @@ var API_CONFIG5 = {
8825
9264
  return url;
8826
9265
  },
8827
9266
  version: "v1",
9267
+ /** DEPRECATED alias for the single session token. */
8828
9268
  get apiKey() {
8829
- try {
8830
- return typeof localStorage !== "undefined" ? localStorage.getItem("shuffle_api_key") : null;
8831
- } catch {
8832
- return null;
8833
- }
9269
+ return getSessionToken2();
8834
9270
  },
9271
+ /** DEPRECATED: writes/clears the single session token. */
8835
9272
  setApiKey(key) {
8836
- try {
8837
- if (key) localStorage.setItem("shuffle_api_key", key);
8838
- else localStorage.removeItem("shuffle_api_key");
8839
- } catch {
8840
- }
9273
+ setSessionToken2(key);
8841
9274
  }
8842
9275
  };
8843
9276
  var getApiUrl2 = (endpoint) => `${API_CONFIG5.baseUrl}${endpoint}`;
8844
9277
  var getAuthHeader2 = (overrideOrgId) => {
8845
9278
  const headers = {};
8846
- const apiKey = API_CONFIG5.apiKey;
8847
- if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
8848
- const orgId = overrideOrgId ?? _trackedOrgId2;
8849
- if (orgId) headers["Org-Id"] = orgId;
9279
+ const token = getSessionToken2();
9280
+ if (token) {
9281
+ headers["Authorization"] = `Bearer ${token}`;
9282
+ }
9283
+ let orgId = overrideOrgId ?? _trackedOrgId2;
9284
+ if (!orgId && typeof localStorage !== "undefined") {
9285
+ try {
9286
+ const raw = localStorage.getItem("shuffle_user_info");
9287
+ if (raw) {
9288
+ const parsed = JSON.parse(raw);
9289
+ orgId = parsed?.active_org?.id || parsed?.org_id || null;
9290
+ }
9291
+ } catch {
9292
+ }
9293
+ }
9294
+ if (orgId) {
9295
+ headers["Org-Id"] = orgId;
9296
+ }
8850
9297
  return headers;
8851
9298
  };
8852
9299
 
@@ -10823,8 +11270,8 @@ var prettyAppName = (name) => name.replace(/[_-]+/g, " ").trim().split(/\s+/).ma
10823
11270
  var AGENT_PRESETS = [
10824
11271
  {
10825
11272
  id: "build-workflows",
10826
- label: "Build Workflows",
10827
- description: "Designs and edits Shuffle workflows for you \u2014 pick apps, wire actions, and iterate on automations from a description.",
11273
+ label: "Build Workflow",
11274
+ description: "Designs and builds Shuffle workflows for you \u2014 pick apps, wire actions, and iterate on automations from a description.",
10828
11275
  defaultPrompt: "Build a Shuffle workflow that ",
10829
11276
  icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.Workflow, { size: 16 }),
10830
11277
  enabled: true,
@@ -10833,8 +11280,8 @@ var AGENT_PRESETS = [
10833
11280
  },
10834
11281
  {
10835
11282
  id: "incident-response",
10836
- label: "Incident Response Agent",
10837
- description: "Triages incidents: enriches observables, correlates related cases, and proposes next actions with rationale.",
11283
+ label: "Incident Handler",
11284
+ description: "Supports your incident investigation \u2014 extracts observables, correlates related alerts, analyzes attack scope, and proposes tiered containment options with human confirmation.",
10838
11285
  defaultPrompt: "Investigate this incident and recommend next steps: ",
10839
11286
  icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.ShieldAlert, { size: 16 }),
10840
11287
  enabled: true,
@@ -10855,28 +11302,41 @@ var AGENT_PRESETS = [
10855
11302
  label: "Support Agent",
10856
11303
  description: 'Uses the platform on your behalf \u2014 navigates settings, runs diagnostics, and answers "how do I\u2026" questions.',
10857
11304
  defaultPrompt: "Help me with the following on the Shuffle platform: ",
10858
- icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.LifeBuoy, { size: 16 })
11305
+ icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.LifeBuoy, { size: 16 }),
11306
+ enabled: true,
11307
+ defaultApps: [{ name: "shuffle_tools" }]
10859
11308
  },
10860
11309
  {
10861
11310
  id: "vulnerability",
10862
- label: "Vulnerability Agent",
10863
- description: "Reviews vulnerabilities, ranks by exploitability and asset criticality, and drafts remediation plans.",
10864
- defaultPrompt: "Review my current vulnerabilities and prioritize them by ",
10865
- icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.Bug, { size: 16 })
11311
+ label: "Vulnerability Management",
11312
+ description: "Helps you solve vulnerabilities \u2014 demystifies CVEs in plain language, analyzes realistic exploitability (EPSS/KEV), and guides you through exact remediation steps and fixes.",
11313
+ defaultPrompt: "Help me review and solve this vulnerability: ",
11314
+ icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.Bug, { size: 16 }),
11315
+ enabled: true,
11316
+ defaultApps: [
11317
+ { name: "shuffle_vulnerabilities" },
11318
+ { name: "shuffle_assets" },
11319
+ { name: "shuffle_software" },
11320
+ { name: "shuffle_packages" }
11321
+ ]
10866
11322
  },
10867
11323
  {
10868
11324
  id: "detection",
10869
11325
  label: "Detection Agent",
10870
11326
  description: "Creates and tunes detection rules (Sigma, pipelines) \u2014 adjusts logic, filters false positives, and validates coverage.",
10871
11327
  defaultPrompt: "Modify my detections to ",
10872
- icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.Radar, { size: 16 })
11328
+ icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.Radar, { size: 16 }),
11329
+ enabled: true,
11330
+ defaultApps: [{ name: "shuffle_detection" }]
10873
11331
  },
10874
11332
  {
10875
11333
  id: "handle-notifications",
10876
11334
  label: "Handle Notifications",
10877
11335
  description: "Automatically handles incoming incidents for you \u2014 triages, enriches, and resolves or escalates based on your rules.",
10878
11336
  defaultPrompt: "Automatically handle incoming incidents by ",
10879
- icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.BellRing, { size: 16 })
11337
+ icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.BellRing, { size: 16 }),
11338
+ enabled: true,
11339
+ defaultApps: [{ name: "shuffle_incidents" }]
10880
11340
  }
10881
11341
  ];
10882
11342
  var isSupportUser = () => {
@@ -10899,7 +11359,7 @@ var filterAgentPresets = (list, support) => {
10899
11359
  if (support === true || support === void 0 && isSupportUser()) return list;
10900
11360
  return list.map((p) => SUPPORT_ONLY_PRESET_IDS.includes(p.id) ? { ...p, enabled: false } : p);
10901
11361
  };
10902
- var AgentPresets = ({ variant = "default", onSelectPreset, selectedPreset, onRemoveSelected, presets, chipRef, isSupport }) => {
11362
+ var AgentPresets = ({ variant = "default", onSelectPreset, selectedPreset, onRemoveSelected, presets, chipRef, isSupport, sx, placement }) => {
10903
11363
  const [anchorEl, setAnchorEl] = (0, import_react17.useState)(null);
10904
11364
  const open = Boolean(anchorEl);
10905
11365
  const [supportTick, setSupportTick] = (0, import_react17.useState)(0);
@@ -10914,67 +11374,129 @@ var AgentPresets = ({ variant = "default", onSelectPreset, selectedPreset, onRem
10914
11374
  [presets, supportTick, open, isSupport]
10915
11375
  );
10916
11376
  const MAX_LABEL_CHARS = 18;
10917
- const displayLabel = selectedPreset ? selectedPreset.label.length > MAX_LABEL_CHARS ? `${selectedPreset.label.slice(0, MAX_LABEL_CHARS - 1).trimEnd()}\u2026` : selectedPreset.label : "Skills";
10918
- const trigger = /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
10919
- import_material19.Button,
11377
+ const cleanLabel = selectedPreset?.label?.replace(/\s+Agent$/i, "") || "";
11378
+ const displayLabel = selectedPreset ? cleanLabel.length > MAX_LABEL_CHARS ? `${cleanLabel.slice(0, MAX_LABEL_CHARS - 1).trimEnd()}\u2026` : cleanLabel : "Skills";
11379
+ const trigger = /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
11380
+ import_material19.ButtonBase,
10920
11381
  {
10921
11382
  ref: chipRef,
10922
- size: "small",
10923
11383
  onClick: (e) => setAnchorEl(e.currentTarget),
10924
- startIcon: selectedPreset ? selectedPreset.icon ?? void 0 : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.Plus, { size: variant === "floating" ? 12 : 14 }),
10925
- endIcon: selectedPreset ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
10926
- import_material19.Box,
11384
+ "aria-label": selectedPreset ? `Skill: ${selectedPreset.label}` : "Select skill",
11385
+ sx: [
10927
11386
  {
10928
- component: "span",
10929
- onClick: (e) => {
10930
- e.stopPropagation();
10931
- onRemoveSelected?.();
10932
- },
10933
- sx: { display: "inline-flex", alignItems: "center", justifyContent: "center", ml: 0.25 },
10934
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.X, { size: variant === "floating" ? 12 : 14 })
10935
- }
10936
- ) : void 0,
10937
- sx: {
10938
- textTransform: "none",
10939
- fontSize: variant === "floating" ? "0.72rem" : "0.78rem",
10940
- fontWeight: 500,
10941
- height: variant === "floating" ? 30 : variant === "inline" ? 38 : 36,
10942
- px: variant === "floating" ? 1.25 : 1.625,
10943
- py: 0.375,
10944
- borderRadius: 999,
10945
- border: "1px solid hsl(var(--border))",
10946
- color: selectedPreset ? "hsl(var(--foreground))" : "hsl(var(--muted-foreground))",
10947
- bgcolor: selectedPreset ? "hsl(var(--muted) / 0.6)" : "transparent",
10948
- flexShrink: 0,
10949
- transition: "background-color 120ms ease, border-color 120ms ease, box-shadow 120ms ease",
10950
- "&:hover": {
10951
- bgcolor: selectedPreset ? "hsl(var(--muted) / 0.85)" : "hsl(var(--muted))",
10952
- color: "hsl(var(--foreground))",
10953
- borderColor: selectedPreset ? "hsl(var(--muted-foreground) / 0.35)" : "hsl(var(--border))",
10954
- boxShadow: selectedPreset ? "0 1px 4px hsl(var(--background) / 0.35)" : "none"
10955
- }
10956
- },
10957
- children: displayLabel
10958
- }
10959
- );
10960
- const [query, setQuery] = (0, import_react17.useState)("");
10961
- const filtered = (0, import_react17.useMemo)(() => {
10962
- const q = query.trim().toLowerCase();
10963
- if (!q) return list;
10964
- return list.filter(
10965
- (p) => p.label.toLowerCase().includes(q) || p.description.toLowerCase().includes(q)
10966
- );
10967
- }, [list, query]);
10968
- const menu = open ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
10969
- import_material19.Popper,
10970
- {
10971
- open,
10972
- anchorEl,
10973
- placement: "bottom-end",
10974
- style: { zIndex: 1400 },
10975
- modifiers: [{ name: "offset", options: { offset: [0, 6] } }],
10976
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_material19.ClickAwayListener, { onClickAway: () => {
10977
- setAnchorEl(null);
11387
+ display: "inline-flex",
11388
+ alignItems: "center",
11389
+ gap: "5px",
11390
+ height: variant === "floating" ? 30 : variant === "inline" ? 38 : 32,
11391
+ px: variant === "floating" ? "9px" : "12px",
11392
+ py: 0,
11393
+ borderRadius: 999,
11394
+ border: "1px solid hsl(var(--border))",
11395
+ color: selectedPreset ? "hsl(var(--foreground))" : "hsl(var(--muted-foreground))",
11396
+ bgcolor: selectedPreset ? "hsl(var(--muted) / 0.7)" : "transparent",
11397
+ fontSize: variant === "floating" ? "0.75rem" : "0.78rem",
11398
+ fontWeight: 500,
11399
+ flexShrink: 0,
11400
+ boxSizing: "border-box",
11401
+ cursor: "pointer",
11402
+ transition: "background-color 120ms ease, border-color 120ms ease, box-shadow 120ms ease",
11403
+ "&:hover": {
11404
+ bgcolor: selectedPreset ? "hsl(var(--muted) / 0.9)" : "hsl(var(--muted) / 0.5)",
11405
+ borderColor: selectedPreset ? "hsl(var(--muted-foreground) / 0.35)" : "hsl(var(--border))",
11406
+ boxShadow: selectedPreset ? "0 1px 4px hsl(var(--background) / 0.35)" : "none"
11407
+ }
11408
+ },
11409
+ ...Array.isArray(sx) ? sx : sx ? [sx] : []
11410
+ ],
11411
+ children: [
11412
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
11413
+ import_material19.Box,
11414
+ {
11415
+ component: "span",
11416
+ sx: {
11417
+ display: "inline-flex",
11418
+ alignItems: "center",
11419
+ justifyContent: "center",
11420
+ flexShrink: 0,
11421
+ color: selectedPreset ? "hsl(var(--primary))" : "inherit",
11422
+ "& svg": {
11423
+ width: variant === "floating" ? 14 : 16,
11424
+ height: variant === "floating" ? 14 : 16
11425
+ }
11426
+ },
11427
+ children: selectedPreset ? selectedPreset.icon ?? void 0 : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.Plus, { size: variant === "floating" ? 12 : 14 })
11428
+ }
11429
+ ),
11430
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
11431
+ import_material19.Typography,
11432
+ {
11433
+ component: "span",
11434
+ sx: {
11435
+ fontSize: "inherit",
11436
+ fontWeight: "inherit",
11437
+ color: "inherit",
11438
+ lineHeight: 1,
11439
+ whiteSpace: "nowrap",
11440
+ overflow: "hidden",
11441
+ textOverflow: "ellipsis",
11442
+ maxWidth: 150
11443
+ },
11444
+ children: displayLabel
11445
+ }
11446
+ ),
11447
+ selectedPreset && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
11448
+ import_material19.Box,
11449
+ {
11450
+ component: "span",
11451
+ role: "button",
11452
+ "aria-label": "Remove skill",
11453
+ onClick: (e) => {
11454
+ e.stopPropagation();
11455
+ onRemoveSelected?.();
11456
+ },
11457
+ sx: {
11458
+ display: "inline-flex",
11459
+ alignItems: "center",
11460
+ justifyContent: "center",
11461
+ ml: "1px",
11462
+ p: "2px",
11463
+ borderRadius: "50%",
11464
+ cursor: "pointer",
11465
+ flexShrink: 0,
11466
+ color: "hsl(var(--muted-foreground))",
11467
+ transition: "background-color 120ms ease, color 120ms ease",
11468
+ "&:hover": {
11469
+ bgcolor: "hsl(var(--foreground) / 0.12)",
11470
+ color: "hsl(var(--foreground))"
11471
+ }
11472
+ },
11473
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react10.X, { size: variant === "floating" ? 11 : 12 })
11474
+ }
11475
+ )
11476
+ ]
11477
+ }
11478
+ );
11479
+ const [query, setQuery] = (0, import_react17.useState)("");
11480
+ const filtered = (0, import_react17.useMemo)(() => {
11481
+ const q = query.trim().toLowerCase();
11482
+ if (!q) return list;
11483
+ return list.filter(
11484
+ (p) => p.label.toLowerCase().includes(q) || p.description.toLowerCase().includes(q)
11485
+ );
11486
+ }, [list, query]);
11487
+ const menu = open ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
11488
+ import_material19.Popper,
11489
+ {
11490
+ open,
11491
+ anchorEl,
11492
+ placement: placement || "bottom-start",
11493
+ style: { zIndex: 1400 },
11494
+ modifiers: [
11495
+ { name: "offset", options: { offset: [0, 6] } },
11496
+ { name: "preventOverflow", options: { padding: 8 } }
11497
+ ],
11498
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_material19.ClickAwayListener, { onClickAway: () => {
11499
+ setAnchorEl(null);
10978
11500
  setQuery("");
10979
11501
  }, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
10980
11502
  import_material19.Paper,
@@ -11210,13 +11732,7 @@ var AgentPresets = ({ variant = "default", onSelectPreset, selectedPreset, onRem
11210
11732
  ) })
11211
11733
  }
11212
11734
  ) : null;
11213
- if (variant === "inline") {
11214
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
11215
- trigger,
11216
- menu
11217
- ] });
11218
- }
11219
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_material19.Box, { sx: { width: "100%", display: "flex", justifyContent: "flex-end" }, children: [
11735
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
11220
11736
  trigger,
11221
11737
  menu
11222
11738
  ] });
@@ -11896,12 +12412,15 @@ var runAgent = async (request) => {
11896
12412
  params.tool_ids = request.toolIds;
11897
12413
  }
11898
12414
  const PRESET_PATHS = {
11899
- "build-workflows": "workflow-edit",
12415
+ "build-workflows": "edit-workflow",
12416
+ "edit-workflow": "edit-workflow",
11900
12417
  "handle-notifications": "handle-notifications",
11901
- "incident-response": "incident-response",
12418
+ "incident-response": "incident-handler",
12419
+ "incident-handler": "incident-handler",
11902
12420
  "host-monitor-control": "computer-use",
11903
12421
  support: "support",
11904
12422
  vulnerability: "vulnerability",
12423
+ "vulnerability-management": "vulnerability",
11905
12424
  detection: "detection"
11906
12425
  };
11907
12426
  const presetSlug = request.presetId ? PRESET_PATHS[request.presetId] : void 0;
@@ -11998,7 +12517,7 @@ var resolveAgentContinuationTargets = async (executionId, authorization) => {
11998
12517
  const resp = await fetch(getApiUrl("/api/v1/streams/results"), {
11999
12518
  method: "POST",
12000
12519
  credentials: "include",
12001
- headers: { "Content-Type": "application/json" },
12520
+ headers: { "Content-Type": "application/json", ...getAuthHeader() },
12002
12521
  body: JSON.stringify({ execution_id: executionId, authorization })
12003
12522
  });
12004
12523
  if (!resp.ok) return empty;
@@ -13315,6 +13834,716 @@ var collectLlmImageAttachments = (root, maxDepth = 10) => {
13315
13834
  return deduped;
13316
13835
  };
13317
13836
 
13837
+ // connectedSourcesService.ts
13838
+ var CACHE_PREFIX = "shuffle:connected_tools:";
13839
+ function getCachedConnectedTools(category) {
13840
+ if (!category) return [];
13841
+ try {
13842
+ if (typeof localStorage === "undefined") return [];
13843
+ const raw = localStorage.getItem(`${CACHE_PREFIX}${category}`);
13844
+ if (!raw) return [];
13845
+ const parsed = JSON.parse(raw);
13846
+ return Array.isArray(parsed) ? parsed : [];
13847
+ } catch {
13848
+ return [];
13849
+ }
13850
+ }
13851
+ function setCachedConnectedTools(category, tools) {
13852
+ if (!category) return;
13853
+ try {
13854
+ if (typeof localStorage === "undefined") return;
13855
+ localStorage.setItem(`${CACHE_PREFIX}${category}`, JSON.stringify(tools));
13856
+ } catch {
13857
+ }
13858
+ }
13859
+ function mergeConnectedTools(baseApps, connectedTools) {
13860
+ if (!connectedTools || connectedTools.length === 0) return baseApps;
13861
+ const seen = /* @__PURE__ */ new Set();
13862
+ const merged = [];
13863
+ for (const app2 of baseApps) {
13864
+ if (!app2?.name) continue;
13865
+ const norm4 = normalizeAppName(app2.name);
13866
+ if (!seen.has(norm4)) {
13867
+ seen.add(norm4);
13868
+ merged.push(app2);
13869
+ }
13870
+ }
13871
+ for (const app2 of connectedTools) {
13872
+ if (!app2?.name) continue;
13873
+ const norm4 = normalizeAppName(app2.name);
13874
+ if (!seen.has(norm4) && !isIgnoredWorkflowAppName(norm4)) {
13875
+ seen.add(norm4);
13876
+ merged.push(app2);
13877
+ }
13878
+ }
13879
+ return merged;
13880
+ }
13881
+ function resolveConnectedTools(category, authApiResponse = [], workflowsResponse = []) {
13882
+ const tools = [];
13883
+ const seen = /* @__PURE__ */ new Set();
13884
+ const authList = Array.isArray(authApiResponse) ? authApiResponse : authApiResponse?.data || [];
13885
+ const workflows = Array.isArray(workflowsResponse) ? workflowsResponse : workflowsResponse?.workflows || [];
13886
+ const addTool = (name, id, icon) => {
13887
+ if (!name || typeof name !== "string") return;
13888
+ const trimmed = name.trim();
13889
+ if (!trimmed) return;
13890
+ const norm4 = normalizeAppName(trimmed);
13891
+ if (!norm4 || seen.has(norm4) || isIgnoredWorkflowAppName(norm4)) return;
13892
+ seen.add(norm4);
13893
+ tools.push({ name: trimmed, id: id || void 0, icon: icon || void 0 });
13894
+ };
13895
+ const authByName = /* @__PURE__ */ new Map();
13896
+ for (const entry of authList) {
13897
+ const app2 = entry?.app || entry;
13898
+ const rawName = app2?.name;
13899
+ if (!rawName) continue;
13900
+ const norm4 = normalizeAppName(rawName);
13901
+ const isValid = entry?.active === true || entry?.validation?.valid === true || entry?.hasValidAuth === true || app2?.is_valid === true || app2?.tested === true;
13902
+ const img = app2?.large_image || app2?.image_url || app2?.image || entry?.bestImage || "";
13903
+ if (!authByName.has(norm4) || isValid) {
13904
+ authByName.set(norm4, {
13905
+ name: rawName,
13906
+ id: app2?.id || entry?.id,
13907
+ icon: img,
13908
+ active: Boolean(entry?.active),
13909
+ valid: isValid
13910
+ });
13911
+ }
13912
+ }
13913
+ if (category === "incidents") {
13914
+ const ingestWf = findIngestTicketsWorkflow(workflows);
13915
+ const ingestAppNames = ingestWf ? extractWorkflowAppNames(ingestWf) : /* @__PURE__ */ new Set();
13916
+ const forwardWf = findForwardTicketsWorkflow(workflows);
13917
+ const forwardAppNames = forwardWf ? extractWorkflowAppNames(forwardWf) : /* @__PURE__ */ new Set();
13918
+ ingestAppNames.forEach((norm4) => {
13919
+ const auth = authByName.get(norm4);
13920
+ if (auth) {
13921
+ addTool(auth.name, auth.id, auth.icon);
13922
+ } else {
13923
+ const formatted = norm4.split("_").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
13924
+ addTool(formatted);
13925
+ }
13926
+ });
13927
+ forwardAppNames.forEach((norm4) => {
13928
+ const auth = authByName.get(norm4);
13929
+ if (auth) {
13930
+ addTool(auth.name, auth.id, auth.icon);
13931
+ } else {
13932
+ const formatted = norm4.split("_").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
13933
+ addTool(formatted);
13934
+ }
13935
+ });
13936
+ for (const [norm4, auth] of authByName.entries()) {
13937
+ if (!auth.valid && !auth.active) continue;
13938
+ const isSiem = SIEM_PATTERNS.some((p) => norm4.includes(p));
13939
+ const isEdr = EDR_PATTERNS.some((p) => norm4.includes(p));
13940
+ const isEmail = EMAIL_APP_PATTERNS.some((p) => norm4.includes(p));
13941
+ const isCases = CASES_PATTERNS.some((p) => norm4.includes(p));
13942
+ const isComms = COMMUNICATION_PATTERNS_NAMES.some((p) => norm4.includes(p));
13943
+ if (isSiem || isEdr || isEmail || isCases || isComms) {
13944
+ addTool(auth.name, auth.id, auth.icon);
13945
+ }
13946
+ }
13947
+ } else if (category === "vulnerabilities") {
13948
+ const ingestVulnWf = workflows.find(
13949
+ (w) => typeof w?.name === "string" && (w.name === "Ingest Vulnerabilities" || w.name.toLowerCase().includes("ingest vulnerabilit"))
13950
+ );
13951
+ const vulnAppNames = ingestVulnWf ? extractWorkflowAppNames(ingestVulnWf) : /* @__PURE__ */ new Set();
13952
+ vulnAppNames.forEach((norm4) => {
13953
+ const auth = authByName.get(norm4);
13954
+ if (auth) {
13955
+ addTool(auth.name, auth.id, auth.icon);
13956
+ } else {
13957
+ const formatted = norm4.split("_").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
13958
+ addTool(formatted);
13959
+ }
13960
+ });
13961
+ for (const [norm4, auth] of authByName.entries()) {
13962
+ if (!auth.valid && !auth.active) continue;
13963
+ const isScanner = isVulnScannerApp(auth.name) || VULN_SCANNER_PATTERNS.some((p) => norm4.includes(p));
13964
+ if (isScanner) {
13965
+ addTool(auth.name, auth.id, auth.icon);
13966
+ }
13967
+ }
13968
+ }
13969
+ return tools;
13970
+ }
13971
+
13972
+ // agentContextRegistry.ts
13973
+ var STORAGE_PREFIX = "shuffle_agent_context_choice_";
13974
+ var matchRoutePattern = (pattern, pathname) => {
13975
+ const normPattern = pattern.replace(/\/+$/, "") || "/";
13976
+ const normPath = pathname.replace(/\/+$/, "") || "/";
13977
+ if (normPattern === normPath) return {};
13978
+ const patternSegments = normPattern.split("/");
13979
+ const pathSegments = normPath.split("/");
13980
+ if (patternSegments.length !== pathSegments.length) {
13981
+ if (patternSegments[patternSegments.length - 1] === "*" && pathSegments.length >= patternSegments.length - 1) {
13982
+ const params2 = {};
13983
+ for (let i = 0; i < patternSegments.length - 1; i++) {
13984
+ if (patternSegments[i].startsWith(":")) {
13985
+ params2[patternSegments[i].slice(1)] = decodeURIComponent(pathSegments[i]);
13986
+ } else if (patternSegments[i] !== pathSegments[i]) {
13987
+ return null;
13988
+ }
13989
+ }
13990
+ params2["*"] = pathSegments.slice(patternSegments.length - 1).join("/");
13991
+ return params2;
13992
+ }
13993
+ return null;
13994
+ }
13995
+ const params = {};
13996
+ for (let i = 0; i < patternSegments.length; i++) {
13997
+ const pSeg = patternSegments[i];
13998
+ const uSeg = pathSegments[i];
13999
+ if (pSeg.startsWith(":")) {
14000
+ params[pSeg.slice(1)] = decodeURIComponent(uSeg);
14001
+ } else if (pSeg !== uSeg) {
14002
+ return null;
14003
+ }
14004
+ }
14005
+ return params;
14006
+ };
14007
+ var isAgentRoute = (pathname) => {
14008
+ const norm4 = pathname.toLowerCase().replace(/\/+$/, "") || "/";
14009
+ return norm4 === "/agents" || norm4 === "/agent" || norm4.startsWith("/agents/") || norm4.startsWith("/agent/");
14010
+ };
14011
+ var getActivePageEntityName = () => {
14012
+ if (typeof document === "undefined") return void 0;
14013
+ try {
14014
+ const titleInput = document.querySelector(
14015
+ '[data-incident-field="title"] input, [data-case-field="title"] input, [data-ticket-field="title"] input, [data-alert-field="title"] input, [data-host-field="name"] input, [data-monitor-field="name"] input, [data-asset-field="name"] input, [data-software-field="name"] input, [data-package-field="name"] input'
14016
+ );
14017
+ if (titleInput?.value && titleInput.value.trim().length > 0) {
14018
+ return titleInput.value.trim();
14019
+ }
14020
+ const heading = document.querySelector("h1, h2, [data-entity-title]");
14021
+ if (heading?.textContent && heading.textContent.trim().length > 0 && heading.textContent.trim().length < 90) {
14022
+ return heading.textContent.trim();
14023
+ }
14024
+ } catch {
14025
+ }
14026
+ return void 0;
14027
+ };
14028
+ var DEFAULT_AGENT_CONTEXT_RULES = [
14029
+ // ==========================================
14030
+ // 1. Incidents, Cases, Tickets, Alerts (Grouped)
14031
+ // ==========================================
14032
+ // Specific Incident Detail
14033
+ {
14034
+ id: "incident-detail",
14035
+ match: "/incidents/:id",
14036
+ defaultApps: [{ name: "shuffle_incidents" }],
14037
+ defaultPresetId: "incident-response",
14038
+ sourceCategory: "incidents",
14039
+ title: (params) => {
14040
+ const entity = getActivePageEntityName();
14041
+ return entity ? `How can we help handle incident "${entity}"?` : `How can we help handle incident #${params.id}?`;
14042
+ },
14043
+ subtitle: () => "Shuffle Incidents MCP",
14044
+ defaultPrompt: (params) => `Investigate incident ${params.id} and recommend next steps: `,
14045
+ placeholder: "Ask about this incident, triage observables, or correlate...",
14046
+ getStorageKey: (params) => `incident_${params.id}`,
14047
+ description: "Focused on the currently viewed incident with Shuffle Incidents MCP"
14048
+ },
14049
+ // Simplified Incident Detail
14050
+ {
14051
+ id: "incident-simple-detail",
14052
+ match: "/incidents-simple/:id",
14053
+ defaultApps: [{ name: "shuffle_incidents" }],
14054
+ defaultPresetId: "incident-response",
14055
+ sourceCategory: "incidents",
14056
+ title: (params) => {
14057
+ const entity = getActivePageEntityName();
14058
+ return entity ? `How can we help handle incident "${entity}"?` : `How can we help handle incident #${params.id}?`;
14059
+ },
14060
+ subtitle: () => "Shuffle Incidents MCP",
14061
+ defaultPrompt: (params) => `Investigate incident ${params.id} and recommend next steps: `,
14062
+ placeholder: "Ask about this incident, triage observables, or correlate...",
14063
+ getStorageKey: (params) => `incident_${params.id}`,
14064
+ description: "Focused on the currently viewed incident with Shuffle Incidents MCP"
14065
+ },
14066
+ // Specific Case Detail
14067
+ {
14068
+ id: "case-detail",
14069
+ match: "/cases/:id",
14070
+ defaultApps: [{ name: "shuffle_incidents" }],
14071
+ defaultPresetId: "incident-response",
14072
+ sourceCategory: "incidents",
14073
+ title: (params) => {
14074
+ const entity = getActivePageEntityName();
14075
+ return entity ? `How can we help handle case "${entity}"?` : `How can we help handle case #${params.id}?`;
14076
+ },
14077
+ subtitle: () => "Shuffle Incidents MCP",
14078
+ defaultPrompt: (params) => `Investigate case ${params.id} and recommend next steps: `,
14079
+ placeholder: "Review case evidence, correlate events, or recommend response actions...",
14080
+ getStorageKey: (params) => `case_${params.id}`,
14081
+ description: "Focused on the currently viewed case with Shuffle Incidents MCP"
14082
+ },
14083
+ // Specific Ticket Detail
14084
+ {
14085
+ id: "ticket-detail",
14086
+ match: "/tickets/:id",
14087
+ defaultApps: [{ name: "shuffle_incidents" }],
14088
+ defaultPresetId: "incident-response",
14089
+ sourceCategory: "incidents",
14090
+ title: (params) => {
14091
+ const entity = getActivePageEntityName();
14092
+ return entity ? `How can we help handle ticket "${entity}"?` : `How can we help handle ticket #${params.id}?`;
14093
+ },
14094
+ subtitle: () => "Shuffle Incidents MCP",
14095
+ defaultPrompt: (params) => `Investigate ticket ${params.id} and recommend next steps: `,
14096
+ placeholder: "Investigate ticket, draft reply, or correlate related incidents...",
14097
+ getStorageKey: (params) => `ticket_${params.id}`,
14098
+ description: "Focused on the currently viewed ticket with Shuffle Incidents MCP"
14099
+ },
14100
+ // Specific Alert Detail
14101
+ {
14102
+ id: "alert-detail",
14103
+ match: "/alerts/:id",
14104
+ defaultApps: [{ name: "shuffle_incidents" }],
14105
+ defaultPresetId: "incident-response",
14106
+ sourceCategory: "incidents",
14107
+ title: (params) => {
14108
+ const entity = getActivePageEntityName();
14109
+ return entity ? `How can we help handle alert "${entity}"?` : `How can we help handle alert #${params.id}?`;
14110
+ },
14111
+ subtitle: () => "Shuffle Incidents MCP",
14112
+ defaultPrompt: (params) => `Investigate alert ${params.id} and recommend next steps: `,
14113
+ placeholder: "Analyze alert telemetry, assess false positive probability, or escalate...",
14114
+ getStorageKey: (params) => `alert_${params.id}`,
14115
+ description: "Focused on the currently viewed alert with Shuffle Incidents MCP"
14116
+ },
14117
+ // Incidents Overview & Subpages (/incidents/*)
14118
+ {
14119
+ id: "incidents-group",
14120
+ match: (pathname) => pathname.startsWith("/incidents") || pathname.startsWith("/incidents-simple"),
14121
+ defaultApps: [{ name: "shuffle_incidents" }],
14122
+ defaultPresetId: "incident-response",
14123
+ sourceCategory: "incidents",
14124
+ title: "How can we help handle incidents?",
14125
+ subtitle: "Shuffle Incidents MCP",
14126
+ defaultPrompt: "Investigate this incident and recommend next steps: ",
14127
+ placeholder: "Triage incidents, correlate alerts, or search threat intelligence...",
14128
+ getStorageKey: () => "incidents_list",
14129
+ description: "Incidents overview with Shuffle Incidents MCP"
14130
+ },
14131
+ // Cases Overview & Subpages (/cases/*)
14132
+ {
14133
+ id: "cases-group",
14134
+ match: (pathname) => pathname.startsWith("/cases"),
14135
+ defaultApps: [{ name: "shuffle_incidents" }],
14136
+ defaultPresetId: "incident-response",
14137
+ sourceCategory: "incidents",
14138
+ title: "How can we help handle cases?",
14139
+ subtitle: "Shuffle Incidents MCP",
14140
+ defaultPrompt: "Investigate this case and recommend next steps: ",
14141
+ placeholder: "Review active cases, correlate investigations, or log evidence...",
14142
+ getStorageKey: () => "cases_list",
14143
+ description: "Cases overview with Shuffle Incidents MCP"
14144
+ },
14145
+ // Tickets Overview & Subpages (/tickets/*)
14146
+ {
14147
+ id: "tickets-group",
14148
+ match: (pathname) => pathname.startsWith("/tickets"),
14149
+ defaultApps: [{ name: "shuffle_incidents" }],
14150
+ defaultPresetId: "incident-response",
14151
+ sourceCategory: "incidents",
14152
+ title: "How can we help handle tickets?",
14153
+ subtitle: "Shuffle Incidents MCP",
14154
+ defaultPrompt: "Investigate this ticket and recommend next steps: ",
14155
+ placeholder: "Triage incoming tickets, automate assignments, or resolve issues...",
14156
+ getStorageKey: () => "tickets_list",
14157
+ description: "Tickets overview with Shuffle Incidents MCP"
14158
+ },
14159
+ // Alerts Overview & Subpages (/alerts/*, /notifications/*)
14160
+ {
14161
+ id: "alerts-group",
14162
+ match: (pathname) => pathname.startsWith("/alerts") || pathname.startsWith("/notifications"),
14163
+ defaultApps: [{ name: "shuffle_incidents" }],
14164
+ defaultPresetId: "incident-response",
14165
+ sourceCategory: "incidents",
14166
+ title: "How can we help handle alerts?",
14167
+ subtitle: "Shuffle Incidents MCP",
14168
+ defaultPrompt: "Investigate this alert and recommend next steps: ",
14169
+ placeholder: "Triage incoming security alerts, filter noise, or escalate to incident...",
14170
+ getStorageKey: () => "alerts_list",
14171
+ description: "Alert and notification triage with Shuffle Incidents MCP"
14172
+ },
14173
+ // ==========================================
14174
+ // 2. Vulnerabilities (shuffle_vulnerabilities, shuffle_assets, shuffle_software, shuffle_packages)
14175
+ // ==========================================
14176
+ // Specific Vulnerability Detail
14177
+ {
14178
+ id: "vulnerability-detail",
14179
+ match: "/vulnerabilities/:id",
14180
+ defaultApps: [
14181
+ { name: "shuffle_vulnerabilities" },
14182
+ { name: "shuffle_assets" },
14183
+ { name: "shuffle_software" },
14184
+ { name: "shuffle_packages" }
14185
+ ],
14186
+ defaultPresetId: "vulnerability",
14187
+ sourceCategory: "vulnerabilities",
14188
+ title: (params) => {
14189
+ const entity = getActivePageEntityName();
14190
+ return entity ? `How can we help with ${entity}?` : `How can we help with vulnerability ${params.id}?`;
14191
+ },
14192
+ subtitle: () => "Shuffle Vulnerabilities, Assets, Software & Packages",
14193
+ defaultPrompt: (params) => `Review vulnerability ${params.id} and draft remediation plan: `,
14194
+ placeholder: "Analyze this CVE, check affected hosts, and draft remediation...",
14195
+ getStorageKey: (params) => `vulnerability_${params.id}`,
14196
+ description: "Focused on the selected vulnerability with Shuffle Vulnerabilities, Assets, Software & Packages"
14197
+ },
14198
+ // Vulnerabilities List & Subpages
14199
+ {
14200
+ id: "vulnerabilities-list",
14201
+ match: (pathname) => pathname.startsWith("/vulnerabilities"),
14202
+ defaultApps: [
14203
+ { name: "shuffle_vulnerabilities" },
14204
+ { name: "shuffle_assets" },
14205
+ { name: "shuffle_software" },
14206
+ { name: "shuffle_packages" }
14207
+ ],
14208
+ defaultPresetId: "vulnerability",
14209
+ sourceCategory: "vulnerabilities",
14210
+ title: "How can we help review vulnerabilities?",
14211
+ subtitle: () => "Shuffle Vulnerabilities, Assets, Software & Packages",
14212
+ defaultPrompt: "Review my current vulnerabilities and prioritize them by ",
14213
+ placeholder: "Review CVEs, prioritize by exploitability, or draft patch workflows...",
14214
+ getStorageKey: () => "vulnerabilities_list",
14215
+ description: "Vulnerabilities overview with Shuffle Vulnerabilities, Assets, Software & Packages"
14216
+ },
14217
+ // ==========================================
14218
+ // 3. Monitors & Computer Use (shuffle_host_monitors for all)
14219
+ // ==========================================
14220
+ // Specific Host Terminal (/monitors/:id/terminal)
14221
+ {
14222
+ id: "monitor-terminal-detail",
14223
+ match: "/monitors/:id/terminal",
14224
+ defaultApps: [{ name: "shuffle_host_monitors" }],
14225
+ defaultPresetId: "host-monitor-control",
14226
+ title: (params) => {
14227
+ const entity = getActivePageEntityName();
14228
+ return entity ? `How can we help in terminal for "${entity}"?` : `How can we help in terminal on host #${params.id}?`;
14229
+ },
14230
+ subtitle: () => "Shuffle Host Monitors MCP",
14231
+ defaultPrompt: (params) => `Run commands on host ${params.id} to `,
14232
+ placeholder: "Ask the agent to execute shell commands, inspect logs, or debug...",
14233
+ getStorageKey: (params) => `monitor_terminal_${params.id}`,
14234
+ description: "Interactive terminal control with Shuffle Host Monitors MCP"
14235
+ },
14236
+ // General Host Terminal (/monitors/terminal)
14237
+ {
14238
+ id: "monitor-terminal",
14239
+ match: "/monitors/terminal",
14240
+ defaultApps: [{ name: "shuffle_host_monitors" }],
14241
+ defaultPresetId: "host-monitor-control",
14242
+ title: "How can we help in the host terminal?",
14243
+ subtitle: "Shuffle Host Monitors MCP",
14244
+ defaultPrompt: "Run terminal commands on this host to ",
14245
+ placeholder: "Ask the agent to execute shell commands, inspect files, or debug...",
14246
+ getStorageKey: () => "monitors_terminal",
14247
+ description: "Interactive terminal control with Shuffle Host Monitors MCP"
14248
+ },
14249
+ // Host Response Actions (/monitors/response)
14250
+ {
14251
+ id: "monitor-response",
14252
+ match: "/monitors/response",
14253
+ defaultApps: [{ name: "shuffle_host_monitors" }],
14254
+ defaultPresetId: "host-monitor-control",
14255
+ title: "How can we help with host response?",
14256
+ subtitle: "Shuffle Host Monitors MCP",
14257
+ defaultPrompt: "Take response actions on this host to ",
14258
+ placeholder: "Isolate host, terminate suspicious processes, or run remediation...",
14259
+ getStorageKey: () => "monitors_response",
14260
+ description: "Host response actions with Shuffle Host Monitors MCP"
14261
+ },
14262
+ // Specific Host Overview (/monitors/:id)
14263
+ {
14264
+ id: "monitor-detail",
14265
+ match: "/monitors/:id",
14266
+ defaultApps: [{ name: "shuffle_host_monitors" }],
14267
+ defaultPresetId: "host-monitor-control",
14268
+ title: (params) => {
14269
+ const entity = getActivePageEntityName();
14270
+ return entity ? `How can we help with host "${entity}"?` : `How can we help with host #${params.id}?`;
14271
+ },
14272
+ subtitle: () => "Shuffle Host Monitors MCP",
14273
+ defaultPrompt: (params) => `Take control of host ${params.id} and help me with: `,
14274
+ placeholder: "Inspect host telemetry, running processes, network connections, or remediate...",
14275
+ getStorageKey: (params) => `monitor_${params.id}`,
14276
+ description: "Host detail view with Shuffle Host Monitors MCP"
14277
+ },
14278
+ // Monitors Fleet Overview & Subpages (/monitors, /monitors/*)
14279
+ {
14280
+ id: "host-monitors-group",
14281
+ match: (pathname) => pathname.startsWith("/monitors"),
14282
+ defaultApps: [{ name: "shuffle_host_monitors" }],
14283
+ defaultPresetId: "host-monitor-control",
14284
+ title: "How can we help with host monitors?",
14285
+ subtitle: "Shuffle Host Monitors MCP",
14286
+ defaultPrompt: "Take control of this host and help me with: ",
14287
+ placeholder: "Ask the agent to run terminal commands, inspect files, or remediate...",
14288
+ getStorageKey: () => "host_monitors",
14289
+ description: "Computer use and host monitors control with Shuffle Host Monitors MCP"
14290
+ },
14291
+ // ==========================================
14292
+ // 4. Workflows & Automations
14293
+ // ==========================================
14294
+ // Specific Workflow Detail (/workflows/:id)
14295
+ {
14296
+ id: "workflow-detail",
14297
+ match: "/workflows/:id",
14298
+ defaultApps: [{ name: "shuffle_workflows_builder" }, { name: "shuffle_apps" }],
14299
+ defaultPresetId: "build-workflows",
14300
+ title: (params) => {
14301
+ const entity = getActivePageEntityName();
14302
+ return entity ? `How can we help edit "${entity}"?` : `How can we help edit workflow #${params.id}?`;
14303
+ },
14304
+ subtitle: () => "Shuffle Workflows Builder & Shuffle Apps",
14305
+ defaultPrompt: (params) => `Edit workflow ${params.id} to `,
14306
+ placeholder: "Describe the changes, new actions, or logic to add...",
14307
+ getStorageKey: (params) => `workflow_${params.id}`,
14308
+ description: "Focused on editing the selected workflow with Shuffle Workflows Builder and Shuffle Apps"
14309
+ },
14310
+ // Workflows Overview & Subpages (/workflows, /workflows/*)
14311
+ {
14312
+ id: "workflows-group",
14313
+ match: (pathname) => pathname === "/workflows" || pathname.startsWith("/workflows") || pathname.startsWith("/infrastructure/flows"),
14314
+ defaultApps: [{ name: "shuffle_workflows_builder" }, { name: "shuffle_apps" }],
14315
+ defaultPresetId: "build-workflows",
14316
+ title: "How can we help edit workflows?",
14317
+ subtitle: "Shuffle Workflows Builder & Shuffle Apps",
14318
+ defaultPrompt: "Edit this Shuffle workflow to ",
14319
+ placeholder: "Describe the workflow you want to build or edit...",
14320
+ getStorageKey: () => "workflows_builder",
14321
+ description: "Workflow builder with Shuffle Workflows Builder and Shuffle Apps"
14322
+ },
14323
+ // ==========================================
14324
+ // 5. Assets, Software & Packages
14325
+ // ==========================================
14326
+ // Specific Asset Tab (/assets/:tab)
14327
+ {
14328
+ id: "assets-tab",
14329
+ match: "/assets/:tab",
14330
+ defaultApps: [{ name: "shuffle_assets" }, { name: "shuffle_vulnerabilities" }],
14331
+ defaultPresetId: "vulnerability",
14332
+ sourceCategory: "vulnerabilities",
14333
+ title: (params) => {
14334
+ const entity = getActivePageEntityName();
14335
+ if (entity) return `How can we help with "${entity}"?`;
14336
+ const tabName = (params.tab || "assets").replace(/[-_]+/g, " ");
14337
+ return `How can we help with ${tabName}?`;
14338
+ },
14339
+ subtitle: () => "Shuffle Assets & Shuffle Vulnerabilities",
14340
+ defaultPrompt: (params) => `Inspect ${params.tab || "assets"} and review vulnerabilities: `,
14341
+ placeholder: "Search assets, inspect hosts, or check security posture...",
14342
+ getStorageKey: (params) => `assets_tab_${params.tab}`,
14343
+ description: "Assets tab view with Shuffle Assets and Shuffle Vulnerabilities MCPs"
14344
+ },
14345
+ // Assets Overview (/assets, /assets/*)
14346
+ {
14347
+ id: "assets-overview",
14348
+ match: (pathname) => pathname === "/assets" || pathname.startsWith("/assets"),
14349
+ defaultApps: [{ name: "shuffle_assets" }, { name: "shuffle_vulnerabilities" }],
14350
+ defaultPresetId: "vulnerability",
14351
+ sourceCategory: "vulnerabilities",
14352
+ title: "How can we help with assets?",
14353
+ subtitle: "Shuffle Assets & Shuffle Vulnerabilities",
14354
+ defaultPrompt: "Audit assets and review vulnerabilities for ",
14355
+ placeholder: "Search assets, inspect hosts, or check security posture...",
14356
+ getStorageKey: () => "assets_overview",
14357
+ description: "Assets overview with Shuffle Assets and Shuffle Vulnerabilities MCPs"
14358
+ },
14359
+ // Software Inventory (/software, /software/*)
14360
+ {
14361
+ id: "software-inventory",
14362
+ match: (pathname) => pathname === "/software" || pathname.startsWith("/software"),
14363
+ defaultApps: [{ name: "shuffle_software" }, { name: "shuffle_vulnerabilities" }],
14364
+ defaultPresetId: "vulnerability",
14365
+ sourceCategory: "vulnerabilities",
14366
+ title: (params) => {
14367
+ const entity = getActivePageEntityName();
14368
+ return entity ? `How can we help with "${entity}"?` : "How can we help with software inventory?";
14369
+ },
14370
+ subtitle: () => "Shuffle Software & Shuffle Vulnerabilities",
14371
+ defaultPrompt: "Inspect software inventory and check for vulnerabilities in ",
14372
+ placeholder: "Search installed software, verify versions, or scan for CVEs...",
14373
+ getStorageKey: () => "software_inventory",
14374
+ description: "Software inventory with Shuffle Software and Shuffle Vulnerabilities MCPs"
14375
+ },
14376
+ // Packages Inventory (/packages, /packages/*)
14377
+ {
14378
+ id: "packages-inventory",
14379
+ match: (pathname) => pathname === "/packages" || pathname.startsWith("/packages"),
14380
+ defaultApps: [{ name: "shuffle_packages" }, { name: "shuffle_vulnerabilities" }],
14381
+ defaultPresetId: "vulnerability",
14382
+ sourceCategory: "vulnerabilities",
14383
+ title: (params) => {
14384
+ const entity = getActivePageEntityName();
14385
+ return entity ? `How can we help with "${entity}"?` : "How can we help with packages?";
14386
+ },
14387
+ subtitle: () => "Shuffle Packages & Shuffle Vulnerabilities",
14388
+ defaultPrompt: "Analyze package dependencies and known vulnerabilities for ",
14389
+ placeholder: "Search package dependencies, verify licenses, or check CVEs...",
14390
+ getStorageKey: () => "packages_inventory",
14391
+ description: "Package dependencies with Shuffle Packages and Shuffle Vulnerabilities MCPs"
14392
+ },
14393
+ // ==========================================
14394
+ // 6. Detection & Sigma
14395
+ // ==========================================
14396
+ {
14397
+ id: "detection",
14398
+ match: (pathname) => pathname.startsWith("/detection"),
14399
+ defaultApps: [{ name: "shuffle_detection" }],
14400
+ defaultPresetId: "detection",
14401
+ title: "How can we help tune detections?",
14402
+ subtitle: "Shuffle Detection MCP",
14403
+ defaultPrompt: "Modify my detections to ",
14404
+ placeholder: "Create Sigma rules, tune detection pipelines, or filter false positives...",
14405
+ getStorageKey: () => "detection",
14406
+ description: "Detection engineering with Shuffle Detection MCP"
14407
+ },
14408
+ // ==========================================
14409
+ // 7. Documentation (/docs, /docs/*)
14410
+ // ==========================================
14411
+ {
14412
+ id: "docs",
14413
+ match: (pathname) => pathname === "/docs" || pathname.startsWith("/docs/"),
14414
+ defaultApps: [{ name: "shuffle_tools" }],
14415
+ defaultPresetId: "support",
14416
+ title: "How can we help with documentation?",
14417
+ subtitle: "Documentation & Knowledge Assistant",
14418
+ defaultPrompt: "Help me understand or find information about ",
14419
+ placeholder: "Ask questions about Shuffle features, guides, or API...",
14420
+ getStorageKey: (params, pathname) => `docs_${pathname.replace(/[^a-zA-Z0-9_-]/g, "_")}`,
14421
+ description: "Documentation assistant for Shuffle guides, features, and API references",
14422
+ sideshift: true
14423
+ },
14424
+ // ==========================================
14425
+ // 8. Default Fallback (Support)
14426
+ // ==========================================
14427
+ {
14428
+ id: "default",
14429
+ match: () => true,
14430
+ defaultApps: [{ name: "shuffle_tools" }],
14431
+ defaultPresetId: "support",
14432
+ title: "How can we help on this page?",
14433
+ subtitle: "General Platform Assistant",
14434
+ defaultPrompt: "Help me with the following on this page: ",
14435
+ placeholder: "Ask anything about Shuffle, integrations, or workflows...",
14436
+ getStorageKey: (params, pathname) => `page_${pathname.replace(/[^a-zA-Z0-9_-]/g, "_")}`,
14437
+ description: "General support agent for the platform",
14438
+ missingConfig: true
14439
+ }
14440
+ ];
14441
+ var customRuleRegistry = [];
14442
+ var registerAgentContextRule = (rule, prepend = true) => {
14443
+ if (prepend) {
14444
+ customRuleRegistry.unshift(rule);
14445
+ } else {
14446
+ customRuleRegistry.push(rule);
14447
+ }
14448
+ return () => {
14449
+ const idx = customRuleRegistry.indexOf(rule);
14450
+ if (idx !== -1) customRuleRegistry.splice(idx, 1);
14451
+ };
14452
+ };
14453
+ var getAgentContextRules = (customRules) => {
14454
+ return [...customRules ?? [], ...customRuleRegistry, ...DEFAULT_AGENT_CONTEXT_RULES];
14455
+ };
14456
+ var getPageContextChoice = (storageKey) => {
14457
+ try {
14458
+ const raw = localStorage.getItem(`${STORAGE_PREFIX}${storageKey}`);
14459
+ if (!raw) return null;
14460
+ return JSON.parse(raw);
14461
+ } catch {
14462
+ return null;
14463
+ }
14464
+ };
14465
+ var setPageContextChoice = (storageKey, choice) => {
14466
+ try {
14467
+ const existing = getPageContextChoice(storageKey) || { updatedAt: Date.now() };
14468
+ const payload = {
14469
+ ...existing,
14470
+ ...choice,
14471
+ updatedAt: Date.now()
14472
+ };
14473
+ localStorage.setItem(`${STORAGE_PREFIX}${storageKey}`, JSON.stringify(payload));
14474
+ } catch {
14475
+ }
14476
+ };
14477
+ var clearPageContextChoice = (storageKey) => {
14478
+ try {
14479
+ localStorage.removeItem(`${STORAGE_PREFIX}${storageKey}`);
14480
+ } catch {
14481
+ }
14482
+ };
14483
+ var resolveAgentContext = (pathname, search = "", customRules) => {
14484
+ const normPath = pathname.replace(/\/+$/, "") || "/";
14485
+ const searchParams = new URLSearchParams(search.startsWith("?") ? search.slice(1) : search);
14486
+ const rules = getAgentContextRules(customRules);
14487
+ let matchedRule = null;
14488
+ let matchedParams = {};
14489
+ for (const rule of rules) {
14490
+ if (typeof rule.match === "string") {
14491
+ const params = matchRoutePattern(rule.match, normPath);
14492
+ if (params !== null) {
14493
+ matchedRule = rule;
14494
+ matchedParams = params;
14495
+ break;
14496
+ }
14497
+ } else if (rule.match instanceof RegExp) {
14498
+ if (rule.match.test(normPath)) {
14499
+ matchedRule = rule;
14500
+ break;
14501
+ }
14502
+ } else if (typeof rule.match === "function") {
14503
+ const res = rule.match(normPath, searchParams);
14504
+ if (res) {
14505
+ matchedRule = rule;
14506
+ if (typeof res === "object") {
14507
+ matchedParams = res;
14508
+ }
14509
+ break;
14510
+ }
14511
+ }
14512
+ }
14513
+ if (!matchedRule) {
14514
+ matchedRule = DEFAULT_AGENT_CONTEXT_RULES[DEFAULT_AGENT_CONTEXT_RULES.length - 1];
14515
+ }
14516
+ const storageKey = matchedRule.getStorageKey ? matchedRule.getStorageKey(matchedParams, normPath) : matchedRule.id;
14517
+ const savedChoice = getPageContextChoice(storageKey);
14518
+ const isOverridden = !!savedChoice && (savedChoice.apps !== void 0 || savedChoice.presetId !== void 0);
14519
+ const cachedConnected = matchedRule.sourceCategory ? getCachedConnectedTools(matchedRule.sourceCategory) : [];
14520
+ const baseDefaultApps = mergeConnectedTools(matchedRule.defaultApps, cachedConnected);
14521
+ const effectiveApps = savedChoice?.apps ?? baseDefaultApps;
14522
+ const effectivePresetId = savedChoice?.presetId !== void 0 ? savedChoice.presetId ?? void 0 : matchedRule.defaultPresetId;
14523
+ const title = typeof matchedRule.title === "function" ? matchedRule.title(matchedParams, normPath) : matchedRule.title ?? "Agent";
14524
+ const subtitle = typeof matchedRule.subtitle === "function" ? matchedRule.subtitle(matchedParams, normPath) : matchedRule.subtitle ?? "Context aware assistant";
14525
+ const defaultPrompt = typeof matchedRule.defaultPrompt === "function" ? matchedRule.defaultPrompt(matchedParams, normPath) : matchedRule.defaultPrompt ?? "";
14526
+ const missingConfig = Boolean(matchedRule.missingConfig ?? matchedRule.id === "default");
14527
+ return {
14528
+ ruleId: matchedRule.id,
14529
+ apps: effectiveApps,
14530
+ presetId: effectivePresetId,
14531
+ title,
14532
+ subtitle,
14533
+ defaultPrompt,
14534
+ placeholder: matchedRule.placeholder,
14535
+ storageKey,
14536
+ params: matchedParams,
14537
+ pathname: normPath,
14538
+ sourceCategory: matchedRule.sourceCategory,
14539
+ isOverridden,
14540
+ originalDefaultApps: baseDefaultApps,
14541
+ originalDefaultPresetId: matchedRule.defaultPresetId,
14542
+ missingConfig,
14543
+ sideshift: matchedRule.sideshift
14544
+ };
14545
+ };
14546
+
13318
14547
  // components/AgentUI.tsx
13319
14548
  var import_jsx_runtime28 = require("react/jsx-runtime");
13320
14549
  var normalizeMarkdown = (raw) => {
@@ -13339,14 +14568,27 @@ var LAST_PRESET_STORAGE_KEY = "agent_last_preset_id";
13339
14568
  var PRESET_APPS_STORAGE_KEY = "agent_preset_apps_overrides";
13340
14569
  var NO_PRESET_KEY = "__none__";
13341
14570
  var PRESET_TEMPLATE_SLUGS = {
13342
- "build-workflows": "workflow-edit",
14571
+ "build-workflows": "edit-workflow",
13343
14572
  "handle-notifications": "handle-notifications",
13344
- "incident-response": "incident-response",
14573
+ "incident-response": "incident-handler",
13345
14574
  "host-monitor-control": "computer-use",
13346
14575
  support: "support",
13347
14576
  vulnerability: "vulnerability",
13348
14577
  detection: "detection"
13349
14578
  };
14579
+ var TEMPLATE_SLUG_TO_PRESET_ID = {
14580
+ "workflow-edit": "build-workflows",
14581
+ "edit-workflow": "build-workflows",
14582
+ "build-workflows": "build-workflows",
14583
+ "incident-response": "incident-response",
14584
+ "incident-handler": "incident-response",
14585
+ "vulnerability": "vulnerability",
14586
+ "vulnerability-management": "vulnerability",
14587
+ "computer-use": "host-monitor-control",
14588
+ support: "support",
14589
+ "handle-notifications": "handle-notifications",
14590
+ detection: "detection"
14591
+ };
13350
14592
  var readPresetAppsOverride = (presetId) => {
13351
14593
  try {
13352
14594
  const raw = localStorage.getItem(PRESET_APPS_STORAGE_KEY);
@@ -14478,6 +15720,7 @@ var AgentUI = ({
14478
15720
  heroIconSize = 84,
14479
15721
  maxWidth = 850,
14480
15722
  compact = false,
15723
+ mobileView,
14481
15724
  hideAppPicker = false,
14482
15725
  hideAttach = false,
14483
15726
  disableStartTab = false,
@@ -14512,10 +15755,14 @@ var AgentUI = ({
14512
15755
  userId,
14513
15756
  hidePresets = false,
14514
15757
  presets,
15758
+ initialPresetId,
14515
15759
  onSelectPreset,
15760
+ contextCategory,
15761
+ contextStorageKey,
14516
15762
  isSupport,
14517
15763
  presetCtas
14518
15764
  }) => {
15765
+ const isEffectiveSupport = isSupport !== void 0 ? isSupport : isSupportUser();
14519
15766
  const resolveUrl2 = (0, import_react22.useCallback)(
14520
15767
  (path) => apiBaseUrl ? `${apiBaseUrl.replace(/\/+$/, "")}${path}` : getApiUrl(path),
14521
15768
  [apiBaseUrl]
@@ -14526,7 +15773,8 @@ var AgentUI = ({
14526
15773
  return h;
14527
15774
  }, [apiKey, orgId]);
14528
15775
  const hasApiKey = !!apiKey || !!API_CONFIG.apiKey;
14529
- const isPhone = (0, import_material23.useMediaQuery)("(max-width:600px)", { noSsr: true });
15776
+ const isPhoneScreen = (0, import_material23.useMediaQuery)("(max-width:600px)", { noSsr: true });
15777
+ const isPhone = mobileView !== void 0 ? mobileView : isPhoneScreen;
14530
15778
  const navigate = (0, import_router_compat2.useNavigate)();
14531
15779
  const [actionInput, setActionInput] = (0, import_react22.useState)(defaultInput);
14532
15780
  const { prompt: savedPromptPrefix } = useAgentPromptPrefix({ userId, persist: !disableStartTab });
@@ -14562,27 +15810,32 @@ var AgentUI = ({
14562
15810
  const inputRef = (0, import_react22.useRef)(null);
14563
15811
  const seededPresetIdRef = (0, import_react22.useRef)(null);
14564
15812
  (0, import_react22.useEffect)(() => {
14565
- if (defaultInput && defaultInput.trim().length > 0) {
15813
+ if (!initialPresetId && defaultInput && defaultInput.trim().length > 0) {
14566
15814
  setSelectedPreset(null);
14567
15815
  return;
14568
15816
  }
14569
15817
  try {
14570
- const lastId = localStorage.getItem(LAST_PRESET_STORAGE_KEY);
14571
- if (!lastId) return;
14572
- const list = filterAgentPresets(presets && presets.length > 0 ? presets : AGENT_PRESETS, isSupport);
15818
+ const lastId = initialPresetId !== void 0 ? initialPresetId : localStorage.getItem(LAST_PRESET_STORAGE_KEY);
15819
+ if (!lastId) {
15820
+ if (initialPresetId === null) setSelectedPreset(null);
15821
+ return;
15822
+ }
15823
+ const list = filterAgentPresets(presets && presets.length > 0 ? presets : AGENT_PRESETS, isEffectiveSupport);
14573
15824
  const match = list.find((p) => p.id === lastId);
14574
15825
  if (!match || match.enabled === false) return;
14575
15826
  setSelectedPreset(match);
14576
- const override = readPresetAppsOverride(match.id);
14577
- if (override) {
14578
- setChosenApps(override);
14579
- } else if (match.defaultApps && match.defaultApps.length > 0) {
14580
- setChosenApps(match.defaultApps.map((app2) => ({ name: app2.name, id: app2.id, icon: app2.icon })));
15827
+ if (!defaultApps && !apps) {
15828
+ const override = readPresetAppsOverride(match.id);
15829
+ if (override) {
15830
+ setChosenApps(override);
15831
+ } else if (match.defaultApps && match.defaultApps.length > 0) {
15832
+ setChosenApps(match.defaultApps.map((app2) => ({ name: app2.name, id: app2.id, icon: app2.icon })));
15833
+ }
14581
15834
  }
14582
15835
  seededPresetIdRef.current = match.id;
14583
15836
  } catch {
14584
15837
  }
14585
- }, [presets, defaultInput, isSupport]);
15838
+ }, [presets, defaultInput, isEffectiveSupport, initialPresetId, defaultApps, apps]);
14586
15839
  (0, import_react22.useLayoutEffect)(() => {
14587
15840
  if (hidePresets) {
14588
15841
  setPresetsChipWidth(0);
@@ -14807,21 +16060,55 @@ var AgentUI = ({
14807
16060
  [agentData]
14808
16061
  );
14809
16062
  const [showStarter, setShowStarter] = (0, import_react22.useState)(true);
14810
- const [scheduleAnchor, setScheduleAnchor] = (0, import_react22.useState)(null);
14811
- const [mobilePlusAnchor, setMobilePlusAnchor] = (0, import_react22.useState)(null);
14812
- const [scheduleCron, setScheduleCron] = (0, import_react22.useState)("0 * * * *");
14813
- const [scheduleSaving, setScheduleSaving] = (0, import_react22.useState)(false);
14814
- const [scheduleSteps, setScheduleSteps] = (0, import_react22.useState)([
14815
- { id: "name", state: "pending" },
14816
- { id: "workflow", state: "pending" },
14817
- { id: "schedule", state: "pending" }
14818
- ]);
14819
- const [schedFreq, setSchedFreq] = (0, import_react22.useState)("hours");
14820
- const [schedInterval, setSchedInterval] = (0, import_react22.useState)(1);
14821
- const [schedHour, setSchedHour] = (0, import_react22.useState)(9);
14822
- const [schedMinute, setSchedMinute] = (0, import_react22.useState)(0);
14823
- const [schedWeekdays, setSchedWeekdays] = (0, import_react22.useState)(() => /* @__PURE__ */ new Set([1]));
14824
- const [schedDayOfMonth, setSchedDayOfMonth] = (0, import_react22.useState)(1);
16063
+ const handleRemovePreset = (0, import_react22.useCallback)(() => {
16064
+ try {
16065
+ localStorage.removeItem(LAST_PRESET_STORAGE_KEY);
16066
+ } catch {
16067
+ }
16068
+ setSelectedPreset(null);
16069
+ }, []);
16070
+ const handleSelectPresetInternal = (0, import_react22.useCallback)((preset) => {
16071
+ try {
16072
+ localStorage.setItem(LAST_PRESET_STORAGE_KEY, preset.id);
16073
+ } catch {
16074
+ }
16075
+ const override = readPresetAppsOverride(preset.id);
16076
+ if (override && override.length > 0) {
16077
+ setChosenApps(override);
16078
+ } else if (preset.defaultApps && preset.defaultApps.length > 0) {
16079
+ setChosenApps(preset.defaultApps.map((app2) => ({ name: app2.name, id: app2.id, icon: app2.icon })));
16080
+ } else {
16081
+ setChosenApps([]);
16082
+ }
16083
+ seededPresetIdRef.current = preset.id;
16084
+ if (onSelectPreset) {
16085
+ onSelectPreset(preset);
16086
+ return;
16087
+ }
16088
+ setSelectedPreset(preset);
16089
+ setTimeout(() => {
16090
+ const el = inputRef.current;
16091
+ try {
16092
+ el?.focus();
16093
+ } catch {
16094
+ }
16095
+ }, 0);
16096
+ }, [onSelectPreset]);
16097
+ const [scheduleAnchor, setScheduleAnchor] = (0, import_react22.useState)(null);
16098
+ const [mobilePlusAnchor, setMobilePlusAnchor] = (0, import_react22.useState)(null);
16099
+ const [scheduleCron, setScheduleCron] = (0, import_react22.useState)("0 * * * *");
16100
+ const [scheduleSaving, setScheduleSaving] = (0, import_react22.useState)(false);
16101
+ const [scheduleSteps, setScheduleSteps] = (0, import_react22.useState)([
16102
+ { id: "name", state: "pending" },
16103
+ { id: "workflow", state: "pending" },
16104
+ { id: "schedule", state: "pending" }
16105
+ ]);
16106
+ const [schedFreq, setSchedFreq] = (0, import_react22.useState)("hours");
16107
+ const [schedInterval, setSchedInterval] = (0, import_react22.useState)(1);
16108
+ const [schedHour, setSchedHour] = (0, import_react22.useState)(9);
16109
+ const [schedMinute, setSchedMinute] = (0, import_react22.useState)(0);
16110
+ const [schedWeekdays, setSchedWeekdays] = (0, import_react22.useState)(() => /* @__PURE__ */ new Set([1]));
16111
+ const [schedDayOfMonth, setSchedDayOfMonth] = (0, import_react22.useState)(1);
14825
16112
  const [schedAdvancedOpen, setSchedAdvancedOpen] = (0, import_react22.useState)(false);
14826
16113
  const cronManualOverrideRef = (0, import_react22.useRef)(false);
14827
16114
  const [openIndexes, setOpenIndexes] = (0, import_react22.useState)(/* @__PURE__ */ new Set());
@@ -14864,7 +16151,7 @@ var AgentUI = ({
14864
16151
  }, [viewMode]);
14865
16152
  const [attachedImages, setAttachedImages] = (0, import_react22.useState)([]);
14866
16153
  const [previewImage, setPreviewImage] = (0, import_react22.useState)(null);
14867
- const promptMultiline = promptMultilineBase || attachedImages.length > 0;
16154
+ const promptMultiline = promptMultilineBase || attachedImages.length > 0 || isPhone && Boolean(selectedPreset);
14868
16155
  const promptSingleLineLocked = !promptMultiline;
14869
16156
  const [nowTick, setNowTick] = (0, import_react22.useState)(() => Math.floor(Date.now() / 1e3));
14870
16157
  const [localRunStart, setLocalRunStart] = (0, import_react22.useState)(null);
@@ -15118,11 +16405,18 @@ var AgentUI = ({
15118
16405
  const loadAuthenticatedApps = (0, import_react22.useCallback)(async (signal) => {
15119
16406
  setAuthAppsLoading(true);
15120
16407
  try {
15121
- const resp = await fetch(resolveUrl2("/api/v1/apps/authentication"), {
15122
- credentials: "include",
15123
- headers: { ...resolveHeaders2() }
15124
- });
15125
- if (!resp.ok) {
16408
+ const [authRes, wfRes] = await Promise.allSettled([
16409
+ fetch(resolveUrl2("/api/v1/apps/authentication"), {
16410
+ credentials: "include",
16411
+ headers: { ...resolveHeaders2() }
16412
+ }),
16413
+ fetch(resolveUrl2("/api/v1/workflows"), {
16414
+ credentials: "include",
16415
+ headers: { ...resolveHeaders2() }
16416
+ })
16417
+ ]);
16418
+ const resp = authRes.status === "fulfilled" ? authRes.value : null;
16419
+ if (!resp || !resp.ok) {
15126
16420
  if (!signal?.cancelled) setAvailableApps([]);
15127
16421
  return;
15128
16422
  }
@@ -15147,12 +16441,53 @@ var AgentUI = ({
15147
16441
  }
15148
16442
  if (signal?.cancelled) return;
15149
16443
  setAvailableApps(loaded);
16444
+ const wfResp = wfRes.status === "fulfilled" ? wfRes.value : null;
16445
+ const wfResult = wfResp && wfResp.ok ? await wfResp.json() : [];
16446
+ const effectiveCategory = contextCategory || ((defaultApps || chosenApps).some((a) => normalizeAgentAppName(a.name || "").includes("incident")) ? "incidents" : (defaultApps || chosenApps).some((a) => normalizeAgentAppName(a.name || "").includes("vulnerabilit")) ? "vulnerabilities" : void 0);
16447
+ let connectedTools = [];
16448
+ if (effectiveCategory) {
16449
+ connectedTools = resolveConnectedTools(effectiveCategory, list, wfResult);
16450
+ if (connectedTools.length > 0) {
16451
+ setCachedConnectedTools(effectiveCategory, connectedTools);
16452
+ }
16453
+ }
16454
+ setChosenApps((prev) => {
16455
+ let changed = false;
16456
+ const savedChoice = contextStorageKey ? getPageContextChoice(contextStorageKey) : null;
16457
+ let baseList = prev;
16458
+ if (!savedChoice?.apps && connectedTools.length > 0) {
16459
+ const merged = mergeConnectedTools(prev, connectedTools);
16460
+ if (merged.length !== prev.length || merged.some((m, idx) => prev[idx]?.name !== m.name)) {
16461
+ baseList = merged;
16462
+ changed = true;
16463
+ }
16464
+ }
16465
+ const updated = baseList.map((app2) => {
16466
+ if (app2.icon && app2.id) return app2;
16467
+ const slug = normalizeAgentAppName(app2.name || "");
16468
+ const match = loaded.find((a) => normalizeAgentAppName(a.name || "") === slug);
16469
+ if (!match) return app2;
16470
+ if (!app2.icon && match.icon || !app2.id && match.id) {
16471
+ changed = true;
16472
+ return {
16473
+ ...app2,
16474
+ id: app2.id || match.id,
16475
+ icon: app2.icon || match.icon
16476
+ };
16477
+ }
16478
+ return app2;
16479
+ });
16480
+ if (changed && onAppsChange) {
16481
+ onAppsChange(updated);
16482
+ }
16483
+ return changed ? updated : prev;
16484
+ });
15150
16485
  setDetectedLLM(resolveActiveLLMProvider(list));
15151
16486
  } catch {
15152
16487
  } finally {
15153
16488
  if (!signal?.cancelled) setAuthAppsLoading(false);
15154
16489
  }
15155
- }, [resolveUrl2, resolveHeaders2]);
16490
+ }, [resolveUrl2, resolveHeaders2, contextCategory, contextStorageKey, defaultApps, onAppsChange]);
15156
16491
  (0, import_react22.useEffect)(() => {
15157
16492
  if (!autoLoadApps) return;
15158
16493
  if (!hasApiKey) return;
@@ -15757,7 +17092,9 @@ var AgentUI = ({
15757
17092
  if (!raw) return null;
15758
17093
  const slug = String(raw).trim().toLowerCase();
15759
17094
  const list = presets && presets.length > 0 ? presets : AGENT_PRESETS;
15760
- return list.find((p) => p.id === slug || PRESET_TEMPLATE_SLUGS[p.id] === slug) ?? null;
17095
+ return list.find(
17096
+ (p) => p.id === slug || PRESET_TEMPLATE_SLUGS[p.id] === slug || TEMPLATE_SLUG_TO_PRESET_ID[slug] === p.id
17097
+ ) ?? null;
15761
17098
  }, [agentData, agentActionResult, execution, presets]);
15762
17099
  const rerunAgent = (0, import_react22.useCallback)(() => {
15763
17100
  const resolved = resolveRunInput();
@@ -16504,14 +17841,13 @@ var AgentUI = ({
16504
17841
  gap: 1,
16505
17842
  width: isPhone ? "100%" : "fit-content",
16506
17843
  alignSelf: "center",
16507
- position: isPhone ? "fixed" : "sticky",
17844
+ position: "sticky",
16508
17845
  top: isPhone ? 0 : 8,
16509
- left: isPhone ? 0 : "auto",
16510
- right: isPhone ? 0 : "auto",
16511
- zIndex: isPhone ? 1300 : 5,
17846
+ left: "auto",
17847
+ right: "auto",
17848
+ zIndex: isPhone ? 20 : 5,
16512
17849
  bgcolor: isPhone ? "hsl(var(--background))" : "hsl(var(--card))",
16513
17850
  borderRadius: isPhone ? 0 : 9999,
16514
- borderBottom: isPhone ? "1px solid hsl(var(--border))" : "none",
16515
17851
  boxShadow: isPhone ? "0 4px 16px rgba(0,0,0,0.12)" : "none",
16516
17852
  px: isPhone ? 1 : 0,
16517
17853
  py: isPhone ? 1 : 0
@@ -16964,7 +18300,7 @@ var AgentUI = ({
16964
18300
  ]
16965
18301
  }
16966
18302
  );
16967
- const postRunDiscovery = Boolean(postRunScheduleHint) && !scheduleDisabledReason || postRunAppReqs.length > 0 || postRunUnauthedApps.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
18303
+ const postRunDiscovery = isEffectiveSupport && (Boolean(postRunScheduleHint) && !scheduleDisabledReason || postRunAppReqs.length > 0 || postRunUnauthedApps.length > 0) ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
16968
18304
  import_material23.Box,
16969
18305
  {
16970
18306
  sx: {
@@ -16978,7 +18314,47 @@ var AgentUI = ({
16978
18314
  bgcolor: "hsl(var(--muted) / 0.35)"
16979
18315
  },
16980
18316
  children: [
16981
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_material23.Typography, { sx: { fontSize: "0.78rem", color: "hsl(var(--muted-foreground))", mr: 0.5 }, children: "Set this up to run on its own" }),
18317
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
18318
+ import_material23.Tooltip,
18319
+ {
18320
+ title: "Visible to support users only \u2014 this helps guide us towards things working better over time.",
18321
+ arrow: true,
18322
+ children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
18323
+ import_material23.Box,
18324
+ {
18325
+ sx: {
18326
+ display: "inline-flex",
18327
+ alignItems: "center",
18328
+ gap: 0.75,
18329
+ mr: 0.5,
18330
+ cursor: "help"
18331
+ },
18332
+ children: [
18333
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_material23.Typography, { sx: { fontSize: "0.78rem", color: "hsl(var(--muted-foreground))" }, children: "Set this up to run on its own" }),
18334
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
18335
+ import_material23.Box,
18336
+ {
18337
+ component: "span",
18338
+ sx: {
18339
+ fontSize: "0.64rem",
18340
+ fontWeight: 600,
18341
+ textTransform: "uppercase",
18342
+ letterSpacing: "0.04em",
18343
+ px: 0.75,
18344
+ py: 0.15,
18345
+ borderRadius: "4px",
18346
+ bgcolor: "hsl(var(--muted))",
18347
+ color: "hsl(var(--muted-foreground))",
18348
+ border: "1px solid hsl(var(--border))"
18349
+ },
18350
+ children: "Support only"
18351
+ }
18352
+ )
18353
+ ]
18354
+ }
18355
+ )
18356
+ }
18357
+ ),
16982
18358
  postRunScheduleHint && !scheduleDisabledReason && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_material23.Tooltip, { title: `Detected schedule: ${postRunScheduleHint.label}. Click to review and save.`, arrow: true, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
16983
18359
  import_material23.Box,
16984
18360
  {
@@ -17073,22 +18449,46 @@ var AgentUI = ({
17073
18449
  ) : null;
17074
18450
  const mobilePlusCanSchedule = (hasExecution || showStarter) && !scheduleDisabledReason && !disableSchedule && (actionInput || "").trim().length >= 1;
17075
18451
  const mobilePlusButton = isPhone ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
17076
- import_material23.IconButton,
18452
+ import_material23.Tooltip,
18453
+ {
18454
+ title: selectedPreset ? `Skill: ${selectedPreset.label} (click to change)` : "Add skill, schedule, or attachments",
18455
+ arrow: true,
18456
+ children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
18457
+ import_material23.IconButton,
18458
+ {
18459
+ type: "button",
18460
+ "aria-label": "Skills, schedule and attachments",
18461
+ onClick: (e) => setMobilePlusAnchor(e.currentTarget),
18462
+ sx: {
18463
+ width: 32,
18464
+ height: 32,
18465
+ flexShrink: 0,
18466
+ alignSelf: "center",
18467
+ border: "1px solid hsl(var(--border))",
18468
+ color: selectedPreset ? "hsl(var(--foreground))" : "hsl(var(--muted-foreground))",
18469
+ bgcolor: selectedPreset ? "hsl(var(--muted))" : "transparent",
18470
+ "&:hover": { bgcolor: "hsl(var(--muted))", color: "hsl(var(--foreground))" }
18471
+ },
18472
+ children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_lucide_react14.Plus, { size: 16 })
18473
+ }
18474
+ )
18475
+ }
18476
+ ) : null;
18477
+ const mobileSkillChip = isPhone && selectedPreset && !hidePresets ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
18478
+ AgentPresets_default,
17077
18479
  {
17078
- type: "button",
17079
- "aria-label": "Skills, schedule and attachments",
17080
- onClick: (e) => setMobilePlusAnchor(e.currentTarget),
18480
+ variant: "floating",
18481
+ chipRef: presetsChipRef,
18482
+ presets,
18483
+ isSupport: isEffectiveSupport,
18484
+ selectedPreset,
18485
+ onRemoveSelected: handleRemovePreset,
18486
+ onSelectPreset: handleSelectPresetInternal,
18487
+ placement: "bottom-start",
17081
18488
  sx: {
17082
- width: 32,
17083
18489
  height: 32,
17084
- flexShrink: 0,
17085
- alignSelf: "flex-start",
17086
- border: "1px solid hsl(var(--border))",
17087
- color: selectedPreset ? "hsl(var(--foreground))" : "hsl(var(--muted-foreground))",
17088
- bgcolor: selectedPreset ? "hsl(var(--muted))" : "transparent",
17089
- "&:hover": { bgcolor: "hsl(var(--muted))", color: "hsl(var(--foreground))" }
17090
- },
17091
- children: selectedPreset?.icon ?? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_lucide_react14.Plus, { size: 16 })
18490
+ alignSelf: "center"
18491
+ }
17092
18492
  }
17093
18493
  ) : null;
17094
18494
  const mobilePlusMenu = isPhone ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
@@ -17148,7 +18548,7 @@ var AgentUI = ({
17148
18548
  ]
17149
18549
  }
17150
18550
  ),
17151
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
18551
+ !disableSchedule && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
17152
18552
  import_material23.MenuItem,
17153
18553
  {
17154
18554
  disabled: !mobilePlusCanSchedule || agentRequestLoading,
@@ -17211,8 +18611,7 @@ var AgentUI = ({
17211
18611
  ...Array.isArray(contentSx) ? contentSx : contentSx ? [contentSx] : []
17212
18612
  ],
17213
18613
  children: [
17214
- (showRunSwitcher || isPhone) && tabBar,
17215
- isPhone && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_material23.Box, { sx: { height: 52, flexShrink: 0, flexGrow: 0 } }),
18614
+ showRunSwitcher && tabBar,
17216
18615
  schedulePopover,
17217
18616
  mobilePlusMenu,
17218
18617
  showStarter ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
@@ -17230,7 +18629,7 @@ var AgentUI = ({
17230
18629
  gap: isPhone ? 1.5 : compact ? 2 : 3,
17231
18630
  py: isPhone ? 1 : compact ? 2 : 4,
17232
18631
  width: "100%",
17233
- ...isPhone ? { flex: 1, justifyContent: "center" } : {}
18632
+ ...isPhone ? { flex: 1, justifyContent: "center", marginTop: compact ? "-16px" : "-50px" } : {}
17234
18633
  },
17235
18634
  children: [
17236
18635
  !hideHeroIcon && !compact && !isPhone && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_material23.Box, { sx: {
@@ -17244,13 +18643,13 @@ var AgentUI = ({
17244
18643
  justifyContent: "center",
17245
18644
  boxShadow: "0 8px 24px rgba(0,0,0,0.25)"
17246
18645
  }, children: heroIcon ?? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(AgentIcon_default, { size: Math.round(heroIconSize * 0.67) }) }),
17247
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_material23.Typography, { component: "h1", sx: {
17248
- fontSize: compact ? { xs: "1.1rem", md: "1.5rem" } : { xs: "1.35rem", md: "2.25rem" },
18646
+ title ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_material23.Typography, { component: "h1", sx: {
18647
+ fontSize: isPhone ? "1.35rem" : compact ? { xs: "1.1rem", md: "1.5rem" } : { xs: "1.35rem", md: "2.25rem" },
17249
18648
  fontWeight: 600,
17250
18649
  color: "hsl(var(--foreground))",
17251
18650
  textAlign: "center",
17252
18651
  letterSpacing: "-0.01em"
17253
- }, children: title }),
18652
+ }, children: title }) : null,
17254
18653
  subtitle && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_material23.Typography, { sx: {
17255
18654
  fontSize: { xs: "0.8rem", sm: "0.95rem" },
17256
18655
  color: "hsl(var(--muted-foreground))",
@@ -17265,11 +18664,11 @@ var AgentUI = ({
17265
18664
  gap: 0.5,
17266
18665
  // Keep the same radius in both states so growing to multiple
17267
18666
  // lines does not visually snap from a pill to a boxy card.
17268
- borderRadius: "28px",
18667
+ borderRadius: isPhone ? promptMultiline ? "20px" : "28px" : "28px",
17269
18668
  border: "1.5px solid hsl(var(--border))",
17270
18669
  bgcolor: "hsl(var(--card))",
17271
- px: isPhone ? 1 : 2.25,
17272
- py: 1,
18670
+ px: isPhone ? 1.5 : 2.25,
18671
+ py: isPhone ? promptMultiline ? 1.25 : 1 : 1,
17273
18672
  boxSizing: "border-box",
17274
18673
  // A one-line prompt is a fixed-size control. Lock all three
17275
18674
  // dimensions so TextareaAutosize cannot retain a stale
@@ -17302,7 +18701,10 @@ var AgentUI = ({
17302
18701
  maxHeight: "33px"
17303
18702
  } : {}
17304
18703
  }, children: [
17305
- isPhone && !promptMultiline && mobilePlusButton,
18704
+ isPhone && !promptMultiline && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_material23.Box, { sx: { display: "inline-flex", alignItems: "center", gap: 0.5, flexShrink: 0 }, children: [
18705
+ mobilePlusButton,
18706
+ mobileSkillChip
18707
+ ] }),
17306
18708
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
17307
18709
  import_material23.InputBase,
17308
18710
  {
@@ -17412,9 +18814,8 @@ var AgentUI = ({
17412
18814
  ref: chipOverlayRef,
17413
18815
  "data-static": promptMultiline ? "1" : "0",
17414
18816
  sx: isPhone ? {
17415
- // Mobile: the skill chip lives inside the "+" menu. The
17416
- // trigger stays mounted (invisible) so the menu can still
17417
- // be opened programmatically and anchored correctly.
18817
+ // Mobile: when no preset is selected, keep an invisible trigger mounted
18818
+ // so the "+" menu can programmatically open the skills picker.
17418
18819
  position: "absolute",
17419
18820
  left: 0,
17420
18821
  bottom: 0,
@@ -17466,48 +18867,16 @@ var AgentUI = ({
17466
18867
  to: { transform: "translateY(0)", opacity: 1 }
17467
18868
  }
17468
18869
  },
17469
- children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
18870
+ children: (!isPhone || !selectedPreset) && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
17470
18871
  AgentPresets_default,
17471
18872
  {
17472
18873
  variant: "floating",
17473
- chipRef: presetsChipRef,
18874
+ chipRef: !isPhone || !selectedPreset ? presetsChipRef : void 0,
17474
18875
  presets,
17475
- isSupport,
18876
+ isSupport: isEffectiveSupport,
17476
18877
  selectedPreset,
17477
- onRemoveSelected: () => {
17478
- try {
17479
- localStorage.removeItem(LAST_PRESET_STORAGE_KEY);
17480
- } catch {
17481
- }
17482
- setSelectedPreset(null);
17483
- },
17484
- onSelectPreset: (preset) => {
17485
- try {
17486
- localStorage.setItem(LAST_PRESET_STORAGE_KEY, preset.id);
17487
- } catch {
17488
- }
17489
- const override = readPresetAppsOverride(preset.id);
17490
- if (override && override.length > 0) {
17491
- setChosenApps(override);
17492
- } else if (preset.defaultApps && preset.defaultApps.length > 0) {
17493
- setChosenApps(preset.defaultApps.map((app2) => ({ name: app2.name, id: app2.id, icon: app2.icon })));
17494
- } else {
17495
- setChosenApps([]);
17496
- }
17497
- seededPresetIdRef.current = preset.id;
17498
- if (onSelectPreset) {
17499
- onSelectPreset(preset);
17500
- return;
17501
- }
17502
- setSelectedPreset(preset);
17503
- setTimeout(() => {
17504
- const el = inputRef.current;
17505
- try {
17506
- el?.focus();
17507
- } catch {
17508
- }
17509
- }, 0);
17510
- }
18878
+ onRemoveSelected: handleRemovePreset,
18879
+ onSelectPreset: handleSelectPresetInternal
17511
18880
  }
17512
18881
  )
17513
18882
  }
@@ -17545,7 +18914,10 @@ var AgentUI = ({
17545
18914
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_material23.Typography, { sx: { fontSize: "0.7rem", color: "hsl(var(--foreground))", maxWidth: 140, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: img.name }),
17546
18915
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_material23.IconButton, { size: "small", onClick: () => setAttachedImages((prev) => prev.filter((_, i) => i !== idx)), sx: { p: 0.125, color: "hsl(var(--muted-foreground))", "&:hover": { color: "hsl(var(--destructive))" } }, "aria-label": "Remove attached image", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_lucide_react14.X, { size: 11 }) })
17547
18916
  ] }, `${img.name}-${idx}`)) }),
17548
- isPhone && promptMultiline && mobilePlusButton,
18917
+ isPhone && promptMultiline && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_material23.Box, { sx: { display: "inline-flex", alignItems: "center", gap: 0.5, flexShrink: 0, minWidth: 0, maxWidth: "calc(100% - 60px)" }, children: [
18918
+ mobilePlusButton,
18919
+ mobileSkillChip
18920
+ ] }),
17549
18921
  /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_material23.Box, { sx: {
17550
18922
  display: "flex",
17551
18923
  alignItems: "center",
@@ -17933,7 +19305,7 @@ var AgentUI = ({
17933
19305
  ]
17934
19306
  }
17935
19307
  ) }),
17936
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
19308
+ !hideChooseLLM && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
17937
19309
  import_material23.Box,
17938
19310
  {
17939
19311
  component: "span",
@@ -18010,15 +19382,18 @@ var AgentUI = ({
18010
19382
  "&:hover": !agentRequestLoading ? { bgcolor: needsAuth ? "hsl(var(--severity-medium) / 0.18)" : "hsl(var(--muted) / 0.9)" } : {}
18011
19383
  },
18012
19384
  children: [
18013
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
18014
- import_material23.Avatar,
18015
- {
18016
- src: app2.icon || void 0,
18017
- alt: app2.name,
18018
- variant: "rounded",
18019
- sx: { width: 18, height: 18, bgcolor: "transparent" }
18020
- }
18021
- ),
19385
+ (() => {
19386
+ const appIcon = app2.icon || availableApps.find((a) => normalizeAgentAppName(a.name || "") === slug)?.icon || resolvedToolApps[slug]?.icon;
19387
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_material23.Box, { sx: { display: "inline-flex", alignItems: "center", justifyContent: "center", width: 18, height: 18, flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
19388
+ AppFallbackIcon,
19389
+ {
19390
+ name: app2.name,
19391
+ imageUrl: appIcon,
19392
+ size: 18,
19393
+ style: { borderRadius: 3, objectFit: "contain" }
19394
+ }
19395
+ ) });
19396
+ })(),
18022
19397
  !isPhone && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_material23.Typography, { sx: { fontSize: "0.8rem", mx: 0.25, textTransform: "capitalize" }, children: app2.name.replace(/_/g, " ") }),
18023
19398
  needsAuth && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_lucide_react14.AlertTriangle, { size: 14, color: "hsl(var(--severity-medium))", style: { marginRight: 2 } }),
18024
19399
  isRequired ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
@@ -18183,21 +19558,38 @@ var AgentUI = ({
18183
19558
  const hasAllowed = Array.isArray(agentData?.allowed_actions) && agentData.allowed_actions.length > 0;
18184
19559
  const list = hasAllowed ? executionApps : chosenApps;
18185
19560
  return list;
18186
- })().map((app2, i) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_material23.Tooltip, { title: (app2.name || "").replace(/_/g, " "), children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
18187
- import_material23.Avatar,
18188
- {
18189
- src: app2.icon || void 0,
18190
- alt: app2.name,
18191
- variant: "rounded",
18192
- onClick: () => setAuthDrawerApp({ name: app2.name, id: app2.id || null }),
18193
- sx: {
18194
- bgcolor: "hsl(var(--muted))",
18195
- cursor: "pointer",
18196
- transition: "transform 0.15s ease, border-color 0.15s ease",
18197
- "&:hover": { transform: "scale(1.08)", borderColor: "hsl(var(--primary)) !important" }
19561
+ })().map((app2, i) => {
19562
+ const slug = normalizeAgentAppName(app2.name || "");
19563
+ const appIcon = app2.icon || availableApps.find((a) => normalizeAgentAppName(a.name || "") === slug)?.icon || resolvedToolApps[slug]?.icon;
19564
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_material23.Tooltip, { title: (app2.name || "").replace(/_/g, " "), children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
19565
+ import_material23.Box,
19566
+ {
19567
+ onClick: () => setAuthDrawerApp({ name: app2.name, id: app2.id || null }),
19568
+ sx: {
19569
+ display: "inline-flex",
19570
+ alignItems: "center",
19571
+ justifyContent: "center",
19572
+ width: 24,
19573
+ height: 24,
19574
+ borderRadius: "6px",
19575
+ overflow: "hidden",
19576
+ bgcolor: "hsl(var(--muted))",
19577
+ cursor: "pointer",
19578
+ transition: "transform 0.15s ease, border-color 0.15s ease",
19579
+ "&:hover": { transform: "scale(1.08)", borderColor: "hsl(var(--primary)) !important" }
19580
+ },
19581
+ children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
19582
+ AppFallbackIcon,
19583
+ {
19584
+ name: app2.name,
19585
+ imageUrl: appIcon,
19586
+ size: 24,
19587
+ style: { borderRadius: 6, objectFit: "contain" }
19588
+ }
19589
+ )
18198
19590
  }
18199
- }
18200
- ) }, i)) }),
19591
+ ) }, i);
19592
+ }) }),
18201
19593
  /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_material23.Box, { sx: { flex: 1, minWidth: 0 }, children: [
18202
19594
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_material23.Typography, { sx: { fontSize: "0.85rem", color: "hsl(var(--foreground))", fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: agentData?.original_input || actionInput || "Agent run" }),
18203
19595
  /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_material23.Typography, { sx: { fontSize: "0.7rem", color: "hsl(var(--muted-foreground))" }, children: [
@@ -21447,6 +22839,7 @@ var AgentExecutionDrawer = ({
21447
22839
  bodySx,
21448
22840
  topBanner,
21449
22841
  onSchedule,
22842
+ isSupport,
21450
22843
  theme,
21451
22844
  colorMode
21452
22845
  }) => {
@@ -21623,6 +23016,7 @@ var AgentExecutionDrawer = ({
21623
23016
  apiBaseUrl,
21624
23017
  orgId,
21625
23018
  onSchedule,
23019
+ isSupport,
21626
23020
  theme,
21627
23021
  colorMode
21628
23022
  },
@@ -21906,6 +23300,7 @@ var AgentsView = ({
21906
23300
  onSchedule: effectiveSchedule,
21907
23301
  apiKey,
21908
23302
  orgId,
23303
+ isSupport: userdata?.support === true || userdata?.support === "true",
21909
23304
  theme: resolvedTheme,
21910
23305
  colorMode
21911
23306
  }
@@ -22302,55 +23697,964 @@ var AddAppModal = ({ open, onClose, initialQuery, categoryLabel }) => {
22302
23697
  };
22303
23698
  var AddAppModal_default = AddAppModal;
22304
23699
 
22305
- // components/AgentPromptPrefixChip.tsx
22306
- var import_react31 = require("react");
23700
+ // components/AskAiButton.tsx
22307
23701
  var import_material31 = require("@mui/material");
22308
- var import_lucide_react21 = require("lucide-react");
22309
23702
  var import_jsx_runtime36 = require("react/jsx-runtime");
22310
- var AgentPromptPrefixChip = ({
22311
- label = "Shuffle Tools MCP",
22312
- userId,
22313
- value,
22314
- onChange,
22315
- onRemove,
22316
- defaultPrompt,
22317
- dialogTitle,
22318
- readOnly
23703
+ var AskAiButton = ({
23704
+ onClick,
23705
+ isOpen = false,
23706
+ isSupport,
23707
+ requireSupport = true,
23708
+ pathname,
23709
+ label = "Ask AI",
23710
+ tagLabel = "Support only",
23711
+ contextHint,
23712
+ tooltipTitle,
23713
+ sx,
23714
+ hideWhenOpen = true
22319
23715
  }) => {
22320
- const controlled = value !== void 0;
22321
- const managed = useAgentPromptPrefix({ userId, defaultPrompt, persist: !controlled });
22322
- const prompt = controlled ? value : managed.prompt;
22323
- const isSaving = controlled ? false : managed.isSaving;
22324
- const [open, setOpen] = (0, import_react31.useState)(false);
22325
- const [draft, setDraft] = (0, import_react31.useState)(prompt);
22326
- (0, import_react31.useEffect)(() => {
22327
- if (!open) setDraft(prompt);
22328
- }, [prompt, open]);
22329
- const handleOpen = () => {
22330
- if (readOnly) return;
22331
- setDraft(prompt);
22332
- setOpen(true);
22333
- };
22334
- const handleSave = async () => {
22335
- if (controlled) {
22336
- await onChange?.(draft);
22337
- } else {
22338
- await managed.savePrompt(draft);
23716
+ const themeScope = useShuffleMcpTheme();
23717
+ const currentPath = pathname !== void 0 ? pathname : typeof window !== "undefined" ? window.location.pathname : "";
23718
+ if (isAgentRoute(currentPath)) {
23719
+ return null;
23720
+ }
23721
+ const isEffectiveSupport = isSupport !== void 0 ? isSupport : isSupportUser();
23722
+ if (requireSupport && !isEffectiveSupport) {
23723
+ return null;
23724
+ }
23725
+ if (hideWhenOpen && isOpen) {
23726
+ return null;
23727
+ }
23728
+ const effectiveTooltip = tooltipTitle !== void 0 ? tooltipTitle : contextHint ? `Ask AI (${contextHint})` : "Ask AI \u2022 Context-aware assistant";
23729
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
23730
+ import_material31.Box,
23731
+ {
23732
+ className: themeScope?.scopeClassName,
23733
+ sx: {
23734
+ position: "fixed",
23735
+ bottom: "calc(24px + env(safe-area-inset-bottom, 0px))",
23736
+ right: "calc(24px + env(safe-area-inset-right, 0px))",
23737
+ zIndex: 1250
23738
+ },
23739
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_material31.Tooltip, { title: effectiveTooltip, arrow: true, placement: "top-end", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
23740
+ import_material31.ButtonBase,
23741
+ {
23742
+ onClick,
23743
+ focusRipple: true,
23744
+ "aria-label": `${label}${tagLabel ? ` (${tagLabel})` : ""}`,
23745
+ sx: [
23746
+ {
23747
+ display: "inline-flex",
23748
+ alignItems: "center",
23749
+ gap: 1.25,
23750
+ px: 2.25,
23751
+ py: 1.25,
23752
+ borderRadius: "9999px",
23753
+ bgcolor: "hsl(var(--card))",
23754
+ color: "hsl(var(--foreground))",
23755
+ border: "1px solid hsl(var(--border))",
23756
+ boxShadow: "0 4px 20px -2px rgba(0, 0, 0, 0.35), 0 2px 6px -1px rgba(0, 0, 0, 0.16)",
23757
+ backdropFilter: "blur(10px)",
23758
+ cursor: "pointer",
23759
+ userSelect: "none",
23760
+ transition: "transform 160ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 160ms ease, border-color 160ms ease, background-color 160ms ease",
23761
+ "&:hover": {
23762
+ transform: "translateY(-2px)",
23763
+ bgcolor: "hsl(var(--card) / 0.95)",
23764
+ borderColor: "hsla(var(--primary) / 0.45)",
23765
+ boxShadow: "0 8px 26px -4px hsla(var(--primary) / 0.25), 0 4px 10px -2px rgba(0, 0, 0, 0.2)"
23766
+ },
23767
+ "&:active": {
23768
+ transform: "translateY(0)",
23769
+ boxShadow: "0 2px 10px -2px rgba(0, 0, 0, 0.25)"
23770
+ }
23771
+ },
23772
+ ...Array.isArray(sx) ? sx : sx ? [sx] : []
23773
+ ],
23774
+ children: [
23775
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
23776
+ import_material31.Box,
23777
+ {
23778
+ sx: {
23779
+ display: "flex",
23780
+ alignItems: "center",
23781
+ justifyContent: "center",
23782
+ width: 24,
23783
+ height: 24,
23784
+ borderRadius: "50%",
23785
+ bgcolor: "hsla(var(--primary) / 0.14)",
23786
+ color: "hsl(var(--primary))",
23787
+ flexShrink: 0
23788
+ },
23789
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(AgentIcon_default, { size: 16 })
23790
+ }
23791
+ ),
23792
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
23793
+ import_material31.Typography,
23794
+ {
23795
+ sx: {
23796
+ fontSize: "0.86rem",
23797
+ fontWeight: 600,
23798
+ letterSpacing: "-0.01em",
23799
+ lineHeight: 1,
23800
+ color: "inherit"
23801
+ },
23802
+ children: label
23803
+ }
23804
+ ),
23805
+ tagLabel && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
23806
+ import_material31.Box,
23807
+ {
23808
+ component: "span",
23809
+ sx: {
23810
+ fontSize: "0.64rem",
23811
+ fontWeight: 700,
23812
+ textTransform: "uppercase",
23813
+ letterSpacing: "0.04em",
23814
+ color: "hsl(var(--primary))",
23815
+ bgcolor: "hsla(var(--primary) / 0.12)",
23816
+ border: "1px solid hsla(var(--primary) / 0.26)",
23817
+ px: 0.9,
23818
+ py: 0.25,
23819
+ borderRadius: "9999px",
23820
+ lineHeight: 1.1,
23821
+ flexShrink: 0
23822
+ },
23823
+ children: tagLabel
23824
+ }
23825
+ )
23826
+ ]
23827
+ }
23828
+ ) })
22339
23829
  }
22340
- setOpen(false);
22341
- };
22342
- const MAX_LABEL_CHARS = 15;
22343
- const displayLabel = label.length > MAX_LABEL_CHARS ? `${label.slice(0, MAX_LABEL_CHARS - 1).trimEnd()}\u2026` : label;
22344
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
22345
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_material31.Tooltip, { title: readOnly ? `${label} \u2014 preset handled by the backend` : "Click to edit the default prompt sent with your message", placement: "top", arrow: true, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
22346
- import_material31.Box,
23830
+ );
23831
+ };
23832
+ var AskAiButton_default = AskAiButton;
23833
+
23834
+ // components/AskAiDrawer.tsx
23835
+ var import_react31 = require("react");
23836
+ var import_material32 = require("@mui/material");
23837
+ var import_jsx_runtime37 = require("react/jsx-runtime");
23838
+ var AskAiDrawer = ({
23839
+ open,
23840
+ onClose,
23841
+ title,
23842
+ subtitle,
23843
+ pathname: propPathname,
23844
+ search: propSearch,
23845
+ rules,
23846
+ onContextResolved,
23847
+ hideResetAction = true,
23848
+ agentUIProps,
23849
+ ...drawerProps
23850
+ }) => {
23851
+ const [internalPathname, setInternalPathname] = (0, import_react31.useState)(
23852
+ () => typeof window !== "undefined" ? window.location.pathname : "/"
23853
+ );
23854
+ const [internalSearch, setInternalSearch] = (0, import_react31.useState)(
23855
+ () => typeof window !== "undefined" ? window.location.search : ""
23856
+ );
23857
+ (0, import_react31.useEffect)(() => {
23858
+ if (propPathname !== void 0) return;
23859
+ const updateLoc = () => {
23860
+ setInternalPathname(window.location.pathname);
23861
+ setInternalSearch(window.location.search);
23862
+ };
23863
+ window.addEventListener("popstate", updateLoc);
23864
+ return () => window.removeEventListener("popstate", updateLoc);
23865
+ }, [propPathname]);
23866
+ const activePathname = propPathname !== void 0 ? propPathname : internalPathname;
23867
+ const activeSearch = propSearch !== void 0 ? propSearch : internalSearch;
23868
+ const [resetKey, setResetKey] = (0, import_react31.useState)(0);
23869
+ const resolvedContext = (0, import_react31.useMemo)(
23870
+ () => resolveAgentContext(activePathname, activeSearch, rules),
23871
+ // eslint-disable-next-line react-hooks/exhaustive-deps
23872
+ [activePathname, activeSearch, rules, resetKey]
23873
+ );
23874
+ (0, import_react31.useEffect)(() => {
23875
+ onContextResolved?.(resolvedContext);
23876
+ }, [resolvedContext, onContextResolved]);
23877
+ const [isOverridden, setIsOverridden] = (0, import_react31.useState)(resolvedContext.isOverridden);
23878
+ (0, import_react31.useEffect)(() => {
23879
+ setIsOverridden(resolvedContext.isOverridden);
23880
+ }, [resolvedContext.isOverridden, resolvedContext.storageKey]);
23881
+ const handleAppsChange = (0, import_react31.useCallback)(
23882
+ (apps) => {
23883
+ setPageContextChoice(resolvedContext.storageKey, {
23884
+ apps: apps.map((a) => ({ name: a.name, id: a.id, icon: a.icon }))
23885
+ });
23886
+ setIsOverridden(true);
23887
+ },
23888
+ [resolvedContext.storageKey]
23889
+ );
23890
+ const handleSelectPreset = (0, import_react31.useCallback)(
23891
+ (preset) => {
23892
+ setPageContextChoice(resolvedContext.storageKey, {
23893
+ presetId: preset.id
23894
+ });
23895
+ setIsOverridden(true);
23896
+ },
23897
+ [resolvedContext.storageKey]
23898
+ );
23899
+ const handleResetToDefaults = (0, import_react31.useCallback)(() => {
23900
+ clearPageContextChoice(resolvedContext.storageKey);
23901
+ setIsOverridden(false);
23902
+ setResetKey((k) => k + 1);
23903
+ }, [resolvedContext.storageKey]);
23904
+ const effectiveTitle = title !== void 0 ? title : resolvedContext.title;
23905
+ const effectiveSubtitle = subtitle !== void 0 ? subtitle : /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_material32.Box, { sx: { display: "flex", alignItems: "center", gap: 1, flexWrap: "wrap", mt: 0.25 }, children: [
23906
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_material32.Typography, { component: "span", sx: { fontSize: "0.78rem", color: "hsl(var(--muted-foreground))" }, children: resolvedContext.subtitle }),
23907
+ isOverridden && !hideResetAction && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
23908
+ import_material32.ButtonBase,
22347
23909
  {
23910
+ onClick: handleResetToDefaults,
22348
23911
  sx: {
22349
- display: "inline-flex",
22350
- alignItems: "center",
22351
- gap: 0.25,
22352
- px: 0.5,
22353
- borderRadius: 0.5,
23912
+ fontSize: "0.68rem",
23913
+ fontWeight: 600,
23914
+ color: "hsl(var(--primary))",
23915
+ textDecoration: "underline",
23916
+ cursor: "pointer",
23917
+ py: 0.1,
23918
+ px: 0.4,
23919
+ borderRadius: 0.5,
23920
+ transition: "opacity 120ms ease",
23921
+ "&:hover": { opacity: 0.75 }
23922
+ },
23923
+ children: "Reset to defaults"
23924
+ }
23925
+ )
23926
+ ] });
23927
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
23928
+ AgentRunDrawer_default,
23929
+ {
23930
+ ...drawerProps,
23931
+ open,
23932
+ onClose,
23933
+ title: effectiveTitle,
23934
+ subtitle: effectiveSubtitle,
23935
+ agentUIProps: {
23936
+ ...agentUIProps,
23937
+ key: `ask_ai_${resolvedContext.storageKey}_${resetKey}`,
23938
+ defaultApps: resolvedContext.apps,
23939
+ initialPresetId: resolvedContext.presetId,
23940
+ defaultInput: agentUIProps?.defaultInput ?? resolvedContext.defaultPrompt,
23941
+ placeholder: agentUIProps?.placeholder ?? resolvedContext.placeholder,
23942
+ contextCategory: resolvedContext.sourceCategory,
23943
+ contextStorageKey: resolvedContext.storageKey,
23944
+ onAppsChange: (apps) => {
23945
+ handleAppsChange(apps);
23946
+ agentUIProps?.onAppsChange?.(apps);
23947
+ },
23948
+ onSelectPreset: (preset) => {
23949
+ handleSelectPreset(preset);
23950
+ agentUIProps?.onSelectPreset?.(preset);
23951
+ }
23952
+ }
23953
+ }
23954
+ );
23955
+ };
23956
+ var AskAiDrawer_default = AskAiDrawer;
23957
+
23958
+ // components/AskAiSidePanel.tsx
23959
+ var import_react32 = __toESM(require("react"));
23960
+ var import_material33 = require("@mui/material");
23961
+ var import_lucide_react21 = require("lucide-react");
23962
+ var import_jsx_runtime38 = require("react/jsx-runtime");
23963
+ var AGENT_DRAWER_OPEN_EVENT = "agent-drawer-open";
23964
+ var AskAiSidePanel = ({
23965
+ open,
23966
+ onClose,
23967
+ initialTab = "run",
23968
+ activeTab: propActiveTab,
23969
+ onTabChange,
23970
+ permissionsSlot,
23971
+ localLLMSlot,
23972
+ localLLMTabBadge,
23973
+ permissionsDisabled = false,
23974
+ permissionsDisabledTooltip,
23975
+ pathname,
23976
+ search,
23977
+ rules,
23978
+ isSupport,
23979
+ width = 380,
23980
+ theme,
23981
+ globalUrl,
23982
+ agentUIProps,
23983
+ onContextResolved,
23984
+ sideshift,
23985
+ userdata,
23986
+ isLoaded,
23987
+ isLoggedIn,
23988
+ serverside,
23989
+ colorMode,
23990
+ sx
23991
+ }) => {
23992
+ const themeScope = useShuffleMcpTheme();
23993
+ const effectiveTheme = theme || (themeScope?.isDark ? "dark" : "light");
23994
+ const [currentPathname, setCurrentPathname] = (0, import_react32.useState)(() => {
23995
+ if (pathname !== void 0) return pathname;
23996
+ if (typeof window !== "undefined") return window.location.pathname;
23997
+ return "/";
23998
+ });
23999
+ const [currentSearch, setCurrentSearch] = (0, import_react32.useState)(() => {
24000
+ if (search !== void 0) return search;
24001
+ if (typeof window !== "undefined") return window.location.search;
24002
+ return "";
24003
+ });
24004
+ (0, import_react32.useEffect)(() => {
24005
+ if (pathname !== void 0) {
24006
+ setCurrentPathname(pathname);
24007
+ return;
24008
+ }
24009
+ if (typeof window !== "undefined") {
24010
+ const handleLocationChange = () => {
24011
+ setCurrentPathname(window.location.pathname);
24012
+ setCurrentSearch(window.location.search);
24013
+ };
24014
+ window.addEventListener("popstate", handleLocationChange);
24015
+ return () => window.removeEventListener("popstate", handleLocationChange);
24016
+ }
24017
+ return;
24018
+ }, [pathname]);
24019
+ (0, import_react32.useEffect)(() => {
24020
+ if (search !== void 0) {
24021
+ setCurrentSearch(search);
24022
+ }
24023
+ }, [search]);
24024
+ const isAgentDisabled = isAgentRoute(currentPathname);
24025
+ (0, import_react32.useEffect)(() => {
24026
+ if (isAgentDisabled && open) {
24027
+ onClose();
24028
+ }
24029
+ }, [isAgentDisabled, open, onClose]);
24030
+ const context = import_react32.default.useMemo(
24031
+ () => resolveAgentContext(currentPathname, currentSearch, rules),
24032
+ [currentPathname, currentSearch, rules]
24033
+ );
24034
+ (0, import_react32.useEffect)(() => {
24035
+ onContextResolved?.(context);
24036
+ }, [context, onContextResolved]);
24037
+ useSyncHostBaseUrl(globalUrl);
24038
+ const [internalTab, setInternalTab] = (0, import_react32.useState)(initialTab);
24039
+ const currentTab = propActiveTab !== void 0 ? propActiveTab : internalTab;
24040
+ const handleTabChange = (0, import_react32.useCallback)(
24041
+ (nextTab) => {
24042
+ setInternalTab(nextTab);
24043
+ onTabChange?.(nextTab);
24044
+ },
24045
+ [onTabChange]
24046
+ );
24047
+ (0, import_react32.useEffect)(() => {
24048
+ if (initialTab) {
24049
+ setInternalTab(initialTab);
24050
+ }
24051
+ }, [initialTab]);
24052
+ const prevOpenRef = (0, import_react32.useRef)(open);
24053
+ (0, import_react32.useEffect)(() => {
24054
+ if (open && !prevOpenRef.current && initialTab) {
24055
+ setInternalTab(initialTab);
24056
+ onTabChange?.(initialTab);
24057
+ }
24058
+ prevOpenRef.current = open;
24059
+ }, [open, initialTab, onTabChange]);
24060
+ (0, import_react32.useEffect)(() => {
24061
+ if (typeof window === "undefined") return;
24062
+ window.__shuffleAgentDrawerMounted = (window.__shuffleAgentDrawerMounted || 0) + 1;
24063
+ return () => {
24064
+ window.__shuffleAgentDrawerMounted = Math.max(
24065
+ 0,
24066
+ (window.__shuffleAgentDrawerMounted || 1) - 1
24067
+ );
24068
+ };
24069
+ }, []);
24070
+ (0, import_react32.useEffect)(() => {
24071
+ if (typeof window === "undefined") return;
24072
+ const onOpen = (event) => {
24073
+ const tab = event.detail?.tab;
24074
+ if (tab) {
24075
+ handleTabChange(tab);
24076
+ }
24077
+ };
24078
+ window.addEventListener(AGENT_DRAWER_OPEN_EVENT, onOpen);
24079
+ return () => {
24080
+ window.removeEventListener(AGENT_DRAWER_OPEN_EVENT, onOpen);
24081
+ };
24082
+ }, [handleTabChange]);
24083
+ const effectiveLocalLLMSlot = localLLMSlot ?? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
24084
+ LocalLLMConfig_default,
24085
+ {
24086
+ open,
24087
+ globalUrl,
24088
+ userdata,
24089
+ isLoaded,
24090
+ isLoggedIn,
24091
+ serverside,
24092
+ theme: effectiveTheme,
24093
+ colorMode
24094
+ }
24095
+ );
24096
+ const TAB_ORDER2 = ["run", "localLLM"];
24097
+ const visibleTabs = TAB_ORDER2.filter((t) => {
24098
+ if (t === "run") return true;
24099
+ if (t === "localLLM") return !!effectiveLocalLLMSlot;
24100
+ return false;
24101
+ });
24102
+ const safeActiveTab = visibleTabs.includes(currentTab) ? currentTab : "run";
24103
+ const effectiveWidth = safeActiveTab === "localLLM" ? Math.max(width, 520) : width;
24104
+ const effectiveSideshift = sideshift !== void 0 ? sideshift : context.sideshift ?? true;
24105
+ (0, import_react32.useEffect)(() => {
24106
+ if (typeof document === "undefined") return;
24107
+ if (open && !isAgentDisabled && effectiveSideshift) {
24108
+ document.documentElement.style.setProperty(
24109
+ "--ask-ai-panel-width",
24110
+ `${effectiveWidth}px`
24111
+ );
24112
+ } else {
24113
+ document.documentElement.style.setProperty("--ask-ai-panel-width", "0px");
24114
+ }
24115
+ return () => {
24116
+ document.documentElement.style.setProperty("--ask-ai-panel-width", "0px");
24117
+ };
24118
+ }, [open, isAgentDisabled, effectiveWidth, effectiveSideshift]);
24119
+ const handleAppsChange = (0, import_react32.useCallback)(
24120
+ (nextApps) => {
24121
+ setPageContextChoice(context.storageKey, {
24122
+ apps: nextApps.map((a) => ({ name: a.name, id: a.id, icon: a.icon }))
24123
+ });
24124
+ agentUIProps?.onAppsChange?.(nextApps);
24125
+ },
24126
+ [context.storageKey, agentUIProps]
24127
+ );
24128
+ const handleSelectPreset = (0, import_react32.useCallback)(
24129
+ (preset) => {
24130
+ setPageContextChoice(context.storageKey, {
24131
+ presetId: preset?.id ?? null
24132
+ });
24133
+ },
24134
+ [context.storageKey]
24135
+ );
24136
+ if (isAgentDisabled) {
24137
+ return null;
24138
+ }
24139
+ const isVisible = open && !isAgentDisabled;
24140
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
24141
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
24142
+ import_material33.Box,
24143
+ {
24144
+ onClick: onClose,
24145
+ "aria-hidden": "true",
24146
+ sx: {
24147
+ position: "fixed",
24148
+ inset: 0,
24149
+ bgcolor: "rgba(0, 0, 0, 0.45)",
24150
+ zIndex: 1199,
24151
+ display: { xs: isVisible ? "block" : "none", sm: "none" },
24152
+ backdropFilter: "blur(2px)",
24153
+ transition: "opacity 0.2s ease"
24154
+ }
24155
+ }
24156
+ ),
24157
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
24158
+ import_material33.Box,
24159
+ {
24160
+ component: "aside",
24161
+ "aria-label": "Ask AI",
24162
+ "aria-hidden": !isVisible,
24163
+ className: themeScope?.scopeClassName,
24164
+ sx: [
24165
+ {
24166
+ position: "fixed",
24167
+ top: 0,
24168
+ right: 0,
24169
+ bottom: 0,
24170
+ width: { xs: "100%", sm: effectiveWidth },
24171
+ maxWidth: "100vw",
24172
+ height: "100dvh",
24173
+ bgcolor: "hsl(var(--card))",
24174
+ borderLeft: "1px solid hsl(var(--border))",
24175
+ boxShadow: isVisible ? "-6px 0 24px rgba(0, 0, 0, 0.16)" : "none",
24176
+ zIndex: 1200,
24177
+ display: "flex",
24178
+ flexDirection: "column",
24179
+ transform: isVisible ? "translateX(0)" : "translateX(100%)",
24180
+ transition: "transform 0.22s cubic-bezier(0.16, 1, 0.3, 1), width 0.2s ease",
24181
+ pointerEvents: isVisible ? "auto" : "none",
24182
+ visibility: isVisible ? "visible" : "hidden",
24183
+ boxSizing: "border-box",
24184
+ overflow: "hidden"
24185
+ },
24186
+ ...Array.isArray(sx) ? sx : [sx]
24187
+ ],
24188
+ children: [
24189
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
24190
+ import_material33.Box,
24191
+ {
24192
+ sx: {
24193
+ display: "flex",
24194
+ alignItems: "center",
24195
+ justifyContent: "space-between",
24196
+ px: 2,
24197
+ pt: 1.5,
24198
+ pb: 1,
24199
+ borderBottom: "1px solid hsl(var(--border) / 0.6)",
24200
+ flexShrink: 0
24201
+ },
24202
+ children: [
24203
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_material33.Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
24204
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
24205
+ import_material33.Box,
24206
+ {
24207
+ sx: {
24208
+ width: 24,
24209
+ height: 24,
24210
+ borderRadius: "6px",
24211
+ bgcolor: "hsl(var(--primary) / 0.12)",
24212
+ border: "1px solid hsl(var(--primary) / 0.3)",
24213
+ display: "flex",
24214
+ alignItems: "center",
24215
+ justifyContent: "center",
24216
+ color: "hsl(var(--primary))",
24217
+ flexShrink: 0
24218
+ },
24219
+ children: safeActiveTab === "permissions" ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_lucide_react21.ShieldCheck, { size: 14 }) : safeActiveTab === "localLLM" ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_lucide_react21.Server, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(AgentIcon_default, { size: 14 })
24220
+ }
24221
+ ),
24222
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
24223
+ import_material33.Typography,
24224
+ {
24225
+ sx: {
24226
+ fontWeight: 700,
24227
+ fontSize: "0.86rem",
24228
+ color: "hsl(var(--foreground))",
24229
+ letterSpacing: "-0.01em"
24230
+ },
24231
+ children: safeActiveTab === "permissions" ? "Agent Permissions" : safeActiveTab === "localLLM" ? "Local LLM Settings" : "Ask AI"
24232
+ }
24233
+ ),
24234
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
24235
+ import_material33.Box,
24236
+ {
24237
+ sx: {
24238
+ fontSize: "0.66rem",
24239
+ fontWeight: 600,
24240
+ textTransform: "uppercase",
24241
+ letterSpacing: "0.04em",
24242
+ px: 0.75,
24243
+ py: 0.2,
24244
+ borderRadius: "4px",
24245
+ bgcolor: "hsl(var(--muted))",
24246
+ color: "hsl(var(--muted-foreground))",
24247
+ border: "1px solid hsl(var(--border))"
24248
+ },
24249
+ children: "Support only"
24250
+ }
24251
+ )
24252
+ ] }),
24253
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_material33.Box, { sx: { display: "flex", alignItems: "center", gap: 0.5 }, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
24254
+ import_material33.IconButton,
24255
+ {
24256
+ onClick: onClose,
24257
+ size: "small",
24258
+ "aria-label": "Close Ask AI panel",
24259
+ sx: {
24260
+ color: "hsl(var(--muted-foreground))",
24261
+ p: 0.75,
24262
+ borderRadius: "8px",
24263
+ "&:hover": {
24264
+ color: "hsl(var(--foreground))",
24265
+ bgcolor: "hsl(var(--muted))"
24266
+ }
24267
+ },
24268
+ children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_lucide_react21.X, { size: 16 })
24269
+ }
24270
+ ) })
24271
+ ]
24272
+ }
24273
+ ),
24274
+ safeActiveTab === "localLLM" && effectiveLocalLLMSlot && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
24275
+ import_material33.Box,
24276
+ {
24277
+ sx: {
24278
+ flex: 1,
24279
+ display: "flex",
24280
+ flexDirection: "column",
24281
+ minHeight: 0,
24282
+ overflow: "hidden"
24283
+ },
24284
+ children: [
24285
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
24286
+ import_material33.Box,
24287
+ {
24288
+ sx: {
24289
+ display: "flex",
24290
+ alignItems: "center",
24291
+ justifyContent: "space-between",
24292
+ px: 2,
24293
+ py: 1.25,
24294
+ borderBottom: "1px solid hsl(var(--border) / 0.6)",
24295
+ bgcolor: "hsl(var(--card))",
24296
+ flexShrink: 0
24297
+ },
24298
+ children: [
24299
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
24300
+ import_material33.ButtonBase,
24301
+ {
24302
+ onClick: () => handleTabChange("run"),
24303
+ sx: {
24304
+ display: "inline-flex",
24305
+ alignItems: "center",
24306
+ gap: 0.75,
24307
+ fontSize: "0.82rem",
24308
+ fontWeight: 600,
24309
+ color: "hsl(var(--primary))",
24310
+ py: 0.5,
24311
+ px: 1,
24312
+ borderRadius: 1,
24313
+ transition: "background-color 120ms ease",
24314
+ "&:hover": {
24315
+ bgcolor: "hsl(var(--muted))"
24316
+ }
24317
+ },
24318
+ children: [
24319
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_lucide_react21.ArrowLeft, { size: 14 }),
24320
+ "Back to chat"
24321
+ ]
24322
+ }
24323
+ ),
24324
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
24325
+ import_material33.Box,
24326
+ {
24327
+ sx: {
24328
+ display: "flex",
24329
+ alignItems: "center",
24330
+ gap: 0.5,
24331
+ fontSize: "0.78rem",
24332
+ color: "hsl(var(--muted-foreground))",
24333
+ fontWeight: 500
24334
+ },
24335
+ children: [
24336
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_lucide_react21.Server, { size: 13 }),
24337
+ "Local LLM",
24338
+ localLLMTabBadge
24339
+ ]
24340
+ }
24341
+ )
24342
+ ]
24343
+ }
24344
+ ),
24345
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
24346
+ import_material33.Box,
24347
+ {
24348
+ sx: {
24349
+ flex: 1,
24350
+ overflowY: "auto",
24351
+ overflowX: "hidden",
24352
+ p: 2,
24353
+ minHeight: 0
24354
+ },
24355
+ children: effectiveLocalLLMSlot
24356
+ }
24357
+ )
24358
+ ]
24359
+ }
24360
+ ),
24361
+ safeActiveTab === "run" && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
24362
+ context.missingConfig && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
24363
+ import_material33.Box,
24364
+ {
24365
+ sx: {
24366
+ mx: 2,
24367
+ mt: 1.5,
24368
+ mb: 0.5,
24369
+ p: 1.25,
24370
+ borderRadius: 1.5,
24371
+ bgcolor: "hsl(var(--warning) / 0.1)",
24372
+ border: "1px solid hsl(var(--warning) / 0.3)",
24373
+ color: "hsl(var(--warning))",
24374
+ display: "flex",
24375
+ flexDirection: "column",
24376
+ gap: 0.5,
24377
+ flexShrink: 0
24378
+ },
24379
+ children: [
24380
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
24381
+ import_material33.Box,
24382
+ {
24383
+ sx: {
24384
+ display: "flex",
24385
+ alignItems: "center",
24386
+ gap: 0.75,
24387
+ fontWeight: 600,
24388
+ fontSize: "0.78rem"
24389
+ },
24390
+ children: [
24391
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_lucide_react21.AlertTriangle, { size: 14 }),
24392
+ "Missing page configuration"
24393
+ ]
24394
+ }
24395
+ ),
24396
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
24397
+ import_material33.Typography,
24398
+ {
24399
+ sx: {
24400
+ fontSize: "0.72rem",
24401
+ color: "hsl(var(--muted-foreground))",
24402
+ lineHeight: 1.35
24403
+ },
24404
+ children: [
24405
+ "No specific MCP apps or skills mapped for",
24406
+ " ",
24407
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("code", { children: currentPathname }),
24408
+ ". Using default platform tools. Support users: add a route mapping in",
24409
+ " ",
24410
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("code", { children: "agentContextRegistry.ts" }),
24411
+ " or manually choose tools below."
24412
+ ]
24413
+ }
24414
+ )
24415
+ ]
24416
+ }
24417
+ ),
24418
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
24419
+ import_material33.Box,
24420
+ {
24421
+ sx: {
24422
+ flex: 1,
24423
+ overflowY: "auto",
24424
+ overflowX: "hidden",
24425
+ px: 1.5,
24426
+ pb: 2,
24427
+ minHeight: 0,
24428
+ display: "flex",
24429
+ flexDirection: "column"
24430
+ },
24431
+ children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
24432
+ AgentUI_default,
24433
+ {
24434
+ compact: true,
24435
+ mobileView: true,
24436
+ hideHeroIcon: true,
24437
+ title: context.title || "How can we help on this page?",
24438
+ subtitle: null,
24439
+ hideChooseLLM: false,
24440
+ disableSchedule: true,
24441
+ hideAttach: false,
24442
+ maxWidth: effectiveWidth - 32,
24443
+ defaultApps: context.apps,
24444
+ initialPresetId: context.presetId,
24445
+ placeholder: context.placeholder,
24446
+ contextCategory: context.sourceCategory,
24447
+ contextStorageKey: context.storageKey,
24448
+ onAppsChange: handleAppsChange,
24449
+ onSelectPreset: handleSelectPreset,
24450
+ onChooseLLM: () => handleTabChange("localLLM"),
24451
+ apiBaseUrl: globalUrl || agentUIProps?.apiBaseUrl,
24452
+ theme: effectiveTheme,
24453
+ ...agentUIProps,
24454
+ sx: {
24455
+ flex: 1,
24456
+ minHeight: "100%",
24457
+ display: "flex",
24458
+ flexDirection: "column",
24459
+ justifyContent: "center",
24460
+ pt: 0,
24461
+ pb: 2,
24462
+ ...agentUIProps?.sx ? Array.isArray(agentUIProps.sx) ? {} : agentUIProps.sx : {}
24463
+ }
24464
+ },
24465
+ context.storageKey
24466
+ )
24467
+ }
24468
+ )
24469
+ ] })
24470
+ ]
24471
+ }
24472
+ )
24473
+ ] });
24474
+ };
24475
+ var AskAiSidePanel_default = AskAiSidePanel;
24476
+
24477
+ // components/AskAiWidget.tsx
24478
+ var import_react33 = require("react");
24479
+ var import_jsx_runtime39 = require("react/jsx-runtime");
24480
+ var AGENT_DRAWER_OPEN_EVENT2 = "agent-drawer-open";
24481
+ var AskAiWidget = ({
24482
+ open: controlledOpen,
24483
+ onOpenChange,
24484
+ mode = "panel",
24485
+ isSupport,
24486
+ requireSupport = true,
24487
+ buttonProps,
24488
+ hideButton = false,
24489
+ pathname,
24490
+ search,
24491
+ rules,
24492
+ initialTab: propInitialTab = "run",
24493
+ onContextResolved,
24494
+ permissionsSlot,
24495
+ localLLMSlot,
24496
+ ...panelProps
24497
+ }) => {
24498
+ const [internalOpen, setInternalOpen] = (0, import_react33.useState)(false);
24499
+ const [activeTab, setActiveTab] = (0, import_react33.useState)(propInitialTab);
24500
+ const isDrawerOpen = controlledOpen !== void 0 ? controlledOpen : internalOpen;
24501
+ const currentPath = pathname !== void 0 ? pathname : typeof window !== "undefined" ? window.location.pathname : "";
24502
+ const isAgentDisabled = isAgentRoute(currentPath);
24503
+ const setDrawerOpen = (0, import_react33.useCallback)(
24504
+ (nextOpen) => {
24505
+ if (isAgentDisabled && nextOpen) return;
24506
+ if (controlledOpen === void 0) {
24507
+ setInternalOpen(nextOpen);
24508
+ }
24509
+ onOpenChange?.(nextOpen);
24510
+ },
24511
+ [controlledOpen, isAgentDisabled, onOpenChange]
24512
+ );
24513
+ (0, import_react33.useEffect)(() => {
24514
+ if (isAgentDisabled && isDrawerOpen) {
24515
+ setDrawerOpen(false);
24516
+ }
24517
+ }, [isAgentDisabled, isDrawerOpen, setDrawerOpen]);
24518
+ const [currentContext, setCurrentContext] = (0, import_react33.useState)(null);
24519
+ const handleContextResolved = (0, import_react33.useCallback)(
24520
+ (ctx) => {
24521
+ setCurrentContext(ctx);
24522
+ onContextResolved?.(ctx);
24523
+ },
24524
+ [onContextResolved]
24525
+ );
24526
+ (0, import_react33.useEffect)(() => {
24527
+ const handleDrawerOpenEvent = (e) => {
24528
+ if (isAgentDisabled) return;
24529
+ const detail = e.detail;
24530
+ if (detail?.tab) {
24531
+ setActiveTab(detail.tab);
24532
+ }
24533
+ setDrawerOpen(true);
24534
+ };
24535
+ window.addEventListener(AGENT_DRAWER_OPEN_EVENT2, handleDrawerOpenEvent);
24536
+ return () => window.removeEventListener(AGENT_DRAWER_OPEN_EVENT2, handleDrawerOpenEvent);
24537
+ }, [isAgentDisabled, setDrawerOpen]);
24538
+ (0, import_react33.useEffect)(() => {
24539
+ if (propInitialTab) {
24540
+ setActiveTab(propInitialTab);
24541
+ }
24542
+ }, [propInitialTab]);
24543
+ const contextHint = currentContext?.apps && currentContext.apps.length > 0 ? currentContext.apps.map((a) => a.name.replace(/^shuffle_/, "").replace(/_/g, " ")).join(", ") : void 0;
24544
+ if (isAgentDisabled) {
24545
+ return null;
24546
+ }
24547
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
24548
+ !hideButton && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
24549
+ AskAiButton,
24550
+ {
24551
+ onClick: () => setDrawerOpen(!isDrawerOpen),
24552
+ isOpen: isDrawerOpen,
24553
+ isSupport,
24554
+ requireSupport,
24555
+ pathname,
24556
+ contextHint,
24557
+ ...buttonProps
24558
+ }
24559
+ ),
24560
+ mode === "drawer" ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
24561
+ AskAiDrawer,
24562
+ {
24563
+ open: isDrawerOpen,
24564
+ onClose: () => setDrawerOpen(false),
24565
+ initialTab: activeTab,
24566
+ pathname,
24567
+ search,
24568
+ rules,
24569
+ permissionsSlot,
24570
+ localLLMSlot,
24571
+ onContextResolved: handleContextResolved,
24572
+ ...panelProps
24573
+ }
24574
+ ) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
24575
+ AskAiSidePanel,
24576
+ {
24577
+ open: isDrawerOpen,
24578
+ onClose: () => setDrawerOpen(false),
24579
+ initialTab: activeTab,
24580
+ activeTab,
24581
+ onTabChange: setActiveTab,
24582
+ permissionsSlot,
24583
+ localLLMSlot,
24584
+ pathname,
24585
+ search,
24586
+ rules,
24587
+ isSupport,
24588
+ onContextResolved: handleContextResolved,
24589
+ ...panelProps
24590
+ }
24591
+ )
24592
+ ] });
24593
+ };
24594
+ var useContextAwareAgent = (pathname, search) => {
24595
+ const [activeContext, setActiveContext] = (0, import_react33.useState)(() => {
24596
+ const p = pathname ?? (typeof window !== "undefined" ? window.location.pathname : "/");
24597
+ const s = search ?? (typeof window !== "undefined" ? window.location.search : "");
24598
+ return resolveAgentContext(p, s);
24599
+ });
24600
+ (0, import_react33.useEffect)(() => {
24601
+ const p = pathname ?? (typeof window !== "undefined" ? window.location.pathname : "/");
24602
+ const s = search ?? (typeof window !== "undefined" ? window.location.search : "");
24603
+ setActiveContext(resolveAgentContext(p, s));
24604
+ }, [pathname, search]);
24605
+ return activeContext;
24606
+ };
24607
+ var AskAiWidget_default = AskAiWidget;
24608
+
24609
+ // components/AgentPromptPrefixChip.tsx
24610
+ var import_react34 = require("react");
24611
+ var import_material34 = require("@mui/material");
24612
+ var import_lucide_react22 = require("lucide-react");
24613
+ var import_jsx_runtime40 = require("react/jsx-runtime");
24614
+ var AgentPromptPrefixChip = ({
24615
+ label = "Shuffle Tools MCP",
24616
+ userId,
24617
+ value,
24618
+ onChange,
24619
+ onRemove,
24620
+ defaultPrompt,
24621
+ dialogTitle,
24622
+ readOnly
24623
+ }) => {
24624
+ const controlled = value !== void 0;
24625
+ const managed = useAgentPromptPrefix({ userId, defaultPrompt, persist: !controlled });
24626
+ const prompt = controlled ? value : managed.prompt;
24627
+ const isSaving = controlled ? false : managed.isSaving;
24628
+ const [open, setOpen] = (0, import_react34.useState)(false);
24629
+ const [draft, setDraft] = (0, import_react34.useState)(prompt);
24630
+ (0, import_react34.useEffect)(() => {
24631
+ if (!open) setDraft(prompt);
24632
+ }, [prompt, open]);
24633
+ const handleOpen = () => {
24634
+ if (readOnly) return;
24635
+ setDraft(prompt);
24636
+ setOpen(true);
24637
+ };
24638
+ const handleSave = async () => {
24639
+ if (controlled) {
24640
+ await onChange?.(draft);
24641
+ } else {
24642
+ await managed.savePrompt(draft);
24643
+ }
24644
+ setOpen(false);
24645
+ };
24646
+ const MAX_LABEL_CHARS = 15;
24647
+ const displayLabel = label.length > MAX_LABEL_CHARS ? `${label.slice(0, MAX_LABEL_CHARS - 1).trimEnd()}\u2026` : label;
24648
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
24649
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_material34.Tooltip, { title: readOnly ? `${label} \u2014 preset handled by the backend` : "Click to edit the default prompt sent with your message", placement: "top", arrow: true, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
24650
+ import_material34.Box,
24651
+ {
24652
+ sx: {
24653
+ display: "inline-flex",
24654
+ alignItems: "center",
24655
+ gap: 0.25,
24656
+ px: 0.5,
24657
+ borderRadius: 0.5,
22354
24658
  bgcolor: "transparent",
22355
24659
  flexShrink: 0,
22356
24660
  userSelect: "none",
@@ -22358,8 +24662,8 @@ var AgentPromptPrefixChip = ({
22358
24662
  "&:hover": readOnly ? void 0 : { bgcolor: "hsl(var(--muted))" }
22359
24663
  },
22360
24664
  children: [
22361
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
22362
- import_material31.Box,
24665
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
24666
+ import_material34.Box,
22363
24667
  {
22364
24668
  onClick: handleOpen,
22365
24669
  role: readOnly ? void 0 : "button",
@@ -22376,8 +24680,8 @@ var AgentPromptPrefixChip = ({
22376
24680
  cursor: readOnly ? "default" : "pointer"
22377
24681
  },
22378
24682
  "aria-label": readOnly ? `${label} preset selected` : `Edit default prompt for ${label}`,
22379
- children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
22380
- import_material31.Typography,
24683
+ children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
24684
+ import_material34.Typography,
22381
24685
  {
22382
24686
  component: "span",
22383
24687
  sx: {
@@ -22392,8 +24696,8 @@ var AgentPromptPrefixChip = ({
22392
24696
  )
22393
24697
  }
22394
24698
  ),
22395
- onRemove && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
22396
- import_material31.IconButton,
24699
+ onRemove && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
24700
+ import_material34.IconButton,
22397
24701
  {
22398
24702
  size: "small",
22399
24703
  onClick: (e) => {
@@ -22408,14 +24712,14 @@ var AgentPromptPrefixChip = ({
22408
24712
  "&:hover": { color: "hsl(var(--destructive))", bgcolor: "transparent" }
22409
24713
  },
22410
24714
  "aria-label": `Remove ${label} preset`,
22411
- children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_lucide_react21.X, { size: 12 })
24715
+ children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react22.X, { size: 12 })
22412
24716
  }
22413
24717
  )
22414
24718
  ]
22415
24719
  }
22416
24720
  ) }),
22417
- /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
22418
- import_material31.Dialog,
24721
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
24722
+ import_material34.Dialog,
22419
24723
  {
22420
24724
  open,
22421
24725
  onClose: () => setOpen(false),
@@ -22431,15 +24735,15 @@ var AgentPromptPrefixChip = ({
22431
24735
  }
22432
24736
  },
22433
24737
  children: [
22434
- /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_material31.DialogTitle, { sx: { display: "flex", alignItems: "center", justifyContent: "space-between", pr: 1.5, borderBottom: "1px solid hsl(var(--border))" }, children: [
22435
- /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_material31.Box, { children: [
22436
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_material31.Typography, { sx: { fontSize: "1rem", fontWeight: 600, color: "hsl(var(--foreground))" }, children: dialogTitle || `Edit default prompt \u2014 ${label}` }),
22437
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_material31.Typography, { sx: { fontSize: "0.75rem", color: "hsl(var(--muted-foreground))", mt: 0.25 }, children: isSaving ? "Saving\u2026" : "This text is prepended to every message you send from this prompt box. Saved to your user preferences." })
24738
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_material34.DialogTitle, { sx: { display: "flex", alignItems: "center", justifyContent: "space-between", pr: 1.5, borderBottom: "1px solid hsl(var(--border))" }, children: [
24739
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_material34.Box, { children: [
24740
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_material34.Typography, { sx: { fontSize: "1rem", fontWeight: 600, color: "hsl(var(--foreground))" }, children: dialogTitle || `Edit default prompt \u2014 ${label}` }),
24741
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_material34.Typography, { sx: { fontSize: "0.75rem", color: "hsl(var(--muted-foreground))", mt: 0.25 }, children: isSaving ? "Saving\u2026" : "This text is prepended to every message you send from this prompt box. Saved to your user preferences." })
22438
24742
  ] }),
22439
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_material31.IconButton, { size: "small", onClick: () => setOpen(false), sx: { color: "hsl(var(--muted-foreground))" }, "aria-label": "Close", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_lucide_react21.X, { size: 16 }) })
24743
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_material34.IconButton, { size: "small", onClick: () => setOpen(false), sx: { color: "hsl(var(--muted-foreground))" }, "aria-label": "Close", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react22.X, { size: 16 }) })
22440
24744
  ] }),
22441
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_material31.DialogContent, { sx: { pt: 2 }, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
22442
- import_material31.TextField,
24745
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_material34.DialogContent, { sx: { pt: 2 }, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
24746
+ import_material34.TextField,
22443
24747
  {
22444
24748
  autoFocus: true,
22445
24749
  fullWidth: true,
@@ -22460,10 +24764,10 @@ var AgentPromptPrefixChip = ({
22460
24764
  }
22461
24765
  }
22462
24766
  ) }),
22463
- /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_material31.DialogActions, { sx: { px: 3, py: 2, borderTop: "1px solid hsl(var(--border))" }, children: [
22464
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_material31.Button, { onClick: () => setOpen(false), sx: { textTransform: "none", color: "hsl(var(--muted-foreground))" }, children: "Cancel" }),
22465
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
22466
- import_material31.Button,
24767
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_material34.DialogActions, { sx: { px: 3, py: 2, borderTop: "1px solid hsl(var(--border))" }, children: [
24768
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_material34.Button, { onClick: () => setOpen(false), sx: { textTransform: "none", color: "hsl(var(--muted-foreground))" }, children: "Cancel" }),
24769
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
24770
+ import_material34.Button,
22467
24771
  {
22468
24772
  onClick: handleSave,
22469
24773
  variant: "contained",
@@ -22485,14 +24789,14 @@ var AgentPromptPrefixChip = ({
22485
24789
  };
22486
24790
 
22487
24791
  // useAppLookup.ts
22488
- var import_react32 = require("react");
24792
+ var import_react35 = require("react");
22489
24793
  init_api();
22490
24794
  init_appsCache();
22491
24795
  var norm3 = (s) => s.toLowerCase().replace(/[\s_\-]+/g, "_");
22492
24796
  function useAppLookup(appName) {
22493
- const [loading, setLoading] = (0, import_react32.useState)(false);
22494
- const [info, setInfo] = (0, import_react32.useState)(null);
22495
- const [algoliaId, setAlgoliaId] = (0, import_react32.useState)(null);
24797
+ const [loading, setLoading] = (0, import_react35.useState)(false);
24798
+ const [info, setInfo] = (0, import_react35.useState)(null);
24799
+ const [algoliaId, setAlgoliaId] = (0, import_react35.useState)(null);
22496
24800
  const {
22497
24801
  authStates,
22498
24802
  authenticatedApps,
@@ -22501,7 +24805,7 @@ function useAppLookup(appName) {
22501
24805
  handleSaveAuth,
22502
24806
  refreshAuth
22503
24807
  } = useAppAuth();
22504
- (0, import_react32.useEffect)(() => {
24808
+ (0, import_react35.useEffect)(() => {
22505
24809
  if (!appName) {
22506
24810
  setInfo(null);
22507
24811
  setAlgoliaId(null);
@@ -22572,13 +24876,13 @@ function useAppLookup(appName) {
22572
24876
  setLoading(false);
22573
24877
  })();
22574
24878
  }, [appName]);
22575
- const matchingEntries = (0, import_react32.useMemo)(() => {
24879
+ const matchingEntries = (0, import_react35.useMemo)(() => {
22576
24880
  if (!appName) return [];
22577
24881
  return authenticatedApps.filter(
22578
24882
  (auth) => auth.app?.name && norm3(auth.app.name) === norm3(appName)
22579
24883
  );
22580
24884
  }, [appName, authenticatedApps]);
22581
- const image = (0, import_react32.useMemo)(() => {
24885
+ const image = (0, import_react35.useMemo)(() => {
22582
24886
  if (info?.image) return info.image;
22583
24887
  for (const entry of matchingEntries) {
22584
24888
  const img = entry.app?.large_image || entry.large_image;
@@ -22586,7 +24890,7 @@ function useAppLookup(appName) {
22586
24890
  }
22587
24891
  return "";
22588
24892
  }, [info, matchingEntries]);
22589
- const algoliaApp = (0, import_react32.useMemo)(() => {
24893
+ const algoliaApp = (0, import_react35.useMemo)(() => {
22590
24894
  if (!appName) return null;
22591
24895
  return {
22592
24896
  objectID: algoliaId || appName,
@@ -22625,23 +24929,23 @@ var resolveMode = (theme, colorMode) => {
22625
24929
  if (theme === "light" || theme === "dark") return theme;
22626
24930
  if (theme === "system") return "auto";
22627
24931
  if (colorMode) return colorMode;
22628
- return "dark";
24932
+ return "auto";
22629
24933
  };
22630
24934
  var withMcpTheme = (Inner, displayName2) => {
22631
- const Wrapped = ({ theme, colorMode, ...rest }) => import_react33.default.createElement(
24935
+ const Wrapped = ({ theme, colorMode, ...rest }) => import_react36.default.createElement(
22632
24936
  ShuffleMcpThemeProvider,
22633
24937
  { mode: resolveMode(theme, colorMode) },
22634
- import_react33.default.createElement(Inner, { ...rest, theme, colorMode })
24938
+ import_react36.default.createElement(Inner, { ...rest, theme, colorMode })
22635
24939
  );
22636
24940
  Wrapped.displayName = `ShuffleMCPs(${displayName2})`;
22637
24941
  return Wrapped;
22638
24942
  };
22639
24943
  var withMcpThemeRef = (Inner, displayName2) => {
22640
- const Wrapped = import_react33.default.forwardRef(
22641
- ({ theme, colorMode, ...rest }, ref) => import_react33.default.createElement(
24944
+ const Wrapped = import_react36.default.forwardRef(
24945
+ ({ theme, colorMode, ...rest }, ref) => import_react36.default.createElement(
22642
24946
  ShuffleMcpThemeProvider,
22643
24947
  { mode: resolveMode(theme, colorMode) },
22644
- import_react33.default.createElement(Inner, { ...rest, theme, colorMode, ref })
24948
+ import_react36.default.createElement(Inner, { ...rest, theme, colorMode, ref })
22645
24949
  )
22646
24950
  );
22647
24951
  Wrapped.displayName = `ShuffleMCPs(${displayName2})`;
@@ -22659,6 +24963,10 @@ var TryMcpSection2 = withMcpTheme(TryMcpSection, "TryMcpSection");
22659
24963
  var SingulActionsPreview2 = withMcpTheme(SingulActionsPreview_default, "SingulActionsPreview");
22660
24964
  var AgentUI2 = withMcpTheme(AgentUI_default, "AgentUI");
22661
24965
  var AgentRunDrawer2 = withMcpTheme(AgentRunDrawer_default, "AgentRunDrawer");
24966
+ var AskAiButton2 = withMcpTheme(AskAiButton_default, "AskAiButton");
24967
+ var AskAiDrawer2 = withMcpTheme(AskAiDrawer_default, "AskAiDrawer");
24968
+ var AskAiSidePanel2 = withMcpTheme(AskAiSidePanel_default, "AskAiSidePanel");
24969
+ var AskAiWidget2 = withMcpTheme(AskAiWidget_default, "AskAiWidget");
22662
24970
  var AgentActivityList2 = withMcpTheme(AgentActivityList_default, "AgentActivityList");
22663
24971
  var AgentExecutionDrawer2 = withMcpTheme(AgentExecutionDrawer_default, "AgentExecutionDrawer");
22664
24972
  var AgentsView2 = withMcpTheme(AgentsView_default, "AgentsView");
@@ -22688,9 +24996,14 @@ var AddAppModal2 = withMcpTheme(AddAppModal_default, "AddAppModal");
22688
24996
  AppDetailProvider,
22689
24997
  AppSearchDrawer,
22690
24998
  AppTitleHeader,
24999
+ AskAiButton,
25000
+ AskAiDrawer,
25001
+ AskAiSidePanel,
25002
+ AskAiWidget,
22691
25003
  CASES_PATTERNS,
22692
25004
  COMMUNICATION_PATTERNS_NAMES,
22693
25005
  DATASTORE_CATEGORIES,
25006
+ DEFAULT_AGENT_CONTEXT_RULES,
22694
25007
  DEFAULT_AGENT_PROMPT_PREFIX,
22695
25008
  EDR_PATTERNS,
22696
25009
  EMAIL_APP_PATTERNS,
@@ -22712,6 +25025,7 @@ var AddAppModal2 = withMcpTheme(AddAppModal_default, "AddAppModal");
22712
25025
  VULN_SCANNER_PATTERNS,
22713
25026
  VideoEmbed,
22714
25027
  applyRegionFromPayload,
25028
+ clearPageContextChoice,
22715
25029
  deleteDatastoreItem,
22716
25030
  deleteDatastoreItems,
22717
25031
  diagnoseOutputWarning,
@@ -22723,6 +25037,8 @@ var AddAppModal2 = withMcpTheme(AddAppModal_default, "AddAppModal");
22723
25037
  fetchAppsViaApiConfig,
22724
25038
  findForwardTicketsWorkflow,
22725
25039
  findIngestTicketsWorkflow,
25040
+ getActivePageEntityName,
25041
+ getAgentContextRules,
22726
25042
  getApiUrl,
22727
25043
  getAuthHeader,
22728
25044
  getDatastoreByCategory,
@@ -22732,10 +25048,12 @@ var AddAppModal2 = withMcpTheme(AddAppModal_default, "AddAppModal");
22732
25048
  getFailureInfo,
22733
25049
  getHostBaseUrl,
22734
25050
  getIngestionCategory,
25051
+ getPageContextChoice,
22735
25052
  hasOutputWarning,
22736
25053
  installFetchBreaker,
22737
25054
  invalidateAppsCache,
22738
25055
  invalidateResolvedApps,
25056
+ isAgentRoute,
22739
25057
  isCloud,
22740
25058
  isCloudDomain,
22741
25059
  isEmailApp,
@@ -22744,11 +25062,14 @@ var AddAppModal2 = withMcpTheme(AddAppModal_default, "AddAppModal");
22744
25062
  isThreatIntelApp,
22745
25063
  isVulnScannerApp,
22746
25064
  isWorkflowScheduleStopped,
25065
+ matchRoutePattern,
22747
25066
  normalizeAppName,
22748
25067
  parseRunResult,
22749
25068
  refreshAllIntegrationStatus,
25069
+ registerAgentContextRule,
22750
25070
  registerProtectedOrigin,
22751
25071
  resetRegionUrl,
25072
+ resolveAgentContext,
22752
25073
  resolveApp,
22753
25074
  resolveApps,
22754
25075
  resolveVideoUrl,
@@ -22758,6 +25079,7 @@ var AddAppModal2 = withMcpTheme(AddAppModal_default, "AddAppModal");
22758
25079
  setDatastoreItem,
22759
25080
  setDatastoreItems,
22760
25081
  setHostBaseUrl,
25082
+ setPageContextChoice,
22761
25083
  setRegionUrl,
22762
25084
  setRuntimeOrgId,
22763
25085
  setToastImpl,
@@ -22769,5 +25091,6 @@ var AddAppModal2 = withMcpTheme(AddAppModal_default, "AddAppModal");
22769
25091
  useAppDetail,
22770
25092
  useAppDetailOptional,
22771
25093
  useAppLookup,
25094
+ useContextAwareAgent,
22772
25095
  useSyncHostBaseUrl
22773
25096
  });