@shuffleio/shuffle-mcps 0.0.31 → 0.0.32

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.mjs CHANGED
@@ -20,13 +20,13 @@ import {
20
20
  } from "./chunk-B4JFC2JU.mjs";
21
21
 
22
22
  // index.ts
23
- import "./shuffle-mcp-ER33P3W2.css";
23
+ import "./shuffle-mcp-TWJGPBOQ.css";
24
24
  import React5 from "react";
25
25
 
26
26
  // ShuffleMcpThemeProvider.tsx
27
27
  import React from "react";
28
28
  import { ThemeProvider, createTheme, useTheme as useMuiTheme } from "@mui/material";
29
- import "./shuffle-mcp-ER33P3W2.css";
29
+ import "./shuffle-mcp-TWJGPBOQ.css";
30
30
  import { jsx, jsxs } from "react/jsx-runtime";
31
31
  var readHtmlDarkClass = () => {
32
32
  if (typeof document === "undefined") return false;
@@ -879,6 +879,65 @@ var isIngestionApp = (appName) => {
879
879
  return isEmailApp(appName) || CASES_PATTERNS.some((p) => name.includes(p)) || EDR_PATTERNS.some((p) => name.includes(p)) || SIEM_PATTERNS.some((p) => name.includes(p));
880
880
  };
881
881
  var normalizeAppName = (name) => name.toLowerCase().trim().replace(/[\s_\-]+/g, "_");
882
+ var IGNORED_WORKFLOW_APP_NAMES = /* @__PURE__ */ new Set([
883
+ "shuffle_tools",
884
+ "singul",
885
+ "integration",
886
+ "ai_agent",
887
+ "shuffle_agent",
888
+ "shuffle_ai"
889
+ ]);
890
+ var isIgnoredWorkflowAppName = (name) => IGNORED_WORKFLOW_APP_NAMES.has(normalizeAppName(name));
891
+ var looksLikeOpaqueId = (raw) => {
892
+ const trimmed = raw.trim();
893
+ if (/^[a-f0-9]{16,}$/i.test(trimmed)) return true;
894
+ if (/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(trimmed)) return true;
895
+ return false;
896
+ };
897
+ var addWorkflowAppName = (names, seen, raw) => {
898
+ if (typeof raw !== "string" || !raw.trim()) return;
899
+ const parts = raw.includes(",") ? raw.split(",") : [raw];
900
+ for (const part of parts) {
901
+ const trimmed = part.trim();
902
+ if (!trimmed) continue;
903
+ const key = normalizeAppName(trimmed);
904
+ if (!key || isIgnoredWorkflowAppName(trimmed) || looksLikeOpaqueId(trimmed) || seen.has(key)) continue;
905
+ seen.add(key);
906
+ names.push(trimmed);
907
+ }
908
+ };
909
+ var collectParameterAppNames = (input, names, seen, depth = 0) => {
910
+ if (!input || depth > 4) return;
911
+ if (Array.isArray(input)) {
912
+ input.forEach((item) => collectParameterAppNames(item, names, seen, depth + 1));
913
+ return;
914
+ }
915
+ if (typeof input !== "object") return;
916
+ const obj = input;
917
+ if (String(obj.name || "").toLowerCase() === "app_name") addWorkflowAppName(names, seen, obj.value);
918
+ for (const [key, value] of Object.entries(obj)) {
919
+ const lower = key.toLowerCase();
920
+ if (lower === "app_name") addWorkflowAppName(names, seen, value);
921
+ else if (lower === "parameters" || lower === "fields" || lower === "value") collectParameterAppNames(value, names, seen, depth + 1);
922
+ }
923
+ };
924
+ function extractActionAppNames(action) {
925
+ const names = [];
926
+ const seen = /* @__PURE__ */ new Set();
927
+ collectParameterAppNames(action?.parameters, names, seen);
928
+ addWorkflowAppName(names, seen, action?.app_name);
929
+ addWorkflowAppName(names, seen, action?.app_id);
930
+ return names;
931
+ }
932
+ function extractWorkflowActionAppNames(workflow) {
933
+ const names = [];
934
+ const seen = /* @__PURE__ */ new Set();
935
+ if (!workflow?.actions || !Array.isArray(workflow.actions)) return names;
936
+ for (const action of workflow.actions) {
937
+ for (const name of extractActionAppNames(action)) addWorkflowAppName(names, seen, name);
938
+ }
939
+ return names;
940
+ }
882
941
  var getIngestionCategory = (appName, appCategories) => {
883
942
  const name = appName.toLowerCase();
884
943
  const categories = (appCategories || []).map((c) => c.toLowerCase());
@@ -892,20 +951,14 @@ var INGEST_TICKETS_WORKFLOW_NAME = "Ingest Tickets";
892
951
  var FORWARD_TICKETS_WORKFLOW_NAME = "Forward Tickets";
893
952
  function extractWorkflowAppNames(workflow) {
894
953
  const names = /* @__PURE__ */ new Set();
895
- if (!workflow?.actions || !Array.isArray(workflow.actions)) return names;
896
- for (const action of workflow.actions) {
897
- if (action.app_name) {
898
- names.add(normalizeAppName(action.app_name));
899
- }
900
- if (action.app_id) {
901
- names.add(normalizeAppName(action.app_id));
902
- }
903
- if (action.parameters) {
904
- for (const param of action.parameters) {
905
- if (param.name === "app_name" && param.value) {
906
- names.add(normalizeAppName(param.value));
907
- }
908
- }
954
+ for (const raw of extractWorkflowActionAppNames(workflow)) {
955
+ const parts = typeof raw === "string" && raw.includes(",") ? raw.split(",") : [raw];
956
+ for (const part of parts) {
957
+ const trimmed = (part || "").trim();
958
+ if (!trimmed) continue;
959
+ if (isIgnoredWorkflowAppName(trimmed) || looksLikeOpaqueId(trimmed)) continue;
960
+ const key = normalizeAppName(trimmed);
961
+ if (key) names.add(key);
909
962
  }
910
963
  }
911
964
  return names;
@@ -1619,6 +1672,7 @@ function AppTitleHeader({
1619
1672
  isActivated = null,
1620
1673
  activateLoading = false,
1621
1674
  onActivateToggle,
1675
+ highlightActivate = false,
1622
1676
  onAdd
1623
1677
  }) {
1624
1678
  return /* @__PURE__ */ jsx4(motion.div, { initial: { opacity: 0, y: 12 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.3 }, children: /* @__PURE__ */ jsxs3(
@@ -1722,7 +1776,16 @@ function AppTitleHeader({
1722
1776
  py: 0.5,
1723
1777
  minHeight: 0,
1724
1778
  flexShrink: 0,
1725
- ...isActivated ? { color: "hsl(var(--muted-foreground))", borderColor: "hsl(var(--border))", "&:hover": { borderColor: "hsl(var(--destructive))", color: "hsl(var(--destructive))", bgcolor: "hsla(var(--destructive) / 0.08)" } } : { bgcolor: "hsl(var(--primary))", "&:hover": { bgcolor: "hsl(var(--primary) / 0.85)" } }
1779
+ ...isActivated ? { color: "hsl(var(--muted-foreground))", borderColor: "hsl(var(--border))", "&:hover": { borderColor: "hsl(var(--destructive))", color: "hsl(var(--destructive))", bgcolor: "hsla(var(--destructive) / 0.08)" } } : { bgcolor: "hsl(var(--primary))", "&:hover": { bgcolor: "hsl(var(--primary) / 0.85)" } },
1780
+ ...highlightActivate ? {
1781
+ boxShadow: "0 0 0 0 hsla(var(--primary) / 0.6)",
1782
+ animation: "appTitleActivatePulse 1.1s ease-out infinite",
1783
+ "@keyframes appTitleActivatePulse": {
1784
+ "0%": { boxShadow: "0 0 0 0 hsla(var(--primary) / 0.55)" },
1785
+ "70%": { boxShadow: "0 0 0 10px hsla(var(--primary) / 0)" },
1786
+ "100%": { boxShadow: "0 0 0 0 hsla(var(--primary) / 0)" }
1787
+ }
1788
+ } : {}
1726
1789
  },
1727
1790
  children: activateLoading ? "\u2026" : isActivated ? "Deactivate" : "Activate"
1728
1791
  }
@@ -5320,6 +5383,7 @@ function AppDetailDrawer({
5320
5383
  onAddToCanvas,
5321
5384
  isAuthenticated = true,
5322
5385
  activeOrgId,
5386
+ autoActivate = false,
5323
5387
  globalUrl,
5324
5388
  userdata,
5325
5389
  isLoaded,
@@ -5517,8 +5581,9 @@ function AppDetailDrawer({
5517
5581
  useEffect7(() => {
5518
5582
  lastValidAuthRef.current = null;
5519
5583
  }, [appName]);
5520
- const handleActivateToggle = async () => {
5584
+ const handleActivateToggle = async (opts) => {
5521
5585
  if (!appName || activateLoading) return;
5586
+ const silent = !!opts?.silent;
5522
5587
  const wasActivated = isActivated;
5523
5588
  const prevAppId = activatedAppId;
5524
5589
  setIsActivated(!wasActivated);
@@ -5532,7 +5597,7 @@ function AppDetailDrawer({
5532
5597
  });
5533
5598
  if (!res.ok) throw new Error("Deactivate failed");
5534
5599
  setActivatedAppId(null);
5535
- toast.success(`${displayName} deactivated`);
5600
+ if (!silent) toast.success(`${displayName} deactivated`);
5536
5601
  } else {
5537
5602
  const appId2 = resolvedAlgoliaId;
5538
5603
  if (!appId2) throw new Error("App ID not resolved yet");
@@ -5543,17 +5608,38 @@ function AppDetailDrawer({
5543
5608
  });
5544
5609
  if (!activateRes.ok) throw new Error(`Activate failed (${activateRes.status})`);
5545
5610
  setActivatedAppId(appId2);
5546
- toast.success(`${displayName} activated`);
5611
+ if (!silent) toast.success(`${displayName} activated`);
5547
5612
  }
5548
5613
  } catch (err) {
5549
5614
  console.error("[Activate] Error:", err);
5550
- toast.error(err?.message || "Activation failed");
5615
+ if (!silent) toast.error(err?.message || "Activation failed");
5551
5616
  setIsActivated(wasActivated);
5552
5617
  setActivatedAppId(prevAppId);
5553
5618
  } finally {
5554
5619
  setActivateLoading(false);
5555
5620
  }
5556
5621
  };
5622
+ const autoActivateFiredRef = useRef5(null);
5623
+ const [autoActivatePulse, setAutoActivatePulse] = useState8(false);
5624
+ useEffect7(() => {
5625
+ if (!open || !autoActivate || !appName) return;
5626
+ const key = `${appName}`;
5627
+ if (autoActivateFiredRef.current === key) return;
5628
+ if (isActivated !== false) return;
5629
+ if (!resolvedAlgoliaId) return;
5630
+ if (activateLoading) return;
5631
+ autoActivateFiredRef.current = key;
5632
+ setAutoActivatePulse(true);
5633
+ handleActivateToggle({ silent: true }).finally(() => {
5634
+ setTimeout(() => setAutoActivatePulse(false), 1200);
5635
+ });
5636
+ }, [open, autoActivate, appName, isActivated, resolvedAlgoliaId, activateLoading]);
5637
+ useEffect7(() => {
5638
+ if (!open) {
5639
+ autoActivateFiredRef.current = null;
5640
+ setAutoActivatePulse(false);
5641
+ }
5642
+ }, [open, appName]);
5557
5643
  const isLoadingAll = appLoading || isAuthenticated && appAuthLoading;
5558
5644
  return /* @__PURE__ */ jsxs9(
5559
5645
  Drawer,
@@ -5629,7 +5715,8 @@ function AppDetailDrawer({
5629
5715
  categories: appInfo?.categories,
5630
5716
  isActivated: onAddToCanvas ? null : effectiveActivated,
5631
5717
  activateLoading,
5632
- onActivateToggle: handleActivateToggle,
5718
+ onActivateToggle: () => handleActivateToggle(),
5719
+ highlightActivate: autoActivatePulse,
5633
5720
  onAdd: onAddToCanvas && appName ? () => {
5634
5721
  onAddToCanvas({ name: appName, icon: resolvedImage || "", algoliaId: resolvedAlgoliaId });
5635
5722
  onClose();
@@ -5733,7 +5820,7 @@ function AppDetailDrawer({
5733
5820
  }
5734
5821
 
5735
5822
  // views/ShuffleMCP.tsx
5736
- import "./shuffle-mcp-ER33P3W2.css";
5823
+ import "./shuffle-mcp-TWJGPBOQ.css";
5737
5824
 
5738
5825
  // components/SegmentedControl.tsx
5739
5826
  import * as React2 from "react";
@@ -5900,7 +5987,7 @@ function SegmentedControl({
5900
5987
  children: opt.label
5901
5988
  }
5902
5989
  ),
5903
- !isAction && typeof opt.count === "number" && /* @__PURE__ */ jsx13(
5990
+ !isAction && (typeof opt.count === "number" || typeof opt.count === "string") && /* @__PURE__ */ jsx13(
5904
5991
  "span",
5905
5992
  {
5906
5993
  style: {
@@ -6399,8 +6486,8 @@ var ShuffleMCP = React3.forwardRef(({
6399
6486
  value: sourceFilter,
6400
6487
  onChange: (v) => setSourceFilter(v),
6401
6488
  options: [
6402
- { value: "all", label: "All", count: results.length + filteredPrivateApps.length, title: "All available apps \u2014 both the public catalog and your private apps." },
6403
- { value: "public", label: "Public", count: results.length, title: "Public apps from the Shuffle catalog (powered by Algolia)." },
6489
+ { value: "all", label: "All", count: results.length >= hitsPerPage ? `${results.length + filteredPrivateApps.length}+` : results.length + filteredPrivateApps.length, title: "All available apps \u2014 both the public catalog and your private apps." },
6490
+ { value: "public", label: "Public", count: results.length >= hitsPerPage ? `${results.length}+` : results.length, title: "Public apps from the Shuffle catalog (powered by Algolia)." },
6404
6491
  { value: "private", label: "Private", count: filteredPrivateApps.length, title: "Private apps are apps you have activated in your organization, or your own custom apps \u2014 not just from the public Algolia catalog." },
6405
6492
  { value: "authenticated", label: "Authenticated", count: [...filteredPrivateApps, ...results].filter((a, i, arr) => arr.findIndex((x) => x.name?.toLowerCase() === a.name?.toLowerCase()) === i).filter(isAppConfigured).length, title: "Apps you have authenticated and that are ready to use." }
6406
6493
  ]
@@ -6577,9 +6664,11 @@ function AppSearchDrawer({
6577
6664
  pinnedApps,
6578
6665
  highlightAppName,
6579
6666
  highlightDelayMs = 5e3,
6667
+ realtimeHighlightAppNames,
6580
6668
  multiSelect = false,
6581
6669
  selectedApps,
6582
6670
  onSelectionChange,
6671
+ autoActivate = false,
6583
6672
  globalUrl,
6584
6673
  theme,
6585
6674
  colorMode
@@ -6838,6 +6927,41 @@ function AppSearchDrawer({
6838
6927
  "0%, 100%": { boxShadow: "0 0 0 2px hsl(var(--primary)), 0 0 12px 1px hsl(var(--primary) / 0.4)" },
6839
6928
  "50%": { boxShadow: "0 0 0 2px hsl(var(--primary)), 0 0 24px 4px hsl(var(--primary) / 0.75)" }
6840
6929
  }
6930
+ } : {},
6931
+ ...realtimeHighlightAppNames && realtimeHighlightAppNames.length > 0 ? {
6932
+ [realtimeHighlightAppNames.map((n) => `& .singul-dropdown-item[data-app-name="${n}"]`).join(", ")]: {
6933
+ position: "relative",
6934
+ zIndex: 2,
6935
+ borderColor: "hsl(var(--primary) / 0.78) !important",
6936
+ backgroundColor: "hsl(var(--primary) / 0.08) !important",
6937
+ outline: "none !important",
6938
+ boxShadow: "none !important",
6939
+ animation: "shuffleHighlightPulseStrong 1.5s ease-in-out infinite",
6940
+ transition: "border-color 120ms ease, background-color 120ms ease",
6941
+ "&::before": {
6942
+ content: '""',
6943
+ position: "absolute",
6944
+ left: "10px",
6945
+ top: "14px",
6946
+ bottom: "14px",
6947
+ width: "3px",
6948
+ borderRadius: "999px",
6949
+ backgroundColor: "hsl(var(--primary))"
6950
+ },
6951
+ "& .singul-app-info": {
6952
+ paddingLeft: "8px"
6953
+ }
6954
+ },
6955
+ "@keyframes shuffleHighlightPulseStrong": {
6956
+ "0%, 100%": {
6957
+ borderColor: "hsl(var(--primary) / 0.62)",
6958
+ backgroundColor: "hsl(var(--primary) / 0.06)"
6959
+ },
6960
+ "50%": {
6961
+ borderColor: "hsl(var(--primary) / 0.92)",
6962
+ backgroundColor: "hsl(var(--primary) / 0.12)"
6963
+ }
6964
+ }
6841
6965
  } : {}
6842
6966
  },
6843
6967
  children: /* @__PURE__ */ jsx15(AnimatePresence2, { mode: "wait", children: /* @__PURE__ */ jsx15(
@@ -6860,8 +6984,9 @@ function AppSearchDrawer({
6860
6984
  layout: "grid",
6861
6985
  gridColumns: 2,
6862
6986
  inline: true,
6987
+ initialQuery,
6863
6988
  initialFilterQuery: initialQuery,
6864
- hitsPerPage: 12,
6989
+ hitsPerPage: 20,
6865
6990
  showDescription: false,
6866
6991
  showCategories: true,
6867
6992
  showCheckbox: multiSelect,
@@ -6952,7 +7077,8 @@ function AppSearchDrawer({
6952
7077
  theme,
6953
7078
  colorMode,
6954
7079
  onRefresh: onClose,
6955
- onAddToCanvas
7080
+ onAddToCanvas,
7081
+ autoActivate
6956
7082
  }
6957
7083
  )
6958
7084
  ] });
@@ -7219,7 +7345,7 @@ function ShufflePipelinesBanner() {
7219
7345
  var ShufflePipelinesBanner_default = ShufflePipelinesBanner;
7220
7346
 
7221
7347
  // components/AgentUI.tsx
7222
- import { useCallback as useCallback8, useEffect as useEffect11, useMemo as useMemo6, useRef as useRef8, useState as useState12 } from "react";
7348
+ import React4, { useCallback as useCallback8, useEffect as useEffect11, useMemo as useMemo6, useRef as useRef8, useState as useState12 } from "react";
7223
7349
  import {
7224
7350
  Plus as AddIcon3,
7225
7351
  Paperclip as AttachFileIcon,
@@ -7240,7 +7366,8 @@ import {
7240
7366
  CircleStop as StopCircleIcon,
7241
7367
  ThumbsDown as ThumbDownIcon,
7242
7368
  ThumbsUp as ThumbUpIcon,
7243
- AlertTriangle as WarningIcon
7369
+ AlertTriangle as WarningIcon,
7370
+ Search as SearchIcon
7244
7371
  } from "lucide-react";
7245
7372
  import { useNavigate, useSearchParams } from "react-router-dom";
7246
7373
  import {
@@ -7250,16 +7377,182 @@ import {
7250
7377
  Button as Button8,
7251
7378
  Chip as Chip7,
7252
7379
  CircularProgress as CircularProgress4,
7380
+ ClickAwayListener,
7253
7381
  IconButton as IconButton8,
7254
7382
  InputBase as InputBase2,
7255
7383
  MenuItem as MenuItem2,
7384
+ MenuList,
7385
+ Paper,
7256
7386
  Popover,
7387
+ Popper,
7257
7388
  Select as Select2,
7258
7389
  TextField as TextField5,
7259
7390
  Tooltip as Tooltip9,
7260
7391
  Typography as Typography13
7261
7392
  } from "@mui/material";
7262
7393
  import Markdown from "react-markdown";
7394
+
7395
+ // components/agentPromptSuggestions.ts
7396
+ var AGENT_PROMPT_SUGGESTIONS = [
7397
+ // — Email & comms ————————————————————————————————————————————————
7398
+ "Take care of my emails by pretending to be me and answering them",
7399
+ "Triage my inbox every hour and draft replies for anything urgent",
7400
+ "Summarise my unread emails from the last 24 hours and rank them by importance",
7401
+ "Forward any phishing-looking emails in my inbox to the security team and quarantine them",
7402
+ "Reply to scheduling requests in my inbox using my working hours and calendar availability",
7403
+ "Every morning, send me a digest of overnight emails grouped by sender and topic",
7404
+ "Auto-unsubscribe from marketing emails I have not opened in 60 days",
7405
+ "Snooze low-priority newsletters until Friday afternoon",
7406
+ "Flag and archive shipping notifications once the package is delivered",
7407
+ "Clean up my inbox by archiving anything older than 30 days that I have already read",
7408
+ // — Calendar & meetings ————————————————————————————————————————————
7409
+ "Block focus time on my calendar every weekday morning between 9 and 11",
7410
+ "Decline meetings that conflict with my focus blocks and propose alternatives",
7411
+ "Send me a daily agenda every weekday at 8am with prep notes per meeting",
7412
+ "Reschedule any meeting without an agenda 24 hours before it starts",
7413
+ "Send meeting notes and action items to all attendees after every call",
7414
+ "Find a 30-minute slot this week that works for me and the people I mention",
7415
+ "Cancel recurring meetings that I have skipped 3 times in a row",
7416
+ "Remind me 15 minutes before any meeting that I have not prepared for",
7417
+ // — Incidents & SOC operations ———————————————————————————————————
7418
+ "Triage new incidents every 15 minutes and assign severity, owner, and next steps",
7419
+ "Watch for new critical incidents and page the on-call engineer immediately",
7420
+ "Close incidents older than 30 days that have had no activity and no observables",
7421
+ "Merge duplicate incidents that share the same observable or hostname",
7422
+ "Enrich every new incident with threat intel and asset context within 2 minutes",
7423
+ "Auto-acknowledge incidents from known maintenance windows and link them to the change ticket",
7424
+ "Summarise yesterday's incidents into a daily standup brief for the SOC team",
7425
+ "Escalate any incident that breaches its SLA to the team lead",
7426
+ "Tag every incident with the MITRE ATT&CK technique that best matches its observables",
7427
+ "Watch the alert queue and group related alerts into a single incident",
7428
+ "Auto-resolve incidents whose root cause matches a previously closed incident",
7429
+ "Reopen any closed incident if the same observable shows up again within 7 days",
7430
+ "Send a weekly incident trend report every Monday morning to the security leadership",
7431
+ // — Threat intel & IOCs —————————————————————————————————————————
7432
+ "Pull new IOCs from my threat feeds every hour and push them to the firewall blocklist",
7433
+ "Check every external IP in today's incidents against VirusTotal and AbuseIPDB",
7434
+ "Sweep the environment for any host that has connected to a known bad domain in the last 7 days",
7435
+ "Subscribe to new CISA advisories and turn them into detection rules where possible",
7436
+ "Cross-reference observed file hashes against MalwareBazaar daily",
7437
+ "Watch for newly registered domains that look like our brand and report them",
7438
+ "Alert me whenever a new CVE with a known exploit affects software in our inventory",
7439
+ "Refresh the IOC datastore every 6 hours and prune entries older than 30 days",
7440
+ // — Detection engineering ———————————————————————————————————————
7441
+ "Review noisy detection rules every week and propose tuning suggestions",
7442
+ "Convert the latest Sigma rules from the public repo into our SIEM's format and stage them for review",
7443
+ "Tell me which detection rules have not fired in the last 90 days so I can retire them",
7444
+ "Audit MITRE ATT&CK coverage every Monday and flag techniques with zero detections",
7445
+ "Backtest new detection rules against the last 30 days of logs before promoting them to production",
7446
+ // — SIEM operations ———————————————————————————————————————————
7447
+ "Triage new Splunk alerts every 10 minutes and assign severity, owner, and next steps",
7448
+ "Watch Microsoft Sentinel for high-severity incidents and page the on-call analyst immediately",
7449
+ "Suppress noisy SIEM alerts that fire more than 50 times per hour and open a tuning ticket",
7450
+ "Correlate failed login spikes in the SIEM with VPN logs and flag possible brute-force attempts",
7451
+ "Hunt the SIEM every morning for sign-ins from countries we do not operate in",
7452
+ "Forward critical SIEM detections straight to Case Management and skip the alert queue",
7453
+ "Run a weekly SIEM data-source health check and alert me on any log source silent for over 1 hour",
7454
+ "Enrich every SIEM alert with asset owner, criticality, and recent vulnerabilities before triage",
7455
+ "Search the SIEM for the latest IOCs from threat intel feeds every 30 minutes",
7456
+ "Summarise top 10 noisiest SIEM rules every Friday and propose tuning candidates",
7457
+ "Detect lateral movement patterns in SIEM auth logs and open an incident with the affected hosts",
7458
+ "Watch the SIEM for any disable or delete of audit logging and treat it as a critical incident",
7459
+ // — EDR operations ————————————————————————————————————————————
7460
+ "Isolate any endpoint that triggers a critical CrowdStrike alert and notify the user's manager",
7461
+ "Kill any process on a flagged host that matches a known malware family in EDR telemetry",
7462
+ "Watch SentinelOne for ransomware behavior detections and auto-contain the host within 60 seconds",
7463
+ "Pull the process tree from EDR for every malware detection and attach it to the incident",
7464
+ "Sweep all endpoints for a given hash or filename when a new IOC arrives and report matches",
7465
+ "Detect newly created local admin accounts on any endpoint and require approval before keeping them",
7466
+ "Alert when an EDR agent goes offline for more than 1 hour and open a ticket against the asset owner",
7467
+ "Quarantine any binary flagged as suspicious by EDR and email the user with next steps",
7468
+ "Hunt EDR telemetry every night for LOLBins executed from user-writable directories",
7469
+ "Correlate EDR alerts with SIEM auth events to detect credential theft followed by execution",
7470
+ "Run a weekly EDR coverage report and list endpoints without an active agent",
7471
+ "Auto-resolve EDR alerts that match a known-good signed binary on our allowlist",
7472
+ // — Vulnerability management ————————————————————————————————————
7473
+ "Scan our asset inventory daily and open tickets for any new critical or high vulnerability",
7474
+ "Remind asset owners every Friday about open vulnerabilities older than 14 days",
7475
+ "Re-scan hosts that recently patched a critical vulnerability and close the ticket if clean",
7476
+ "Send the patch compliance report every Monday morning to IT and security leadership",
7477
+ "Correlate vulnerabilities with active exploits in the wild and re-prioritise the queue",
7478
+ // — Endpoint & host monitoring ———————————————————————————————————
7479
+ "Watch my host monitors and alert me if any agent goes offline for more than 1 hour",
7480
+ "Isolate any endpoint that triggers a critical EDR alert and notify the user's manager",
7481
+ "Kill any process on a flagged host that matches a known malware family",
7482
+ "Run an inventory of installed software on every endpoint every Sunday",
7483
+ "Detect new local admin accounts created on any endpoint and require approval",
7484
+ "Make sure full-disk encryption is on for every laptop and report exceptions weekly",
7485
+ // — Identity & access —————————————————————————————————————————
7486
+ "Review user access every quarter and flag anyone with privileges they have not used in 90 days",
7487
+ "Disable accounts that have not logged in for 60 days and notify their manager",
7488
+ "Watch for impossible travel sign-ins and force a password reset plus MFA re-enrolment",
7489
+ "Alert me whenever someone is added to an admin group and require justification within 24 hours",
7490
+ "Audit MFA enrolment weekly and email any user without a second factor",
7491
+ "Rotate service account credentials every 90 days and update the vault",
7492
+ "Detect dormant API keys older than 180 days and revoke them after notice",
7493
+ "Make sure new joiners get baseline access provisioned within 1 hour of their start date",
7494
+ "Make sure leavers have all access revoked within 15 minutes of their termination event",
7495
+ // — Cloud, infra & posture ————————————————————————————————————
7496
+ "Check our cloud accounts every hour for public S3 buckets and lock them down",
7497
+ "Detect new IAM roles with wildcard permissions and require review before they go live",
7498
+ "Watch for unencrypted databases in production and open a ticket against the owner",
7499
+ "Run a CIS benchmark scan against all production cloud accounts every Sunday",
7500
+ "Alert on any new security group that opens ports to 0.0.0.0/0",
7501
+ "Monitor cloud spend daily and warn me if any service's cost jumps more than 50% week-over-week",
7502
+ "Check certificate expiry across our domains weekly and renew anything within 30 days",
7503
+ "Watch our DNS records for unauthorised changes and revert them",
7504
+ // — Phishing & user reports ———————————————————————————————————
7505
+ "Investigate phishing reports from the abuse mailbox and respond to the reporter within 1 hour",
7506
+ "Detonate suspicious attachments in a sandbox and attach the verdict to the incident",
7507
+ "Pull URLs from reported phishing emails and submit them to URLhaus and PhishTank",
7508
+ "Send a phishing simulation to a sample of users every month and report results",
7509
+ // — Compliance, audit & reporting —————————————————————————————
7510
+ "Generate the SOC2 evidence pack at the start of every month",
7511
+ "Track changes to security policies and alert the GRC team when one is edited",
7512
+ "Send a weekly KPI report covering MTTD, MTTR, and open incident backlog",
7513
+ "Audit log retention every Sunday and confirm we still meet the 1-year requirement",
7514
+ "Generate a monthly executive summary of incidents, vulnerabilities, and detection coverage",
7515
+ // — Backups, DR & uptime ————————————————————————————————————
7516
+ "Verify nightly backups completed successfully and alert me if any failed",
7517
+ "Test restore from backup on a sample workload every month and report results",
7518
+ "Watch our public status page for incidents at our critical vendors and notify the team",
7519
+ "Ping our public endpoints every minute and open an incident if any return non-2xx for 3 minutes",
7520
+ // — Tickets, workflow & docs ——————————————————————————————————
7521
+ "Triage new Jira tickets in the security project every hour and assign by category",
7522
+ "Close tickets that have been waiting on the reporter for more than 14 days",
7523
+ "Summarise this week's closed tickets into release notes every Friday",
7524
+ "Pull action items out of meeting notes I share and create tickets for them",
7525
+ "Keep the on-call runbook in sync with the latest incident postmortems",
7526
+ "Review pull requests in our security repos and leave a summary comment within an hour",
7527
+ // — People, HR & onboarding ————————————————————————————————————
7528
+ "Send a welcome message and security training link to every new joiner on their start date",
7529
+ "Run the offboarding checklist whenever someone is marked as a leaver in HR",
7530
+ "Remind managers to complete quarterly performance reviews 1 week before the deadline",
7531
+ "Send birthday and work anniversary greetings from me on the right day",
7532
+ // — Personal / lightweight ops chores —————————————————————————
7533
+ "Order more office coffee whenever the inventory drops below 2 bags",
7534
+ "File my receipts every Friday and submit the expense report",
7535
+ "Track my open invoices and chase clients whose payment is more than 14 days overdue",
7536
+ "Send a polite follow-up to anyone I have emailed but who has not replied in 5 working days",
7537
+ "Read the security news feeds every morning and summarise the top 5 stories for me"
7538
+ ];
7539
+ var matchAgentPromptSuggestions = (input, limit = 8) => {
7540
+ const q = (input || "").trim().toLowerCase();
7541
+ if (!q) return [];
7542
+ const scored = [];
7543
+ for (const text of AGENT_PROMPT_SUGGESTIONS) {
7544
+ const lower = text.toLowerCase();
7545
+ const idx = lower.indexOf(q);
7546
+ if (idx < 0) continue;
7547
+ const score = idx === 0 ? -1e3 + text.length : idx * 10 + text.length;
7548
+ scored.push({ text, score });
7549
+ }
7550
+ scored.sort((a, b) => a.score - b.score);
7551
+ return scored.slice(0, limit).map((s) => s.text);
7552
+ };
7553
+ var DEFAULT_AGENT_PROMPT_PLACEHOLDER = "Take care of my emails by pretending to be me and answering them";
7554
+
7555
+ // components/AgentUI.tsx
7263
7556
  import remarkGfm4 from "remark-gfm";
7264
7557
  import remarkBreaks from "remark-breaks";
7265
7558
  import JsonView2 from "react18-json-view";
@@ -9193,7 +9486,7 @@ var AgentUI = ({
9193
9486
  autoLoadApps = true,
9194
9487
  title = "What do you want to do?",
9195
9488
  subtitle,
9196
- placeholder = 'Describe a task, e.g. "Get my emails for today and summarise them"',
9489
+ placeholder = DEFAULT_AGENT_PROMPT_PLACEHOLDER,
9197
9490
  defaultInput = "",
9198
9491
  autoSubmit = false,
9199
9492
  hideHeroIcon = false,
@@ -9245,6 +9538,29 @@ var AgentUI = ({
9245
9538
  const hasApiKey = !!apiKey || !!API_CONFIG.apiKey;
9246
9539
  const navigate = useNavigate();
9247
9540
  const [actionInput, setActionInput] = useState12(defaultInput);
9541
+ const promptAnchorRef = React4.useRef(null);
9542
+ const [suggestionsDismissed, setSuggestionsDismissed] = useState12(false);
9543
+ const [suggestionIndex, setSuggestionIndex] = useState12(-1);
9544
+ const promptSuggestions = useMemo6(
9545
+ () => matchAgentPromptSuggestions(actionInput, 8),
9546
+ [actionInput]
9547
+ );
9548
+ useEffect11(() => {
9549
+ setSuggestionIndex(-1);
9550
+ setSuggestionsDismissed(false);
9551
+ }, [actionInput]);
9552
+ const suggestionsOpen = promptSuggestions.length > 0 && !suggestionsDismissed;
9553
+ const acceptSuggestion = useCallback8((s) => {
9554
+ setActionInput(s);
9555
+ setSuggestionsDismissed(true);
9556
+ setSuggestionIndex(-1);
9557
+ requestAnimationFrame(() => {
9558
+ try {
9559
+ inputRef.current?.focus();
9560
+ } catch {
9561
+ }
9562
+ });
9563
+ }, []);
9248
9564
  const BUILTIN_DEFAULT_APPS = [
9249
9565
  { name: "http" },
9250
9566
  { name: "shuffle_tools" }
@@ -10238,6 +10554,34 @@ var AgentUI = ({
10238
10554
  }
10239
10555
  }, [submitOverride, actionInput, chosenApps, submitInput]);
10240
10556
  const onKeyDown = (e) => {
10557
+ if (suggestionsOpen && promptSuggestions.length > 0) {
10558
+ if (e.key === "ArrowDown") {
10559
+ e.preventDefault();
10560
+ setSuggestionIndex((i) => (i + 1) % promptSuggestions.length);
10561
+ return;
10562
+ }
10563
+ if (e.key === "ArrowUp") {
10564
+ e.preventDefault();
10565
+ setSuggestionIndex((i) => i <= 0 ? promptSuggestions.length - 1 : i - 1);
10566
+ return;
10567
+ }
10568
+ if (e.key === "Escape") {
10569
+ e.preventDefault();
10570
+ setSuggestionsDismissed(true);
10571
+ setSuggestionIndex(-1);
10572
+ return;
10573
+ }
10574
+ if (e.key === "Tab" && suggestionIndex >= 0) {
10575
+ e.preventDefault();
10576
+ acceptSuggestion(promptSuggestions[suggestionIndex]);
10577
+ return;
10578
+ }
10579
+ if (e.key === "Enter" && suggestionIndex >= 0 && !e.metaKey && !e.ctrlKey && !e.shiftKey) {
10580
+ e.preventDefault();
10581
+ acceptSuggestion(promptSuggestions[suggestionIndex]);
10582
+ return;
10583
+ }
10584
+ }
10241
10585
  if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
10242
10586
  e.preventDefault();
10243
10587
  handlePrimarySubmit();
@@ -10895,7 +11239,7 @@ var AgentUI = ({
10895
11239
  mt: -1,
10896
11240
  maxWidth: 600
10897
11241
  }, children: subtitle }),
10898
- /* @__PURE__ */ jsxs17(Box14, { sx: {
11242
+ /* @__PURE__ */ jsxs17(Box14, { ref: promptAnchorRef, sx: {
10899
11243
  width: "100%",
10900
11244
  display: "flex",
10901
11245
  flexDirection: "column",
@@ -11103,6 +11447,67 @@ var AgentUI = ({
11103
11447
  ) }) })
11104
11448
  ] })
11105
11449
  ] }),
11450
+ /* @__PURE__ */ jsx20(
11451
+ Popper,
11452
+ {
11453
+ open: suggestionsOpen,
11454
+ anchorEl: promptAnchorRef.current,
11455
+ placement: "bottom-start",
11456
+ style: { zIndex: 1300, width: promptAnchorRef.current?.offsetWidth },
11457
+ modifiers: [{ name: "offset", options: { offset: [0, 6] } }],
11458
+ children: /* @__PURE__ */ jsx20(ClickAwayListener, { onClickAway: () => setSuggestionsDismissed(true), children: /* @__PURE__ */ jsx20(
11459
+ Paper,
11460
+ {
11461
+ elevation: 6,
11462
+ sx: {
11463
+ bgcolor: "hsl(var(--card))",
11464
+ border: "1px solid hsl(var(--border))",
11465
+ borderRadius: 2,
11466
+ overflow: "hidden",
11467
+ maxHeight: 320,
11468
+ overflowY: "auto"
11469
+ },
11470
+ children: /* @__PURE__ */ jsx20(MenuList, { dense: true, disablePadding: true, children: promptSuggestions.map((s, i) => {
11471
+ const q = actionInput.trim();
11472
+ const lower = s.toLowerCase();
11473
+ const idx = q ? lower.indexOf(q.toLowerCase()) : -1;
11474
+ const before = idx >= 0 ? s.slice(0, idx) : s;
11475
+ const match = idx >= 0 ? s.slice(idx, idx + q.length) : "";
11476
+ const after = idx >= 0 ? s.slice(idx + q.length) : "";
11477
+ return /* @__PURE__ */ jsxs17(
11478
+ MenuItem2,
11479
+ {
11480
+ selected: i === suggestionIndex,
11481
+ onMouseEnter: () => setSuggestionIndex(i),
11482
+ onMouseDown: (e) => {
11483
+ e.preventDefault();
11484
+ acceptSuggestion(s);
11485
+ },
11486
+ sx: {
11487
+ fontSize: "0.88rem",
11488
+ color: "hsl(var(--foreground))",
11489
+ py: 0.75,
11490
+ px: 2,
11491
+ "&.Mui-selected, &.Mui-selected:hover": {
11492
+ bgcolor: "hsl(var(--muted) / 0.6)"
11493
+ }
11494
+ },
11495
+ children: [
11496
+ /* @__PURE__ */ jsx20(SearchIcon, { size: 14, style: { marginRight: 10, color: "hsl(var(--muted-foreground))", flexShrink: 0 } }),
11497
+ /* @__PURE__ */ jsxs17(Box14, { component: "span", sx: { whiteSpace: "normal", lineHeight: 1.4 }, children: [
11498
+ before,
11499
+ /* @__PURE__ */ jsx20(Box14, { component: "span", sx: { fontWeight: 700, color: "hsl(var(--foreground))" }, children: match }),
11500
+ after
11501
+ ] })
11502
+ ]
11503
+ },
11504
+ s
11505
+ );
11506
+ }) })
11507
+ }
11508
+ ) })
11509
+ }
11510
+ ),
11106
11511
  !hideAppPicker && /* @__PURE__ */ jsx20(Box14, { sx: { display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ jsxs17(Box14, { sx: {
11107
11512
  display: "inline-flex",
11108
11513
  flexWrap: "wrap",
@@ -11959,6 +12364,32 @@ var ENDPOINT_PRESETS = [
11959
12364
  { label: "LM Studio (localhost)", url: "http://localhost:1234/v1", apiKeyHint: "Local LM Studio does not require an API key \u2014 any non-empty value works." },
11960
12365
  { label: CUSTOM_PRESET, url: "" }
11961
12366
  ];
12367
+ var PROVIDER_DOMAINS = {
12368
+ "Shuffle AI": "shuffler.io",
12369
+ OpenAI: "openai.com",
12370
+ Anthropic: "anthropic.com",
12371
+ "Google Gemini": "gemini.google.com",
12372
+ Mistral: "mistral.ai",
12373
+ Groq: "groq.com",
12374
+ DeepSeek: "deepseek.com",
12375
+ "Together AI": "together.ai",
12376
+ OpenRouter: "openrouter.ai",
12377
+ "Ollama (localhost)": "ollama.com",
12378
+ "LM Studio (localhost)": "lmstudio.ai"
12379
+ };
12380
+ var ProviderLogo = ({ label, url }) => {
12381
+ const [errored, setErrored] = useState13(false);
12382
+ let domain = PROVIDER_DOMAINS[label];
12383
+ if (!domain && url) {
12384
+ try {
12385
+ domain = new URL(url).hostname.replace(/^www\./, "");
12386
+ } catch {
12387
+ }
12388
+ }
12389
+ const src = domain ? `https://www.google.com/s2/favicons?domain=${domain}&sz=64` : "";
12390
+ const initial = label.trim().charAt(0).toUpperCase() || "?";
12391
+ return /* @__PURE__ */ jsx22(Box16, { sx: { width: 18, height: 18, borderRadius: "4px", display: "flex", alignItems: "center", justifyContent: "center", bgcolor: "hsl(var(--muted))", overflow: "hidden", flexShrink: 0 }, children: src && !errored ? /* @__PURE__ */ jsx22("img", { src, alt: "", width: 18, height: 18, style: { display: "block" }, onError: () => setErrored(true) }) : /* @__PURE__ */ jsx22(Typography15, { sx: { fontSize: "0.65rem", fontWeight: 600, color: "hsl(var(--muted-foreground))", lineHeight: 1 }, children: initial }) });
12392
+ };
11962
12393
  var LocalLLMConfig = ({ compact, globalUrl, userdata, isLoaded, isLoggedIn, serverside, theme, colorMode }) => {
11963
12394
  useSyncHostBaseUrl(globalUrl);
11964
12395
  const { authStates, authenticatedApps, handleAuthChange, handleTestConnection, handleSaveAuth, refreshAuth } = useAppAuth();
@@ -12102,12 +12533,28 @@ var LocalLLMConfig = ({ compact, globalUrl, userdata, isLoaded, isLoggedIn, serv
12102
12533
  isOptionEqualToValue: (opt, val) => opt === val,
12103
12534
  renderOption: (props, option) => {
12104
12535
  const preset = ENDPOINT_PRESETS.find((p) => p.label === option);
12105
- return /* @__PURE__ */ createElement2("li", { ...props, key: option }, /* @__PURE__ */ jsxs19(Box16, { sx: { display: "flex", alignItems: "center", gap: 1, width: "100%", minWidth: 0 }, children: [
12536
+ return /* @__PURE__ */ createElement2("li", { ...props, key: option }, /* @__PURE__ */ jsxs19(Box16, { sx: { display: "flex", alignItems: "center", gap: 1.25, width: "100%", minWidth: 0 }, children: [
12537
+ /* @__PURE__ */ jsx22(ProviderLogo, { label: option, url: preset?.url }),
12106
12538
  /* @__PURE__ */ jsx22(Typography15, { sx: { fontSize: "0.85rem", color: "hsl(var(--popover-foreground))" }, children: option }),
12107
12539
  preset?.url && /* @__PURE__ */ jsx22(Typography15, { component: "span", sx: { ml: "auto", color: "hsl(var(--muted-foreground))", fontSize: "0.75rem", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: preset.url })
12108
12540
  ] }));
12109
12541
  },
12110
- renderInput: (params) => /* @__PURE__ */ jsx22(TextField6, { ...params, placeholder: "Search a provider\u2026" }),
12542
+ renderInput: (params) => {
12543
+ const preset = ENDPOINT_PRESETS.find((p) => p.label === effectivePreset);
12544
+ return /* @__PURE__ */ jsx22(
12545
+ TextField6,
12546
+ {
12547
+ ...params,
12548
+ placeholder: "Search a provider\u2026",
12549
+ slotProps: {
12550
+ input: {
12551
+ ...params.InputProps,
12552
+ startAdornment: effectivePreset ? /* @__PURE__ */ jsx22(Box16, { sx: { display: "flex", alignItems: "center", pl: 0.5, mr: 0.5 }, children: /* @__PURE__ */ jsx22(ProviderLogo, { label: effectivePreset, url: preset?.url }) }) : void 0
12553
+ }
12554
+ }
12555
+ }
12556
+ );
12557
+ },
12111
12558
  slotProps: {
12112
12559
  paper: { sx: { bgcolor: "hsl(var(--popover))", color: "hsl(var(--popover-foreground))", border: "1px solid hsl(var(--border))" } },
12113
12560
  popper: { sx: { zIndex: 9999 } }
@@ -12451,7 +12898,7 @@ import {
12451
12898
  Server as Server2,
12452
12899
  XCircle,
12453
12900
  Zap,
12454
- Search as SearchIcon
12901
+ Search as SearchIcon2
12455
12902
  } from "lucide-react";
12456
12903
 
12457
12904
  // agentActivity.ts
@@ -13097,17 +13544,20 @@ var AgentRunRow = ({ run, onClick, sx, appIcons, onAppClick }) => {
13097
13544
  ] })
13098
13545
  ] })
13099
13546
  ] }),
13100
- tools.length > 0 && /* @__PURE__ */ jsx24(
13547
+ /* @__PURE__ */ jsx24(
13101
13548
  Box18,
13102
13549
  {
13103
13550
  onClick: (e) => e.stopPropagation(),
13104
13551
  sx: {
13105
13552
  display: { xs: "none", sm: "flex" },
13106
13553
  alignItems: "center",
13554
+ justifyContent: "flex-end",
13107
13555
  flexShrink: 0,
13108
- ml: 1
13556
+ ml: 1,
13557
+ width: 140,
13558
+ minHeight: 28
13109
13559
  },
13110
- children: /* @__PURE__ */ jsx24(
13560
+ children: tools.length > 0 ? /* @__PURE__ */ jsx24(
13111
13561
  AvatarGroup2,
13112
13562
  {
13113
13563
  max: 5,
@@ -13156,6 +13606,17 @@ var AgentRunRow = ({ run, onClick, sx, appIcons, onAppClick }) => {
13156
13606
  ) }, t.name);
13157
13607
  })
13158
13608
  }
13609
+ ) : /* @__PURE__ */ jsx24(
13610
+ Typography17,
13611
+ {
13612
+ sx: {
13613
+ fontSize: "0.7rem",
13614
+ color: "hsl(var(--muted-foreground))",
13615
+ opacity: 0.5,
13616
+ fontStyle: "italic"
13617
+ },
13618
+ children: "No apps"
13619
+ }
13159
13620
  )
13160
13621
  }
13161
13622
  )
@@ -13499,7 +13960,7 @@ var AgentActivityList = ({
13499
13960
  onChange: (e) => updateSearchQuery(e.target.value),
13500
13961
  slotProps: {
13501
13962
  input: {
13502
- startAdornment: /* @__PURE__ */ jsx24(InputAdornment, { position: "start", children: /* @__PURE__ */ jsx24(SearchIcon, { size: 18, color: "hsl(var(--muted-foreground))" }) })
13963
+ startAdornment: /* @__PURE__ */ jsx24(InputAdornment, { position: "start", children: /* @__PURE__ */ jsx24(SearchIcon2, { size: 18, color: "hsl(var(--muted-foreground))" }) })
13503
13964
  }
13504
13965
  },
13505
13966
  sx: {
@@ -14370,7 +14831,9 @@ export {
14370
14831
  deleteDatastoreItem,
14371
14832
  deleteDatastoreItems,
14372
14833
  diagnoseOutputWarning,
14834
+ extractActionAppNames,
14373
14835
  extractValidatedIngestionApps,
14836
+ extractWorkflowActionAppNames,
14374
14837
  extractWorkflowAppNames,
14375
14838
  fetchApps,
14376
14839
  fetchAppsViaApiConfig,