@shuffleio/shuffle-mcps 0.0.70 → 0.0.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.js +2198 -2197
  2. package/dist/index.mjs +2198 -2197
  3. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -14133,1867 +14133,276 @@ var collectLlmImageAttachments = (root2, maxDepth = 10) => {
14133
14133
  return deduped;
14134
14134
  };
14135
14135
 
14136
- // components/AgentActivityList.tsx
14137
- import { Pencil, StopCircle, AlertTriangle } from "lucide-react";
14136
+ // ../components/docs/tocUtils.ts
14137
+ var isTocHeading = (text2) => {
14138
+ const norm4 = text2.toLowerCase().replace(/[^a-z0-9]/g, "");
14139
+ return norm4 === "tableofcontents" || norm4 === "tableofcontent" || norm4 === "toc";
14140
+ };
14141
+ var stripMarkdownInline = (value) => value.replace(/!\[[^\]]*\]\([^)]*\)/g, "").replace(/\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/<[^>]+>/g, "").replace(/[`*_~#>]/g, "").replace(/\{#[^}]+\}/g, "").replace(/\s+/g, " ").trim();
14142
+ var stripInContentToc = (markdown) => {
14143
+ if (!markdown) return "";
14144
+ const lines = markdown.split("\n");
14145
+ const result = [];
14146
+ let inToc = false;
14147
+ for (let i = 0; i < lines.length; i++) {
14148
+ const line = lines[i];
14149
+ const trimmed = line.trim();
14150
+ const headingMatch = trimmed.match(/^(#{1,4})\s+(.+)$/);
14151
+ if (headingMatch) {
14152
+ const headingText = stripMarkdownInline(headingMatch[2]);
14153
+ if (isTocHeading(headingText)) {
14154
+ inToc = true;
14155
+ continue;
14156
+ } else if (inToc) {
14157
+ inToc = false;
14158
+ }
14159
+ }
14160
+ if (inToc) {
14161
+ if (!trimmed || /^[-*+]\s+/.test(trimmed) || /^\d+\.\s+/.test(trimmed) || /^[-*_]{3,}$/.test(trimmed) || /^>/.test(trimmed) || /^<ul[\s\S]*?<\/ul>$/i.test(trimmed)) {
14162
+ continue;
14163
+ } else {
14164
+ inToc = false;
14165
+ }
14166
+ }
14167
+ result.push(line);
14168
+ }
14169
+ return result.join("\n");
14170
+ };
14138
14171
 
14139
- // ../Shuffle-Core/components/SegmentedControl.tsx
14140
- import * as React6 from "react";
14141
- import { motion as motion6, LayoutGroup } from "framer-motion";
14142
- import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
14143
- var SIZE = {
14144
- sm: {
14145
- containerPadding: "2px",
14146
- fontSize: "11px",
14147
- itemPaddingY: "4px",
14148
- itemPaddingX: "10px",
14149
- itemGap: "6px",
14150
- countMin: "16px",
14151
- countH: "16px",
14152
- countPx: "4px",
14153
- countFs: "10px",
14154
- dividerH: "16px",
14155
- dividerMx: "2px"
14172
+ // ../lib/docGroups.ts
14173
+ var DOC_GROUPS = [
14174
+ {
14175
+ id: "usability",
14176
+ label: "Usability",
14177
+ description: "Platform overview, getting started, tenants, features, and API reference.",
14178
+ slugs: ["getting_started", "getting-started", "about", "tenants", "features", "api"]
14156
14179
  },
14157
- md: {
14158
- containerPadding: "4px",
14159
- fontSize: "12px",
14160
- itemPaddingY: "6px",
14161
- itemPaddingX: "12px",
14162
- itemGap: "6px",
14163
- countMin: "18px",
14164
- countH: "18px",
14165
- countPx: "6px",
14166
- countFs: "10px",
14167
- dividerH: "20px",
14168
- dividerMx: "4px"
14180
+ {
14181
+ id: "automation",
14182
+ label: "Automation",
14183
+ description: "Workflows, apps, AI agents, and playbook automations.",
14184
+ slugs: ["workflows", "apps", "ai"]
14185
+ },
14186
+ {
14187
+ id: "security",
14188
+ label: "Security",
14189
+ description: "Incident triage, vulnerability management, host posture, and response.",
14190
+ slugs: ["incidents", "vulnerabilities", "monitors", "host-monitors", "detection", "sigma"]
14191
+ },
14192
+ {
14193
+ id: "infrastructure",
14194
+ label: "Infrastructure",
14195
+ description: "Architecture, configuration, extensions, and troubleshooting.",
14196
+ slugs: ["architecture", "configuration", "extensions", "troubleshooting"]
14169
14197
  }
14198
+ ];
14199
+ var DOC_LABEL_OVERRIDES = {
14200
+ ai: "Agents & AI",
14201
+ api: "API Reference",
14202
+ getting_started: "Getting Started",
14203
+ incidents: "Incidents & Cases",
14204
+ vulnerabilities: "Vulnerabilities",
14205
+ monitors: "Host Monitors",
14206
+ "host-monitors": "Host Monitors",
14207
+ about: "About Shuffle",
14208
+ tenants: "Tenants & Multi-Tenancy",
14209
+ features: "Features Overview",
14210
+ workflows: "Workflows",
14211
+ apps: "Apps & Integrations",
14212
+ extensions: "Extensions",
14213
+ architecture: "Architecture",
14214
+ configuration: "Configuration",
14215
+ troubleshooting: "Troubleshooting"
14170
14216
  };
14171
- var uid = 0;
14172
- function useUniqueId(prefix) {
14173
- const ref = React6.useRef(void 0);
14174
- if (!ref.current) {
14175
- uid += 1;
14176
- ref.current = `${prefix}-${uid}`;
14217
+ var getDocGroup = (slug) => {
14218
+ const usabilityGroup = DOC_GROUPS.find((g) => g.id === "usability") || DOC_GROUPS[0];
14219
+ if (!slug || slug === "index") return usabilityGroup;
14220
+ const clean = slug.toLowerCase().replace(/_+/g, "-");
14221
+ for (const group of DOC_GROUPS) {
14222
+ if (group.slugs.includes(clean)) {
14223
+ return group;
14224
+ }
14177
14225
  }
14178
- return ref.current;
14179
- }
14180
- function SegmentedControl({
14181
- options,
14182
- value,
14183
- onChange,
14184
- size = "md",
14185
- variant = "outline",
14186
- className,
14187
- ariaLabel,
14188
- layoutId,
14189
- trailing
14190
- }) {
14191
- const autoId = useUniqueId("segmented");
14192
- const groupId = layoutId ?? autoId;
14193
- const s = SIZE[size];
14194
- return /* @__PURE__ */ jsx20(LayoutGroup, { id: groupId, children: /* @__PURE__ */ jsxs14(
14195
- "div",
14196
- {
14197
- role: "tablist",
14198
- "aria-label": ariaLabel,
14199
- className,
14200
- style: {
14201
- display: "inline-flex",
14202
- alignItems: "center",
14203
- gap: "2px",
14204
- borderRadius: 9999,
14205
- padding: s.containerPadding,
14206
- fontSize: s.fontSize,
14207
- border: variant === "outline" ? "1px solid hsl(var(--border))" : "1px solid transparent",
14208
- background: variant === "outline" ? "transparent" : "hsl(var(--muted) / 0.4)"
14209
- },
14210
- children: [
14211
- options.map((opt, i) => {
14212
- if (opt.type === "divider") {
14213
- return /* @__PURE__ */ jsx20(
14214
- "span",
14215
- {
14216
- "aria-hidden": true,
14217
- style: {
14218
- width: 1,
14219
- height: s.dividerH,
14220
- margin: `0 ${s.dividerMx}`,
14221
- background: "hsl(var(--border))",
14222
- alignSelf: "center"
14223
- }
14224
- },
14225
- opt.key ?? `divider-${i}`
14226
- );
14227
- }
14228
- const isAction2 = opt.type === "action";
14229
- const active2 = !isAction2 && opt.value === value;
14230
- const key = isAction2 ? opt.key : opt.value;
14231
- return /* @__PURE__ */ jsxs14(
14232
- "button",
14233
- {
14234
- type: "button",
14235
- "data-tour": opt.dataTour,
14236
- "data-active": active2 ? "true" : "false",
14237
- role: isAction2 ? "button" : "tab",
14238
- "aria-selected": isAction2 ? void 0 : active2,
14239
- title: opt.title,
14240
- disabled: opt.disabled,
14241
- onClick: () => {
14242
- if (opt.disabled) return;
14243
- if (isAction2) opt.onClick();
14244
- else onChange(opt.value);
14245
- },
14246
- style: {
14247
- position: "relative",
14248
- display: "inline-flex",
14249
- alignItems: "center",
14250
- gap: s.itemGap,
14251
- padding: `${s.itemPaddingY} ${s.itemPaddingX}`,
14252
- borderRadius: 9999,
14253
- fontWeight: 500,
14254
- fontSize: s.fontSize,
14255
- background: "transparent",
14256
- border: "none",
14257
- cursor: opt.disabled ? "not-allowed" : "pointer",
14258
- color: active2 ? "hsl(var(--foreground))" : "hsl(var(--muted-foreground))",
14259
- opacity: opt.disabled ? 0.5 : 1,
14260
- transition: "color 200ms ease",
14261
- outline: "none",
14262
- fontFamily: "inherit"
14263
- },
14264
- onMouseEnter: (e) => {
14265
- if (!active2 && !opt.disabled) {
14266
- e.currentTarget.style.color = "hsl(var(--foreground))";
14267
- }
14268
- },
14269
- onMouseLeave: (e) => {
14270
- if (!active2 && !opt.disabled) {
14271
- e.currentTarget.style.color = "hsl(var(--muted-foreground))";
14272
- }
14273
- },
14274
- children: [
14275
- active2 && /* @__PURE__ */ jsx20(
14276
- motion6.span,
14277
- {
14278
- layoutId: `${groupId}-pill`,
14279
- style: {
14280
- position: "absolute",
14281
- inset: 0,
14282
- borderRadius: 9999,
14283
- background: "hsl(var(--muted))",
14284
- border: "1px solid hsl(var(--border))"
14285
- },
14286
- initial: false,
14287
- transition: {
14288
- type: "spring",
14289
- stiffness: 260,
14290
- damping: 30
14291
- }
14292
- }
14293
- ),
14294
- /* @__PURE__ */ jsx20(
14295
- "span",
14296
- {
14297
- style: {
14298
- position: "relative",
14299
- zIndex: 1,
14300
- display: "inline-flex",
14301
- alignItems: "center",
14302
- gap: s.itemGap
14303
- },
14304
- children: opt.label
14305
- }
14306
- ),
14307
- !isAction2 && (typeof opt.count === "number" || typeof opt.count === "string") && /* @__PURE__ */ jsx20(
14308
- "span",
14309
- {
14310
- style: {
14311
- position: "relative",
14312
- zIndex: 1,
14313
- display: "inline-flex",
14314
- alignItems: "center",
14315
- justifyContent: "center",
14316
- minWidth: s.countMin,
14317
- height: s.countH,
14318
- padding: `0 ${s.countPx}`,
14319
- fontSize: s.countFs,
14320
- fontWeight: 600,
14321
- fontVariantNumeric: "tabular-nums",
14322
- borderRadius: 6,
14323
- border: "1px solid hsl(var(--border) / 0.6)",
14324
- color: active2 ? "hsl(var(--foreground))" : "hsl(var(--muted-foreground))",
14325
- background: "transparent"
14326
- },
14327
- children: opt.count
14328
- }
14329
- )
14330
- ]
14331
- },
14332
- key
14333
- );
14334
- }),
14335
- trailing != null && /* @__PURE__ */ jsx20(
14336
- "span",
14337
- {
14338
- style: {
14339
- display: "inline-flex",
14340
- alignItems: "center",
14341
- alignSelf: "center"
14342
- },
14343
- children: trailing
14344
- }
14345
- )
14346
- ]
14347
- }
14348
- ) });
14349
- }
14350
-
14351
- // components/AgentActivityList.tsx
14352
- import { Fragment as Fragment5, jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
14353
- var STATUS_CONFIG = {
14354
- FINISHED: { icon: /* @__PURE__ */ jsx21(CheckCircle, { size: 16 }), color: "hsl(var(--severity-low, 142 71% 45%))", label: "Completed" },
14355
- SUCCESS: { icon: /* @__PURE__ */ jsx21(CheckCircle, { size: 16 }), color: "hsl(var(--severity-low, 142 71% 45%))", label: "Completed" },
14356
- FAILED: { icon: /* @__PURE__ */ jsx21(XCircle, { size: 16 }), color: "hsl(var(--severity-critical, 0 72% 55%))", label: "Failed" },
14357
- ABORTED: { icon: /* @__PURE__ */ jsx21(XCircle, { size: 16 }), color: "hsl(var(--severity-critical, 0 72% 55%))", label: "Aborted" },
14358
- EXECUTING: { icon: /* @__PURE__ */ jsx21(Loader2, { size: 16 }), color: "hsl(var(--severity-medium, 38 92% 50%))", label: "Running" },
14359
- RUNNING: { icon: /* @__PURE__ */ jsx21(Loader2, { size: 16 }), color: "hsl(var(--severity-medium, 38 92% 50%))", label: "Running" },
14360
- WAITING: { icon: /* @__PURE__ */ jsx21(Clock, { size: 16 }), color: "hsl(var(--severity-info, 217 91% 60%))", label: "Waiting" },
14361
- LIMIT_REACHED: { icon: /* @__PURE__ */ jsx21(AlertTriangle, { size: 16 }), color: "hsl(var(--severity-critical, 0 72% 55%))", label: "Limit reached" }
14226
+ return usabilityGroup;
14362
14227
  };
14363
- var getEffectiveStatus = (run2) => {
14364
- const raw = (run2.status || "").toUpperCase();
14365
- if (raw === "FINISHED" || raw === "SUCCESS" || raw === "FAILED" || raw === "ABORTED") {
14366
- try {
14367
- const d = diagnoseOutputWarning(run2);
14368
- if (d?.kind === "token_limit") return "LIMIT_REACHED";
14369
- } catch {
14370
- }
14228
+ var getDocDisplayLabel = (slug, fallbackName) => {
14229
+ const clean = (slug || "").toLowerCase().replace(/_+/g, "-");
14230
+ if (DOC_LABEL_OVERRIDES[clean]) {
14231
+ return DOC_LABEL_OVERRIDES[clean];
14371
14232
  }
14372
- return raw;
14233
+ const raw = fallbackName || slug || "";
14234
+ return raw.replace(/[_-]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
14373
14235
  };
14374
- var SOURCE_ICON_SIZE = 18;
14375
- var looksLikeExecutionId = (s) => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s.trim());
14376
- var DATASTORE_KEY_RE = /\bKey:\s*(\S+)/i;
14377
- var DATASTORE_CATEGORY_RE = /\bCategory:\s*(\S+)/i;
14378
- var parseDatastoreTask = (arg) => {
14379
- if (!arg) return null;
14380
- const k = arg.match(DATASTORE_KEY_RE);
14381
- const c = arg.match(DATASTORE_CATEGORY_RE);
14382
- if (!k || !c) return null;
14383
- const key = (k[1] || "").trim();
14384
- const category = (c[1] || "").trim();
14385
- if (!key || !category) return null;
14386
- return { key, category };
14236
+
14237
+ // ../components/docs/docGroups.ts
14238
+ var groupDocsCache = /* @__PURE__ */ new Map();
14239
+ var getCachedGroupDocs = (groupId, folder) => {
14240
+ const cacheKey2 = `${folder || "docs"}:${groupId}`;
14241
+ const cached = groupDocsCache.get(cacheKey2);
14242
+ if (!cached) return [];
14243
+ return Object.entries(cached).map(([slug, content]) => ({
14244
+ slug,
14245
+ title: getDocDisplayLabel(slug),
14246
+ content
14247
+ }));
14387
14248
  };
14388
- var isAIAgentResult = (result) => String(result?.action?.app_name || "").trim().toLowerCase() === "ai agent";
14389
- var getAIAgentResultPayload = (run2) => {
14390
- const results = Array.isArray(run2.results) ? run2.results : [];
14391
- const agentResult = results.find(isAIAgentResult);
14392
- if (!agentResult || typeof agentResult !== "object" || !("result" in agentResult)) return null;
14393
- return tryParseJson(agentResult.result);
14249
+
14250
+ // ../lib/incidentPromptContext.ts
14251
+ var GLOBAL_INCIDENT_KEY = "__shuffleActiveIncidentContext";
14252
+ var getGlobalScope = () => {
14253
+ if (typeof globalThis !== "undefined") return globalThis;
14254
+ if (typeof window !== "undefined") return window;
14255
+ if (typeof global !== "undefined") return global;
14256
+ return null;
14394
14257
  };
14395
- var getDirectAgentInput = (payload) => {
14396
- if (!payload || typeof payload !== "object") return null;
14397
- const obj = payload;
14398
- if (typeof obj.original_input === "string" && obj.original_input.trim()) {
14399
- return obj.original_input.trim();
14400
- }
14401
- if (typeof obj.input === "string" && obj.input.trim()) {
14402
- return obj.input.trim();
14258
+ var getActiveIncidentPromptContext = () => {
14259
+ const scope2 = getGlobalScope();
14260
+ if (!scope2) return null;
14261
+ const info = scope2[GLOBAL_INCIDENT_KEY];
14262
+ if (info && typeof info === "object") {
14263
+ return info;
14403
14264
  }
14404
14265
  return null;
14405
14266
  };
14406
- var findDatastoreTaskInRun = (run2) => {
14407
- const agentInput = getDirectAgentInput(getAIAgentResultPayload(run2));
14408
- const agentHit = parseDatastoreTask(agentInput || void 0);
14409
- if (agentHit) return agentHit;
14410
- const anyRun = run2;
14411
- const candidates = [];
14412
- if (typeof anyRun.original_input === "string") candidates.push(anyRun.original_input);
14413
- if (typeof anyRun.input === "string") candidates.push(anyRun.input);
14414
- if (run2.execution_argument) candidates.push(run2.execution_argument);
14415
- if (run2.result) candidates.push(run2.result);
14416
- if (Array.isArray(anyRun.results)) {
14417
- try {
14418
- candidates.push(JSON.stringify(anyRun.results));
14419
- } catch {
14420
- }
14421
- }
14422
- for (const c of candidates) {
14423
- const hit = parseDatastoreTask(c);
14424
- if (hit) return hit;
14267
+ var INCIDENT_PROMPT_DELIMITER_START = "=== INCIDENT CONTEXT ===";
14268
+ var INCIDENT_PROMPT_DELIMITER_END = "=== END INCIDENT CONTEXT ===";
14269
+ var formatObservableType = (type) => {
14270
+ if (!type) return "Indicator";
14271
+ const clean = type.toLowerCase().trim();
14272
+ switch (clean) {
14273
+ case "ip":
14274
+ case "ipv4":
14275
+ case "ip_address":
14276
+ return "IPv4";
14277
+ case "ipv6":
14278
+ return "IPv6";
14279
+ case "hash":
14280
+ case "sha256":
14281
+ case "file_hash":
14282
+ return "SHA256";
14283
+ case "md5":
14284
+ return "MD5";
14285
+ case "sha1":
14286
+ return "SHA1";
14287
+ case "hostname":
14288
+ case "host":
14289
+ case "endpoint":
14290
+ return "Hostname";
14291
+ case "domain":
14292
+ return "Domain";
14293
+ case "url":
14294
+ return "URL";
14295
+ case "user":
14296
+ case "username":
14297
+ case "account":
14298
+ return "User";
14299
+ case "email":
14300
+ return "Email";
14301
+ case "process":
14302
+ case "process_name":
14303
+ return "Process";
14304
+ case "file":
14305
+ case "filename":
14306
+ return "File";
14307
+ default:
14308
+ return type.charAt(0).toUpperCase() + type.slice(1);
14425
14309
  }
14426
- return null;
14427
14310
  };
14428
- var classifyRunSource = (run2) => {
14429
- const raw = (run2.execution_source || "").trim();
14430
- const src = raw.toLowerCase();
14431
- const sourceParts = raw.split("|");
14432
- const fromSource = src.startsWith("datastore") && sourceParts.length > 2 ? { category: sourceParts[1].trim(), key: sourceParts[2].trim() } : null;
14433
- const ds = findDatastoreTaskInRun(run2) || fromSource;
14434
- if (ds || src.includes("datastore") || src.includes("enrichment") || src.includes("automation")) {
14435
- return {
14436
- kind: "datastore",
14437
- label: "Datastore automation",
14438
- reason: ds ? `Category: ${ds.category}
14439
- Key: ${ds.key}
14440
- Click to open it in the Datastore.` : "Started by a datastore automation (e.g. enrichment trigger).",
14441
- icon: /* @__PURE__ */ jsx21(Database, { size: SOURCE_ICON_SIZE }),
14442
- datastoreKey: ds?.key,
14443
- datastoreCategory: ds?.category
14444
- };
14311
+ var isIncidentInjectedPrompt = (prompt) => {
14312
+ if (!prompt || typeof prompt !== "string") return false;
14313
+ return prompt.includes(INCIDENT_PROMPT_DELIMITER_START) || prompt.includes("OPERATIONAL DIRECTIVES:") || prompt.includes("INCIDENT OBSERVABLES / IOCs:");
14314
+ };
14315
+ var extractCleanIncidentPrompt = (prompt) => {
14316
+ if (!prompt || typeof prompt !== "string") return "";
14317
+ const trimmed = prompt.trim();
14318
+ const taskMatch = trimmed.match(/TASK TO EXECUTE:\s*\n\s*-\s*Task:\s*([^\n]+)/i);
14319
+ if (taskMatch && taskMatch[1]?.trim()) {
14320
+ return `Task: ${taskMatch[1].trim()}`;
14445
14321
  }
14446
- if (src.includes("schedule") || src.includes("cron")) {
14447
- return {
14448
- kind: "schedule",
14449
- label: "Schedule",
14450
- reason: "Started by a scheduled trigger (cron).",
14451
- icon: /* @__PURE__ */ jsx21(Clock, { size: SOURCE_ICON_SIZE })
14452
- };
14322
+ const legacyMatch = trimmed.match(/\n\s*Task:\s*([^\n]+)/i);
14323
+ if (legacyMatch && legacyMatch[1]?.trim()) {
14324
+ return `Task: ${legacyMatch[1].trim()}`;
14453
14325
  }
14454
- if (src.includes("webhook") || src.includes("http")) {
14455
- return {
14456
- kind: "webhook",
14457
- label: "Webhook",
14458
- reason: "Started by an incoming webhook / HTTP trigger.",
14459
- icon: /* @__PURE__ */ jsx21(Globe, { size: SOURCE_ICON_SIZE })
14460
- };
14326
+ if (trimmed.includes(INCIDENT_PROMPT_DELIMITER_START)) {
14327
+ const before = trimmed.split(INCIDENT_PROMPT_DELIMITER_START)[0]?.trim();
14328
+ if (before) return before;
14461
14329
  }
14462
- if (src.includes("form")) {
14463
- return {
14464
- kind: "form",
14465
- label: "Form",
14466
- reason: "Started by a form submission.",
14467
- icon: /* @__PURE__ */ jsx21(FileText, { size: SOURCE_ICON_SIZE })
14468
- };
14330
+ return trimmed;
14331
+ };
14332
+ var composeIncidentPromptInput = (rawInput, context) => {
14333
+ if (!rawInput || typeof rawInput !== "string") return "";
14334
+ const trimmed = rawInput.trim();
14335
+ if (isIncidentInjectedPrompt(trimmed)) {
14336
+ return trimmed;
14469
14337
  }
14470
- if (raw && looksLikeExecutionId(raw)) {
14471
- return {
14472
- kind: "workflow",
14473
- label: "Workflow",
14474
- reason: "Started by another workflow as a subflow.",
14475
- icon: /* @__PURE__ */ jsx21(WorkflowIcon, { size: SOURCE_ICON_SIZE })
14476
- };
14338
+ const ctx = context || getActiveIncidentPromptContext();
14339
+ if (!ctx || !ctx.id) {
14340
+ return trimmed;
14477
14341
  }
14478
- if (src === "" || src === "manual" || src === "default" || src === "demo") {
14479
- return {
14480
- kind: "manual",
14481
- label: "Manual",
14482
- reason: "Started manually from the Agents UI.",
14483
- icon: /* @__PURE__ */ jsx21(Hand, { size: SOURCE_ICON_SIZE })
14484
- };
14485
- }
14486
- return {
14487
- kind: "unknown",
14488
- label: raw || "Unknown",
14489
- reason: raw ? `Started by "${raw}".` : "Trigger source unknown.",
14490
- icon: /* @__PURE__ */ jsx21(Zap, { size: SOURCE_ICON_SIZE })
14491
- };
14492
- };
14493
- var getRunIconColor = (run2) => {
14494
- const status = getEffectiveStatus(run2);
14495
- if (status === "FINISHED" || status === "SUCCESS") return "hsl(var(--severity-low, 142 71% 45%))";
14496
- if (status === "FAILED" || status === "ABORTED") return "hsl(var(--severity-critical, 0 72% 55%))";
14497
- if (status === "LIMIT_REACHED") return "hsl(var(--severity-critical, 0 72% 55%))";
14498
- if (status === "EXECUTING" || status === "RUNNING") return "hsl(var(--severity-medium, 38 92% 50%))";
14499
- return "hsl(var(--primary, 24 100% 50%))";
14500
- };
14501
- var formatDuration = (run2) => {
14502
- if (run2.started_at && run2.completed_at) {
14503
- const start = Number(run2.started_at);
14504
- const end = Number(run2.completed_at);
14505
- if (!isNaN(start) && !isNaN(end)) {
14506
- const toMs = (n) => n > 1e12 ? n : n * 1e3;
14507
- const ms = Math.max(0, toMs(end) - toMs(start));
14508
- if (ms < 1e3) return `${(ms / 1e3).toFixed(2)}s`;
14509
- if (ms < 6e4) return `${(ms / 1e3).toFixed(2)}s`;
14510
- return `${Math.floor(ms / 6e4)}m ${Math.floor(ms % 6e4 / 1e3)}s`;
14511
- }
14512
- }
14513
- if (run2.duration) return `${run2.duration.toFixed(2)}s`;
14514
- return "";
14515
- };
14516
- var getTimeAgo = (dateStr) => {
14517
- try {
14518
- const ts = isNaN(Number(dateStr)) ? new Date(dateStr).getTime() : Number(dateStr) * 1e3;
14519
- if (isNaN(ts)) return dateStr;
14520
- const diffSec = Math.max(0, Math.floor((Date.now() - ts) / 1e3));
14521
- if (diffSec < 60) return `${diffSec}s ago`;
14522
- if (diffSec < 3600) return `${Math.floor(diffSec / 60)}m ago`;
14523
- if (diffSec < 86400) return `${Math.floor(diffSec / 3600)}h ago`;
14524
- return `${Math.floor(diffSec / 86400)}d ago`;
14525
- } catch {
14526
- return dateStr;
14527
- }
14528
- };
14529
- var PROMPT_KEYS = [
14530
- "original_input",
14531
- "originalinput",
14532
- "input",
14533
- "prompt",
14534
- "user_input",
14535
- "userinput",
14536
- "user_prompt",
14537
- "userprompt",
14538
- "question",
14539
- "query",
14540
- "message",
14541
- "text",
14542
- "content",
14543
- "task",
14544
- "instruction",
14545
- "instructions"
14546
- ];
14547
- var deepFindPrompt = (value, depth = 0) => {
14548
- if (value == null || depth > 6) return null;
14549
- if (typeof value === "string") {
14550
- const s = value.trim();
14551
- if (s.startsWith("{") && s.endsWith("}") || s.startsWith("[") && s.endsWith("]")) {
14552
- try {
14553
- return deepFindPrompt(JSON.parse(s), depth + 1);
14554
- } catch {
14555
- }
14556
- }
14557
- return null;
14558
- }
14559
- if (Array.isArray(value)) {
14560
- for (const item of value) {
14561
- const hit = deepFindPrompt(item, depth + 1);
14562
- if (hit) return hit;
14563
- }
14564
- return null;
14565
- }
14566
- if (typeof value === "object") {
14567
- const obj = value;
14568
- for (const key of Object.keys(obj)) {
14569
- if (PROMPT_KEYS.includes(key.toLowerCase())) {
14570
- const v = obj[key];
14571
- if (typeof v === "string" && v.trim()) return v.trim();
14572
- const nested = deepFindPrompt(v, depth + 1);
14573
- if (nested) return nested;
14574
- }
14575
- }
14576
- for (const key of Object.keys(obj)) {
14577
- if (PROMPT_KEYS.includes(key.toLowerCase())) continue;
14578
- const hit = deepFindPrompt(obj[key], depth + 1);
14579
- if (hit) return hit;
14580
- }
14581
- }
14582
- return null;
14583
- };
14584
- var tryParseJson = (s) => {
14585
- if (typeof s !== "string") return s;
14586
- const trimmed = s.trim();
14587
- if (!trimmed) return null;
14588
- try {
14589
- return JSON.parse(trimmed);
14590
- } catch {
14591
- return trimmed;
14592
- }
14593
- };
14594
- var getRunPrompt = (run2) => {
14595
- const anyRun = run2;
14596
- const agentInput = getDirectAgentInput(getAIAgentResultPayload(run2));
14597
- if (agentInput) return agentInput;
14598
- const results = Array.isArray(anyRun.results) ? anyRun.results : null;
14599
- if (results) {
14600
- const agentResult = results.find(isAIAgentResult);
14601
- if (agentResult?.result) {
14602
- const hit = deepFindPrompt(tryParseJson(agentResult.result));
14603
- if (hit) return hit;
14604
- }
14605
- for (const r of results) {
14606
- if (r === agentResult) continue;
14607
- const hit = deepFindPrompt(tryParseJson(r?.result));
14608
- if (hit) return hit;
14342
+ const incidentRef = ctx.id ? `#${ctx.id}` : "";
14343
+ const incidentTitle = ctx.title?.trim() || "Incident";
14344
+ const severity = ctx.severity || "Medium";
14345
+ const status = ctx.status || "In Progress";
14346
+ const description = ctx.description?.trim() || "No description provided.";
14347
+ const seenObservables = /* @__PURE__ */ new Set();
14348
+ const formattedObservables = [];
14349
+ if (Array.isArray(ctx.observables)) {
14350
+ for (const obs of ctx.observables) {
14351
+ if (!obs || obs.archived || !obs.value?.trim()) continue;
14352
+ const val = obs.value.trim();
14353
+ const typeLabel = formatObservableType(obs.type);
14354
+ const key = `${typeLabel.toLowerCase()}:${val.toLowerCase()}`;
14355
+ if (seenObservables.has(key)) continue;
14356
+ seenObservables.add(key);
14357
+ formattedObservables.push(`- ${typeLabel}: ${val}`);
14609
14358
  }
14610
14359
  }
14611
- if (typeof anyRun.original_input === "string" && anyRun.original_input.trim()) {
14612
- return anyRun.original_input.trim();
14613
- }
14614
- if (typeof anyRun.input === "string" && anyRun.input.trim()) {
14615
- return anyRun.input.trim();
14616
- }
14617
- if (run2.result) {
14618
- const hit = deepFindPrompt(tryParseJson(run2.result));
14619
- if (hit) return hit;
14620
- }
14621
- if (run2.execution_argument) {
14622
- const parsed = tryParseJson(run2.execution_argument);
14623
- const hit = deepFindPrompt(parsed);
14624
- if (hit) return hit;
14625
- if (typeof parsed === "string" && parsed.length < 240) return parsed;
14626
- }
14627
- return null;
14360
+ const observablesBlock = formattedObservables.length > 0 ? formattedObservables.slice(0, 50).join("\n") : "- None recorded yet";
14361
+ return `${trimmed}
14362
+
14363
+ ${INCIDENT_PROMPT_DELIMITER_START}
14364
+ INCIDENT CONTEXT:
14365
+ - Incident: ${incidentRef} ("${incidentTitle}")
14366
+ - Severity: ${severity} | Status: ${status}
14367
+ - Summary: ${description}
14368
+
14369
+ INCIDENT OBSERVABLES / IOCs:
14370
+ ${observablesBlock}
14371
+ ${INCIDENT_PROMPT_DELIMITER_END}
14372
+
14373
+ DIRECTIVES:
14374
+ - Relevant incident details and observables are provided above. Do not call "get_incident" unless raw external logs are required.`;
14628
14375
  };
14629
- var stripMarkdown = (input) => {
14630
- if (!input) return "";
14631
- let s = input.replace(/\n*=== USECASE CONTEXT ===[\s\S]*?(?:=== END USECASE CONTEXT ===|$)/g, "").replace(/\n*=== DOCS ARTICLE CONTEXT ===[\s\S]*?(?:=== END DOCS CONTEXT ===|$)/g, "");
14632
- s = s.replace(/```[\s\S]*?```/g, " ");
14633
- s = s.replace(/`([^`]*)`/g, "$1");
14634
- s = s.replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1");
14635
- s = s.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1");
14636
- s = s.replace(/(\*\*|__)(.*?)\1/g, "$2");
14637
- s = s.replace(/(\*|_)(?=\S)([^*_\n]+?)\1/g, "$2");
14638
- s = s.replace(/^\s{0,3}#{1,6}\s+/gm, "");
14639
- s = s.replace(/^\s{0,3}>\s?/gm, "");
14640
- s = s.replace(/^\s*[-*+]\s+/gm, "");
14641
- s = s.replace(/^\s*\d+\.\s+/gm, "");
14642
- s = s.replace(/\s+/g, " ").trim();
14643
- return s;
14376
+
14377
+ // ../lib/usecasePromptContext.ts
14378
+ var GLOBAL_KEY = "__shuffleActiveUsecaseContext";
14379
+ var GLOBAL_OVERVIEW_KEY = "__shuffleActiveUsecasesOverviewContext";
14380
+ var USECASE_CONTEXT_EVENT = "shuffle-usecase-context-changed";
14381
+ var scope = () => typeof globalThis !== "undefined" ? globalThis : null;
14382
+ var getActiveUsecasePromptContext = () => {
14383
+ const v = scope()?.[GLOBAL_KEY];
14384
+ return v && typeof v === "object" ? v : null;
14644
14385
  };
14645
- var getRunTitle = (run2) => {
14646
- const prompt = getRunPrompt(run2);
14647
- if (prompt) {
14648
- const oneLine = stripMarkdown(prompt);
14649
- return oneLine.length > 80 ? oneLine.slice(0, 80) + "\u2026" : oneLine;
14650
- }
14651
- if (run2.workflow?.name) return stripMarkdown(run2.workflow.name);
14652
- return `Execution ${run2.execution_id?.slice(0, 8) || "\u2014"}`;
14386
+ var getActiveUsecasesOverviewContext = () => {
14387
+ const v = scope()?.[GLOBAL_OVERVIEW_KEY];
14388
+ return v && typeof v === "object" ? v : null;
14653
14389
  };
14654
- var getDecisionCount = (run2) => {
14655
- if (Array.isArray(run2.decisions)) return run2.decisions.length;
14656
- return 0;
14390
+ var humanizeUsecaseId = (id) => id.replace(/_\d+$/, "").replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()).trim();
14391
+ var getUsecaseDisplayName = (flowId) => {
14392
+ const ctx = getActiveUsecasePromptContext();
14393
+ if (ctx?.label) return ctx.label;
14394
+ return flowId ? humanizeUsecaseId(flowId) : "Usecases";
14657
14395
  };
14658
- var getRunSubtitle = (run2) => {
14659
- if (run2.result) {
14660
- try {
14661
- const p = JSON.parse(run2.result);
14662
- if (p.output && typeof p.output === "string") {
14663
- const out = stripMarkdown(p.output);
14664
- return out.length > 120 ? out.slice(0, 120) + "\u2026" : out;
14665
- }
14666
- if (p.original_input && typeof p.original_input === "string") {
14667
- const inp = stripMarkdown(p.original_input);
14668
- return inp.length > 120 ? inp.slice(0, 120) + "\u2026" : inp;
14669
- }
14670
- if (p.message && typeof p.message === "string") return stripMarkdown(p.message);
14671
- } catch {
14672
- if (run2.result.length < 120) return stripMarkdown(run2.result);
14673
- }
14674
- }
14675
- const status = getEffectiveStatus(run2);
14676
- const isRunning = status === "EXECUTING" || status === "RUNNING" || status === "WAITING";
14677
- return run2.execution_source || (isRunning ? "Agent still running\u2026" : "Agent execution");
14396
+ var USECASE_PROMPT_START = "=== USECASE CONTEXT ===";
14397
+ var USECASE_PROMPT_END = "=== END USECASE CONTEXT ===";
14398
+ var isUsecaseInjectedPrompt = (prompt) => {
14399
+ if (!prompt || typeof prompt !== "string") return false;
14400
+ return prompt.includes(USECASE_PROMPT_START);
14678
14401
  };
14679
- var normalizeResultStatus = (s) => {
14680
- const v = (s || "").toUpperCase();
14681
- if (v === "SUCCESS" || v === "FINISHED") return "success";
14682
- if (v === "FAILURE" || v === "FAILED" || v === "ABORTED") return "failure";
14683
- if (v === "WAITING" || v === "SKIPPED") return "waiting";
14684
- return "unknown";
14685
- };
14686
- var getRunTools = (run2) => {
14687
- const map = /* @__PURE__ */ new Map();
14688
- const ids = /* @__PURE__ */ new Map();
14689
- const skip = (s) => /^(ai\s*agent|shuffle\s*agent|shuffle_agent)$/i.test(s);
14690
- const rank = { failure: 3, waiting: 2, success: 1, unknown: 0 };
14691
- const merge2 = (name, status, id) => {
14692
- if (!name) return;
14693
- const s = String(name).trim();
14694
- if (!s || skip(s)) return;
14695
- const next = status || "unknown";
14696
- const prev = map.get(s);
14697
- if (!prev || rank[next] > rank[prev]) map.set(s, next);
14698
- if (id && !ids.has(s)) ids.set(s, id);
14699
- };
14700
- const allowed = run2.allowed_actions;
14701
- if (Array.isArray(allowed) && allowed.length > 0) {
14702
- for (const entry of allowed) {
14703
- if (typeof entry !== "string") continue;
14704
- const parts = entry.split(":");
14705
- if (parts.length < 3 || parts[0] !== "app") continue;
14706
- const id = parts[1];
14707
- const name = parts.slice(2).join(":");
14708
- merge2(name, "unknown", id);
14709
- }
14710
- const normalize3 = (s) => s.toLowerCase().replace(/[\s_-]+/g, "_");
14711
- const known = /* @__PURE__ */ new Map();
14712
- for (const real of map.keys()) known.set(normalize3(real), real);
14713
- const apply = (name, status) => {
14714
- if (!name) return;
14715
- const hit = known.get(normalize3(name));
14716
- if (hit) merge2(hit, status);
14717
- };
14718
- (run2.results || []).forEach((r) => {
14719
- apply(r?.action?.app_name || r?.action?.label, normalizeResultStatus(r?.status));
14720
- });
14721
- (run2.decisions || []).forEach((d) => {
14722
- if (typeof d?.tool === "string") apply(d.tool, normalizeResultStatus(d?.status));
14723
- });
14724
- } else {
14725
- (run2.results || []).forEach((r) => {
14726
- merge2(r?.action?.app_name || r?.action?.label, normalizeResultStatus(r?.status));
14727
- });
14728
- (run2.decisions || []).forEach((d) => {
14729
- if (typeof d?.tool === "string") merge2(d.tool, normalizeResultStatus(d?.status));
14730
- });
14731
- }
14732
- return Array.from(map.entries()).slice(0, 6).map(([name, status]) => ({ name, status, id: ids.get(name) }));
14733
- };
14734
- var TOOL_STATUS_RING = {
14735
- success: "hsl(var(--severity-low, 142 71% 45%))",
14736
- failure: "hsl(var(--severity-critical, 0 72% 55%))",
14737
- waiting: "hsl(var(--severity-medium, 38 92% 50%))",
14738
- unknown: "hsl(var(--border))"
14739
- };
14740
- var TOOL_STATUS_LABEL = {
14741
- success: "ran successfully",
14742
- failure: "failed",
14743
- waiting: "needs input",
14744
- unknown: "used"
14745
- };
14746
- var normToolKey = (s) => s.toLowerCase().replace(/[\s_\-]+/g, "_");
14747
- var AgentRunRow = ({ run: run2, onClick, sx, appIcons, onAppClick, apiKey, apiBaseUrl, orgId, abortingIds, onAbort }) => {
14748
- const navigate = useNavigate();
14749
- const statusKey = getEffectiveStatus(run2);
14750
- const iconColor = getRunIconColor(run2);
14751
- const duration = formatDuration(run2);
14752
- const tools = getRunTools(run2);
14753
- const decisionCount = getDecisionCount(run2);
14754
- const attachmentCount = collectLlmImageAttachments(run2).length;
14755
- const isRunning = statusKey === "EXECUTING" || statusKey === "RUNNING" || statusKey === "WAITING";
14756
- const isAborting = !!run2.execution_id && !!abortingIds?.has(run2.execution_id);
14757
- const wfId = run2.workflow_id || run2?.workflow?.id;
14758
- const canAbort = isRunning && !!run2.execution_id && !!wfId && !isAborting;
14759
- return /* @__PURE__ */ jsxs15(
14760
- Box14,
14761
- {
14762
- onClick,
14763
- sx: [
14764
- {
14765
- borderRadius: 2,
14766
- border: "1px solid hsl(var(--border))",
14767
- bgcolor: "hsl(var(--card))",
14768
- display: "flex",
14769
- alignItems: "center",
14770
- gap: 2,
14771
- px: 2.5,
14772
- py: 2,
14773
- cursor: "pointer",
14774
- transition: "background 0.15s ease, border-color 0.15s ease",
14775
- "&:hover": {
14776
- bgcolor: "hsla(var(--muted) / 0.5)",
14777
- borderColor: "hsl(var(--muted-foreground) / 0.3)"
14778
- }
14779
- },
14780
- ...Array.isArray(sx) ? sx : sx ? [sx] : []
14781
- ],
14782
- children: [
14783
- (() => {
14784
- const sourceInfo = classifyRunSource(run2);
14785
- const dsLink = sourceInfo.kind === "datastore" && sourceInfo.datastoreKey && sourceInfo.datastoreCategory ? `https://shuffler.io/admin?tab=datastore&category=${encodeURIComponent(sourceInfo.datastoreCategory)}&key=${encodeURIComponent(sourceInfo.datastoreKey)}` : null;
14786
- return /* @__PURE__ */ jsx21(
14787
- Tooltip7,
14788
- {
14789
- title: /* @__PURE__ */ jsxs15(Box14, { sx: { lineHeight: 1.4 }, children: [
14790
- /* @__PURE__ */ jsx21(Box14, { sx: { fontWeight: 600 }, children: sourceInfo.label }),
14791
- /* @__PURE__ */ jsx21(Box14, { sx: { opacity: 0.85, whiteSpace: "pre-line" }, children: sourceInfo.reason })
14792
- ] }),
14793
- placement: "top",
14794
- arrow: true,
14795
- children: /* @__PURE__ */ jsx21(
14796
- Box14,
14797
- {
14798
- onClick: dsLink ? async (e) => {
14799
- e.stopPropagation();
14800
- await navigateToShuffleCore(dsLink, { newTab: true });
14801
- } : void 0,
14802
- sx: {
14803
- width: 40,
14804
- height: 40,
14805
- borderRadius: "50%",
14806
- display: "flex",
14807
- alignItems: "center",
14808
- justifyContent: "center",
14809
- bgcolor: `${iconColor}15`,
14810
- color: iconColor,
14811
- flexShrink: 0,
14812
- cursor: dsLink ? "pointer" : "inherit",
14813
- transition: "background 0.15s ease",
14814
- "&:hover": dsLink ? { bgcolor: `${iconColor}30` } : void 0
14815
- },
14816
- children: sourceInfo.icon
14817
- }
14818
- )
14819
- }
14820
- );
14821
- })(),
14822
- /* @__PURE__ */ jsxs15(Box14, { sx: { flex: 1, minWidth: 0 }, children: [
14823
- /* @__PURE__ */ jsx21(Box14, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 0.25 }, children: /* @__PURE__ */ jsx21(
14824
- Typography12,
14825
- {
14826
- sx: {
14827
- fontSize: "0.9rem",
14828
- fontWeight: 500,
14829
- color: "hsl(var(--foreground))",
14830
- overflow: "hidden",
14831
- textOverflow: "ellipsis",
14832
- whiteSpace: "nowrap"
14833
- },
14834
- children: getRunTitle(run2)
14835
- }
14836
- ) }),
14837
- /* @__PURE__ */ jsx21(
14838
- Typography12,
14839
- {
14840
- sx: {
14841
- fontSize: "0.78rem",
14842
- color: "hsl(var(--muted-foreground))",
14843
- overflow: "hidden",
14844
- textOverflow: "ellipsis",
14845
- whiteSpace: "nowrap"
14846
- },
14847
- children: getRunSubtitle(run2)
14848
- }
14849
- ),
14850
- /* @__PURE__ */ jsxs15(Box14, { sx: { display: "flex", alignItems: "center", gap: 1, mt: 0.5 }, children: [
14851
- /* @__PURE__ */ jsx21(Typography12, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.7 }, children: run2.started_at ? getTimeAgo(run2.started_at) : "\u2014" }),
14852
- duration && /* @__PURE__ */ jsxs15(Fragment5, { children: [
14853
- /* @__PURE__ */ jsx21(Typography12, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.4 }, children: "\xB7" }),
14854
- /* @__PURE__ */ jsx21(Typography12, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.7 }, children: duration })
14855
- ] }),
14856
- decisionCount > 0 && /* @__PURE__ */ jsxs15(Fragment5, { children: [
14857
- /* @__PURE__ */ jsx21(Typography12, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.4 }, children: "\xB7" }),
14858
- /* @__PURE__ */ jsxs15(Box14, { sx: { display: "inline-flex", alignItems: "center", gap: 0.5, color: "hsl(var(--muted-foreground))", opacity: 0.7 }, children: [
14859
- /* @__PURE__ */ jsx21(GitBranch, { size: 11 }),
14860
- /* @__PURE__ */ jsxs15(Typography12, { sx: { fontSize: "0.72rem", color: "inherit" }, children: [
14861
- decisionCount,
14862
- " ",
14863
- decisionCount === 1 ? "decision" : "decisions"
14864
- ] })
14865
- ] })
14866
- ] }),
14867
- attachmentCount > 0 && /* @__PURE__ */ jsxs15(Fragment5, { children: [
14868
- /* @__PURE__ */ jsx21(Typography12, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.4 }, children: "\xB7" }),
14869
- /* @__PURE__ */ jsxs15(Box14, { sx: { display: "inline-flex", alignItems: "center", gap: 0.5, color: "hsl(var(--muted-foreground))", opacity: 0.7 }, children: [
14870
- /* @__PURE__ */ jsx21(ImageIcon, { size: 11 }),
14871
- /* @__PURE__ */ jsxs15(Typography12, { sx: { fontSize: "0.72rem", color: "inherit" }, children: [
14872
- attachmentCount,
14873
- " ",
14874
- attachmentCount === 1 ? "attachment" : "attachments"
14875
- ] })
14876
- ] })
14877
- ] })
14878
- ] })
14879
- ] }),
14880
- /* @__PURE__ */ jsx21(
14881
- Box14,
14882
- {
14883
- onClick: (e) => e.stopPropagation(),
14884
- sx: {
14885
- display: { xs: "none", sm: "flex" },
14886
- alignItems: "center",
14887
- justifyContent: "flex-end",
14888
- flexShrink: 0,
14889
- ml: tools.length > 0 ? 1 : 0,
14890
- width: tools.length > 0 ? 140 : 0,
14891
- minHeight: tools.length > 0 ? 28 : 0
14892
- },
14893
- children: tools.length > 0 ? /* @__PURE__ */ jsx21(
14894
- AvatarGroup,
14895
- {
14896
- max: 5,
14897
- sx: {
14898
- "& .MuiAvatar-root": {
14899
- width: 28,
14900
- height: 28,
14901
- fontSize: "0.7rem",
14902
- borderColor: "hsl(var(--border))",
14903
- bgcolor: "hsl(var(--muted))",
14904
- color: "hsl(var(--muted-foreground))"
14905
- }
14906
- },
14907
- children: tools.map((t) => {
14908
- const icon = appIcons?.[normToolKey(t.name)];
14909
- const label = t.name.replace(/_/g, " ");
14910
- const slug = t.name.toLowerCase().replace(/\s+/g, "_");
14911
- const ring = TOOL_STATUS_RING[t.status];
14912
- return /* @__PURE__ */ jsx21(Tooltip7, { title: `${label} \u2014 ${TOOL_STATUS_LABEL[t.status]}`, arrow: true, children: /* @__PURE__ */ jsx21(
14913
- Avatar4,
14914
- {
14915
- src: icon || void 0,
14916
- alt: label,
14917
- variant: "rounded",
14918
- onClick: (e) => {
14919
- e.stopPropagation();
14920
- if (onAppClick) {
14921
- onAppClick({ id: t.id, name: t.name });
14922
- } else {
14923
- navigate(`/apps/${encodeURIComponent(t.id || slug)}`);
14924
- }
14925
- },
14926
- sx: {
14927
- cursor: "pointer",
14928
- borderColor: `${ring} !important`,
14929
- borderWidth: "1px",
14930
- borderStyle: "solid",
14931
- transition: "transform 0.15s ease, border-color 0.15s ease",
14932
- "&:hover": {
14933
- transform: "scale(1.08)",
14934
- borderColor: "hsl(var(--primary)) !important"
14935
- }
14936
- },
14937
- children: label.charAt(0).toUpperCase()
14938
- }
14939
- ) }, t.name);
14940
- })
14941
- }
14942
- ) : null
14943
- }
14944
- ),
14945
- canAbort && /* @__PURE__ */ jsx21(Tooltip7, { title: isAborting ? "Aborting\u2026" : "Abort this execution", arrow: true, placement: "top", children: /* @__PURE__ */ jsx21("span", { children: /* @__PURE__ */ jsx21(
14946
- IconButton4,
14947
- {
14948
- size: "small",
14949
- disabled: isAborting,
14950
- onClick: (e) => {
14951
- e.stopPropagation();
14952
- onAbort?.(run2);
14953
- },
14954
- sx: {
14955
- flexShrink: 0,
14956
- ml: 1.5,
14957
- color: "hsl(var(--muted-foreground))",
14958
- "&:hover": { color: "hsl(var(--destructive))", bgcolor: "hsl(var(--muted))" }
14959
- },
14960
- children: isAborting ? /* @__PURE__ */ jsx21(CircularProgress5, { size: 16, sx: { color: "hsl(var(--destructive))" } }) : /* @__PURE__ */ jsx21(StopCircle, { size: 18 })
14961
- }
14962
- ) }) })
14963
- ]
14964
- }
14965
- );
14966
- };
14967
- var STATUS_FILTERS = [
14968
- { label: "All", value: "" },
14969
- { label: "Completed", value: "FINISHED" },
14970
- { label: "Running", value: "EXECUTING" },
14971
- { label: "Failed", value: "ABORTED" }
14972
- ];
14973
- var AgentActivityList = ({
14974
- apiKey,
14975
- apiBaseUrl,
14976
- orgId,
14977
- onRunClick,
14978
- renderAppDetailDrawer,
14979
- onEditWorkflow,
14980
- showSearchBar = true,
14981
- showStatusChips = true,
14982
- limit = 50,
14983
- top,
14984
- emptyTitle = "No agent activity found",
14985
- emptySubtitle = "The agent has not performed any actions yet",
14986
- className,
14987
- sx,
14988
- toolbarSx,
14989
- rowSx,
14990
- usecaseFilters = [],
14991
- globalUrl,
14992
- theme,
14993
- colorMode,
14994
- initialRuns,
14995
- disableFetch = false
14996
- }) => {
14997
- const [appDrawer, setAppDrawer] = useState13(null);
14998
- const [runs, setRuns] = useState13(
14999
- () => initialRuns ? initialRuns.slice(0, limit) : []
15000
- );
15001
- const [cursor, setCursor] = useState13("");
15002
- const [hasMore, setHasMore] = useState13(false);
15003
- const [isLoading, setIsLoading] = useState13(false);
15004
- const [error, setError] = useState13(null);
15005
- const [statusFilter, setStatusFilter] = useState13("");
15006
- const [searchQuery, setSearchQuery] = useState13("");
15007
- const [debouncedQuery, setDebouncedQuery] = useState13("");
15008
- const [agentWorkflows, setAgentWorkflows] = useState13([]);
15009
- const [workflowFilter, setWorkflowFilter] = useState13("");
15010
- const [usecaseFilter, setUsecaseFilter] = useState13("");
15011
- const [stopOpen, setStopOpen] = useState13(false);
15012
- const [stopLoading, setStopLoading] = useState13(false);
15013
- const [appIcons, setAppIcons] = useState13({});
15014
- const [enrichedRuns, setEnrichedRuns] = useState13({});
15015
- const enrichedRunsRef = useRef11({});
15016
- const processedRunIdsRef = useRef11(/* @__PURE__ */ new Set());
15017
- useEffect12(() => {
15018
- enrichedRunsRef.current = enrichedRuns;
15019
- }, [enrichedRuns]);
15020
- const [abortingIds, setAbortingIds] = useState13(/* @__PURE__ */ new Set());
15021
- const [abortedIds, setAbortedIds] = useState13(/* @__PURE__ */ new Set());
15022
- const [lastOpenedRunId, setLastOpenedRunId] = useState13(() => getLastOpenedAgentRun());
15023
- useEffect12(() => {
15024
- const unsubAbort = subscribeAgentAborted((eid) => {
15025
- setAbortedIds((prev) => prev.has(eid) ? prev : /* @__PURE__ */ new Set([...Array.from(prev), eid]));
15026
- setRuns((prev) => prev.map((r) => r.execution_id === eid ? { ...r, status: "ABORTED" } : r));
15027
- });
15028
- const unsubOpened = subscribeLastOpenedAgentRun((eid) => setLastOpenedRunId(eid));
15029
- return () => {
15030
- unsubAbort();
15031
- unsubOpened();
15032
- };
15033
- }, []);
15034
- const debounceRef = useRef11(void 0);
15035
- const selectedAgentWorkflow = agentWorkflows.find((w) => w.id === workflowFilter) || null;
15036
- const openEditPrompt = useCallback8(async () => {
15037
- if (!workflowFilter) return;
15038
- try {
15039
- const { prompt, apps } = await getAgentScheduleConfig(workflowFilter, { apiKey, apiBaseUrl, orgId });
15040
- onEditWorkflow?.({
15041
- workflowId: workflowFilter,
15042
- name: selectedAgentWorkflow?.name || "Schedule",
15043
- prompt,
15044
- apps
15045
- });
15046
- } catch (e) {
15047
- setError(e instanceof Error ? e.message : "Failed to load workflow");
15048
- }
15049
- }, [workflowFilter, selectedAgentWorkflow, apiKey, apiBaseUrl, orgId, onEditWorkflow]);
15050
- const confirmStop = useCallback8(async () => {
15051
- if (!workflowFilter) return;
15052
- setStopLoading(true);
15053
- try {
15054
- await stopAgentSchedule(workflowFilter, { apiKey, apiBaseUrl, orgId });
15055
- setStopOpen(false);
15056
- const items = await listAgentScheduleWorkflows({ apiKey, apiBaseUrl, orgId });
15057
- setAgentWorkflows(items);
15058
- setWorkflowFilter("");
15059
- } catch (e) {
15060
- setError(e instanceof Error ? e.message : "Failed to stop schedule");
15061
- } finally {
15062
- setStopLoading(false);
15063
- }
15064
- }, [workflowFilter, apiKey, apiBaseUrl, orgId]);
15065
- const handleAbort = useCallback8(
15066
- async (run2) => {
15067
- const eid = run2.execution_id;
15068
- const wfId = run2.workflow_id || run2?.workflow?.id;
15069
- if (!eid || !wfId) return;
15070
- const prevStatus = run2.status;
15071
- setAbortingIds((prev) => /* @__PURE__ */ new Set([...Array.from(prev), eid]));
15072
- setAbortedIds((prev) => /* @__PURE__ */ new Set([...Array.from(prev), eid]));
15073
- broadcastAgentAborted(eid);
15074
- try {
15075
- await abortAgentExecution({
15076
- workflowId: wfId,
15077
- executionId: eid,
15078
- authorization: run2.authorization,
15079
- apiKey,
15080
- apiBaseUrl,
15081
- orgId
15082
- });
15083
- setRuns(
15084
- (prev) => prev.map((r) => r.execution_id === eid ? { ...r, status: "ABORTED" } : r)
15085
- );
15086
- } catch (e) {
15087
- setAbortedIds((prev) => {
15088
- const next = new Set(Array.from(prev));
15089
- next.delete(eid);
15090
- return next;
15091
- });
15092
- setRuns(
15093
- (prev) => prev.map((r) => r.execution_id === eid ? { ...r, status: prevStatus } : r)
15094
- );
15095
- toast2({
15096
- title: "Abort failed",
15097
- description: e instanceof Error ? e.message : "Failed to abort execution",
15098
- variant: "destructive"
15099
- });
15100
- } finally {
15101
- setAbortingIds((prev) => {
15102
- const next = new Set(Array.from(prev));
15103
- next.delete(eid);
15104
- return next;
15105
- });
15106
- }
15107
- },
15108
- [apiKey, apiBaseUrl, orgId]
15109
- );
15110
- const updateSearchQuery = useCallback8((q) => {
15111
- setSearchQuery(q);
15112
- if (debounceRef.current) clearTimeout(debounceRef.current);
15113
- debounceRef.current = setTimeout(() => setDebouncedQuery(q), 350);
15114
- }, []);
15115
- useEffect12(() => () => {
15116
- if (debounceRef.current) clearTimeout(debounceRef.current);
15117
- }, []);
15118
- const fetchRuns = useCallback8(
15119
- async (append = false, cursorParam = "") => {
15120
- if (disableFetch) {
15121
- if (initialRuns && !append) {
15122
- setRuns(initialRuns.slice(0, limit));
15123
- }
15124
- return;
15125
- }
15126
- setIsLoading(true);
15127
- setError(null);
15128
- try {
15129
- const result = await searchAgentActivity({
15130
- limit,
15131
- top,
15132
- status: statusFilter,
15133
- cursor: cursorParam,
15134
- apiKey,
15135
- apiBaseUrl,
15136
- orgId,
15137
- workflowId: workflowFilter || "AGENT"
15138
- });
15139
- if (result.success && result.runs && result.runs.length > 0) {
15140
- const pageRuns = result.runs.slice(0, limit);
15141
- setRuns((prev) => append ? [...prev, ...pageRuns] : pageRuns);
15142
- setCursor(result.cursor);
15143
- setHasMore(!!result.cursor && result.runs.length > 0);
15144
- } else if (result.success) {
15145
- setRuns((prev) => append ? prev : initialRuns && initialRuns.length > 0 ? initialRuns.slice(0, limit) : []);
15146
- setCursor(result.cursor);
15147
- setHasMore(false);
15148
- } else {
15149
- if (initialRuns && initialRuns.length > 0 && !append) {
15150
- setRuns(initialRuns.slice(0, limit));
15151
- } else {
15152
- setError("Failed to fetch agent activity");
15153
- }
15154
- }
15155
- } catch (err) {
15156
- if (initialRuns && initialRuns.length > 0 && !append) {
15157
- setRuns(initialRuns.slice(0, limit));
15158
- } else {
15159
- setError(err instanceof Error ? err.message : "Failed to fetch agent activity");
15160
- }
15161
- } finally {
15162
- setIsLoading(false);
15163
- }
15164
- },
15165
- [disableFetch, initialRuns, statusFilter, limit, top, apiKey, apiBaseUrl, orgId, workflowFilter]
15166
- );
15167
- useEffect12(() => {
15168
- fetchRuns(false, "");
15169
- }, [statusFilter, apiKey, apiBaseUrl, orgId, workflowFilter]);
15170
- useEffect12(() => {
15171
- let cancelled = false;
15172
- listAgentScheduleWorkflows({ apiKey, apiBaseUrl, orgId }).then((items) => {
15173
- if (!cancelled) setAgentWorkflows(items);
15174
- }).catch(() => {
15175
- });
15176
- return () => {
15177
- cancelled = true;
15178
- };
15179
- }, [apiKey, apiBaseUrl, orgId]);
15180
- useEffect12(() => {
15181
- let cancelled = false;
15182
- fetchAppsViaApiConfig().then((apps) => {
15183
- if (cancelled || !Array.isArray(apps)) return;
15184
- const map = {};
15185
- for (const a of apps) {
15186
- const name = a?.name;
15187
- const img = a?.large_image || a?.image_url || a?.image;
15188
- if (!name || !img) continue;
15189
- map[normToolKey(name)] = img;
15190
- }
15191
- setAppIcons(map);
15192
- }).catch(() => {
15193
- });
15194
- return () => {
15195
- cancelled = true;
15196
- };
15197
- }, []);
15198
- const iconAttemptsRef = useRef11(/* @__PURE__ */ new Set());
15199
- const iconMountedRef = useRef11(true);
15200
- useEffect12(() => {
15201
- iconMountedRef.current = true;
15202
- return () => {
15203
- iconMountedRef.current = false;
15204
- };
15205
- }, []);
15206
- useEffect12(() => {
15207
- if (!runs.length) return;
15208
- const missing = /* @__PURE__ */ new Set();
15209
- for (const run2 of runs) {
15210
- for (const t of getRunTools(run2)) {
15211
- const key = normToolKey(t.name);
15212
- if (!appIcons[key] && !iconAttemptsRef.current.has(key)) missing.add(t.name);
15213
- }
15214
- }
15215
- if (missing.size === 0) return;
15216
- (async () => {
15217
- const names = [...missing];
15218
- const resolved = {};
15219
- for (let pass = 0; pass < 2; pass++) {
15220
- for (const name of names) {
15221
- const key = normToolKey(name);
15222
- iconAttemptsRef.current.add(key);
15223
- if (!resolved[key]) {
15224
- const fb = publicAppImage(name);
15225
- if (fb) {
15226
- resolved[key] = fb;
15227
- } else {
15228
- try {
15229
- const hit = await Promise.race([
15230
- resolveApp(name),
15231
- new Promise((r) => setTimeout(() => r(null), 4e3))
15232
- ]);
15233
- if (hit?.image) resolved[key] = hit.image;
15234
- } catch {
15235
- }
15236
- }
15237
- }
15238
- await new Promise((r) => setTimeout(r, 120));
15239
- }
15240
- if (pass === 0) await new Promise((r) => setTimeout(r, 2e3));
15241
- }
15242
- if (!iconMountedRef.current || Object.keys(resolved).length === 0) return;
15243
- setAppIcons((prev) => ({ ...prev, ...resolved }));
15244
- })();
15245
- }, [runs, appIcons]);
15246
- useEffect12(() => {
15247
- if (!runs.length) return;
15248
- let cancelled = false;
15249
- const buildPatchFromRun = (r) => {
15250
- const results = Array.isArray(r.results) ? r.results : null;
15251
- if (!results || results.length === 0) return null;
15252
- const agentResult = results.find((x) => x?.action?.app_name === "AI Agent");
15253
- let decisions = Array.isArray(r.decisions) ? r.decisions : void 0;
15254
- let originalInput;
15255
- let allowedActions;
15256
- if (agentResult?.result) {
15257
- try {
15258
- const parsed = JSON.parse(agentResult.result);
15259
- if (!decisions && Array.isArray(parsed?.decisions)) decisions = parsed.decisions;
15260
- if (typeof parsed?.original_input === "string") originalInput = parsed.original_input;
15261
- if (Array.isArray(parsed?.allowed_actions)) allowedActions = parsed.allowed_actions;
15262
- } catch {
15263
- }
15264
- }
15265
- const hasPrompt = !!r.execution_argument || !!originalInput || !!agentResult?.result;
15266
- if (!decisions || !hasPrompt) return null;
15267
- const patch2 = {
15268
- results,
15269
- execution_argument: r.execution_argument,
15270
- result: agentResult?.result ?? r.result,
15271
- decisions,
15272
- allowed_actions: allowedActions
15273
- };
15274
- if (originalInput && !patch2.execution_argument) {
15275
- patch2.execution_argument = JSON.stringify({ original_input: originalInput });
15276
- }
15277
- return patch2;
15278
- };
15279
- let pending = {};
15280
- let flushTimer = null;
15281
- const flush = () => {
15282
- flushTimer = null;
15283
- if (cancelled) return;
15284
- const batch = pending;
15285
- pending = {};
15286
- if (Object.keys(batch).length === 0) return;
15287
- setEnrichedRuns((prev) => ({ ...prev, ...batch }));
15288
- };
15289
- const queuePatch = (id, patch2) => {
15290
- pending[id] = patch2;
15291
- if (!flushTimer) flushTimer = setTimeout(flush, 250);
15292
- };
15293
- const needsFetch = [];
15294
- for (const r of runs) {
15295
- const id = r.execution_id;
15296
- if (!id || enrichedRunsRef.current[id] || processedRunIdsRef.current.has(id)) continue;
15297
- if (id.startsWith("demo-") || id.startsWith("dummy-")) {
15298
- processedRunIdsRef.current.add(id);
15299
- continue;
15300
- }
15301
- const native = buildPatchFromRun(r);
15302
- if (native) {
15303
- processedRunIdsRef.current.add(id);
15304
- queuePatch(id, native);
15305
- } else {
15306
- needsFetch.push(id);
15307
- }
15308
- }
15309
- const CONCURRENCY = 2;
15310
- let i = 0;
15311
- const fetchOne = async (executionId) => {
15312
- try {
15313
- const resp = await fetch(
15314
- getApiUrl(
15315
- `/api/v1/executions/${encodeURIComponent(executionId)}?authorization=${encodeURIComponent(executionId)}`
15316
- ),
15317
- {
15318
- method: "GET",
15319
- credentials: "include",
15320
- headers: {
15321
- ...apiKey ? { Authorization: `Bearer ${apiKey}` } : getAuthHeader(),
15322
- ...orgId ? { "Org-Id": orgId } : {}
15323
- }
15324
- }
15325
- );
15326
- if (!resp.ok || cancelled) return;
15327
- const json = await resp.json().catch(() => null);
15328
- if (!json || cancelled) return;
15329
- let decisions;
15330
- let originalInput;
15331
- let allowedActions;
15332
- const agentResult = Array.isArray(json.results) ? json.results.find((r) => r?.action?.app_name === "AI Agent") : null;
15333
- if (agentResult?.result) {
15334
- try {
15335
- const parsed = JSON.parse(agentResult.result);
15336
- if (Array.isArray(parsed?.decisions)) decisions = parsed.decisions;
15337
- if (typeof parsed?.original_input === "string") originalInput = parsed.original_input;
15338
- if (Array.isArray(parsed?.allowed_actions)) allowedActions = parsed.allowed_actions;
15339
- } catch {
15340
- }
15341
- }
15342
- const patch2 = {
15343
- results: json.results,
15344
- execution_argument: json.execution_argument,
15345
- result: agentResult?.result,
15346
- decisions,
15347
- allowed_actions: allowedActions
15348
- };
15349
- if (originalInput && !patch2.execution_argument) {
15350
- patch2.execution_argument = JSON.stringify({ original_input: originalInput });
15351
- }
15352
- queuePatch(executionId, patch2);
15353
- } catch {
15354
- }
15355
- };
15356
- const idle = (fn) => {
15357
- const ric = window.requestIdleCallback;
15358
- if (ric) ric(fn, { timeout: 1e3 });
15359
- else setTimeout(fn, 32);
15360
- };
15361
- const nextIdle = () => new Promise((resolve) => idle(resolve));
15362
- if (needsFetch.length > 0) {
15363
- for (const id of needsFetch) processedRunIdsRef.current.add(id);
15364
- const workers = Array.from({ length: Math.min(CONCURRENCY, needsFetch.length) }, async () => {
15365
- while (!cancelled && i < needsFetch.length) {
15366
- const id = needsFetch[i++];
15367
- await nextIdle();
15368
- if (cancelled) return;
15369
- await fetchOne(id);
15370
- }
15371
- });
15372
- Promise.all(workers).catch(() => {
15373
- });
15374
- }
15375
- return () => {
15376
- cancelled = true;
15377
- if (flushTimer) clearTimeout(flushTimer);
15378
- };
15379
- }, [runs, apiKey, orgId]);
15380
- const mergedRuns = runs.map((r) => {
15381
- const patch2 = r.execution_id ? enrichedRuns[r.execution_id] : void 0;
15382
- const merged = patch2 ? { ...r, ...patch2 } : r;
15383
- return r.execution_id && abortedIds.has(r.execution_id) ? { ...merged, status: "ABORTED" } : merged;
15384
- });
15385
- const loadMore = useCallback8(() => {
15386
- if (cursor && !isLoading) fetchRuns(true, cursor);
15387
- }, [cursor, isLoading, fetchRuns]);
15388
- const activeUsecase = usecaseFilters.find((u) => u.id === usecaseFilter) || null;
15389
- const usecaseMatchedRuns = activeUsecase ? mergedRuns.filter((r) => {
15390
- const hay = [
15391
- r.execution_source,
15392
- r.workflow?.name,
15393
- r.workflow?.description,
15394
- r.execution_argument,
15395
- ...(r.workflow?.actions || []).flatMap((a) => [a.app_name, a.label])
15396
- ].filter(Boolean).join(" ").toLowerCase();
15397
- return activeUsecase.matchTokens.some((t) => hay.includes(t.toLowerCase()));
15398
- }) : mergedRuns;
15399
- const filteredRuns = debouncedQuery ? usecaseMatchedRuns.filter((r) => {
15400
- const hay = [
15401
- getRunTitle(r),
15402
- getRunSubtitle(r),
15403
- r.execution_id,
15404
- r.execution_source,
15405
- r.status
15406
- ].filter(Boolean).join(" ").toLowerCase();
15407
- return hay.includes(debouncedQuery.toLowerCase());
15408
- }) : usecaseMatchedRuns;
15409
- return /* @__PURE__ */ jsxs15(
15410
- Box14,
15411
- {
15412
- className,
15413
- sx: [{ display: "flex", flexDirection: "column", gap: 2 }, ...Array.isArray(sx) ? sx : sx ? [sx] : []],
15414
- children: [
15415
- (showSearchBar || showStatusChips) && /* @__PURE__ */ jsxs15(Box14, { sx: [{ display: "flex", alignItems: "center", justifyContent: "center", gap: 1.5, flexWrap: "wrap" }, ...Array.isArray(toolbarSx) ? toolbarSx : toolbarSx ? [toolbarSx] : []], children: [
15416
- /* @__PURE__ */ jsxs15(
15417
- Select2,
15418
- {
15419
- size: "small",
15420
- value: usecaseFilter ? `uc:${usecaseFilter}` : workflowFilter,
15421
- onChange: (e) => {
15422
- const val = String(e.target.value);
15423
- if (val.startsWith("uc:")) {
15424
- setUsecaseFilter(val.slice(3));
15425
- setWorkflowFilter("");
15426
- } else {
15427
- setUsecaseFilter("");
15428
- setWorkflowFilter(val);
15429
- }
15430
- },
15431
- displayEmpty: true,
15432
- renderValue: (val) => {
15433
- if (!val) return "All Agent runs";
15434
- if (String(val).startsWith("uc:")) {
15435
- const uc = usecaseFilters.find((u) => u.id === String(val).slice(3));
15436
- return uc?.label || "Usecase agent";
15437
- }
15438
- const wf = agentWorkflows.find((w) => w.id === val);
15439
- return wf?.name || "Selected workflow";
15440
- },
15441
- sx: {
15442
- height: 36,
15443
- minWidth: 200,
15444
- maxWidth: 260,
15445
- fontSize: "0.85rem",
15446
- bgcolor: "hsl(var(--card))",
15447
- color: "hsl(var(--foreground))",
15448
- borderRadius: 1.5,
15449
- "& .MuiOutlinedInput-notchedOutline": { borderColor: "hsl(var(--border))" },
15450
- "&:hover .MuiOutlinedInput-notchedOutline": { borderColor: "hsl(var(--muted-foreground) / 0.3)" },
15451
- "&.Mui-focused .MuiOutlinedInput-notchedOutline": { borderColor: "hsl(var(--primary))" }
15452
- },
15453
- MenuProps: {
15454
- slotProps: {
15455
- paper: {
15456
- sx: {
15457
- bgcolor: "hsl(var(--card))",
15458
- border: "1px solid hsl(var(--border))",
15459
- color: "hsl(var(--foreground))",
15460
- maxHeight: 320
15461
- }
15462
- }
15463
- }
15464
- },
15465
- children: [
15466
- /* @__PURE__ */ jsx21(MenuItem2, { value: "", sx: { fontSize: "0.85rem" }, children: "All Agent runs" }),
15467
- usecaseFilters.length > 0 && /* @__PURE__ */ jsx21(Divider2, { sx: { borderColor: "hsl(var(--border))", my: 0.5 } }),
15468
- usecaseFilters.length > 0 && /* @__PURE__ */ jsx21(
15469
- Box14,
15470
- {
15471
- sx: {
15472
- px: 1.5,
15473
- py: 0.5,
15474
- fontSize: "0.7rem",
15475
- textTransform: "uppercase",
15476
- letterSpacing: "0.04em",
15477
- color: "hsl(var(--muted-foreground))",
15478
- pointerEvents: "none"
15479
- },
15480
- children: "Usecase agents"
15481
- }
15482
- ),
15483
- usecaseFilters.map((u) => /* @__PURE__ */ jsx21(MenuItem2, { value: `uc:${u.id}`, sx: { fontSize: "0.85rem" }, children: u.label }, u.id)),
15484
- agentWorkflows.length > 0 && usecaseFilters.length > 0 && /* @__PURE__ */ jsx21(Divider2, { sx: { borderColor: "hsl(var(--border))", my: 0.5 } }),
15485
- agentWorkflows.map((w) => /* @__PURE__ */ jsx21(MenuItem2, { value: w.id, sx: { fontSize: "0.85rem" }, children: w.name }, w.id)),
15486
- /* @__PURE__ */ jsx21(Divider2, { sx: { borderColor: "hsl(var(--border))", my: 0.5 } }),
15487
- /* @__PURE__ */ jsx21(
15488
- Box14,
15489
- {
15490
- sx: {
15491
- px: 1.5,
15492
- py: 1,
15493
- fontSize: "0.72rem",
15494
- color: "hsl(var(--muted-foreground))",
15495
- lineHeight: 1.45,
15496
- whiteSpace: "normal",
15497
- maxWidth: 280,
15498
- pointerEvents: "none"
15499
- },
15500
- children: "Scheduled agent runs will appear in this list once you set them up."
15501
- }
15502
- )
15503
- ]
15504
- }
15505
- ),
15506
- showSearchBar && /* @__PURE__ */ jsx21(
15507
- TextField5,
15508
- {
15509
- placeholder: "Search results...",
15510
- size: "small",
15511
- value: searchQuery,
15512
- onChange: (e) => updateSearchQuery(e.target.value),
15513
- slotProps: {
15514
- input: {
15515
- startAdornment: /* @__PURE__ */ jsx21(InputAdornment, { position: "start", children: /* @__PURE__ */ jsx21(SearchIcon, { size: 18, color: "hsl(var(--muted-foreground))" }) })
15516
- }
15517
- },
15518
- sx: {
15519
- flex: 1,
15520
- maxWidth: 280,
15521
- minWidth: 160,
15522
- "& .MuiOutlinedInput-root": {
15523
- bgcolor: "hsl(var(--card))",
15524
- borderRadius: 1.5,
15525
- fontSize: "0.85rem",
15526
- height: 36,
15527
- "& fieldset": { borderColor: "hsl(var(--border))" },
15528
- "&:hover fieldset": { borderColor: "hsl(var(--muted-foreground) / 0.3)" },
15529
- "&.Mui-focused fieldset": { borderColor: "hsl(var(--primary))" }
15530
- },
15531
- "& .MuiInputBase-input": { color: "hsl(var(--foreground))" }
15532
- }
15533
- }
15534
- ),
15535
- showStatusChips && /* @__PURE__ */ jsx21(
15536
- SegmentedControl,
15537
- {
15538
- size: "sm",
15539
- ariaLabel: "Filter by status",
15540
- value: statusFilter,
15541
- onChange: setStatusFilter,
15542
- options: STATUS_FILTERS.map((f) => ({ value: f.value, label: f.label }))
15543
- }
15544
- ),
15545
- isLoading && runs.length > 0 && /* @__PURE__ */ jsx21(CircularProgress5, { size: 16, sx: { color: "hsl(var(--primary))", ml: 0.5 } })
15546
- ] }),
15547
- selectedAgentWorkflow && /* @__PURE__ */ jsxs15(
15548
- Box14,
15549
- {
15550
- sx: {
15551
- display: "flex",
15552
- alignItems: "center",
15553
- gap: 1.5,
15554
- p: 1.5,
15555
- borderRadius: 2,
15556
- border: "1px solid hsl(var(--border))",
15557
- bgcolor: "hsl(var(--card))"
15558
- },
15559
- children: [
15560
- /* @__PURE__ */ jsxs15(Box14, { sx: { flex: 1, minWidth: 0 }, children: [
15561
- /* @__PURE__ */ jsx21(Typography12, { sx: { fontSize: "0.85rem", fontWeight: 600, color: "hsl(var(--foreground))", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: selectedAgentWorkflow.name }),
15562
- selectedAgentWorkflow.description && /* @__PURE__ */ jsx21(Typography12, { sx: { fontSize: "0.75rem", color: "hsl(var(--muted-foreground))", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: selectedAgentWorkflow.description })
15563
- ] }),
15564
- /* @__PURE__ */ jsx21(
15565
- Button8,
15566
- {
15567
- size: "small",
15568
- startIcon: /* @__PURE__ */ jsx21(Pencil, { size: 14 }),
15569
- onClick: openEditPrompt,
15570
- sx: {
15571
- height: 36,
15572
- border: "1px solid hsl(var(--border))",
15573
- borderRadius: 1.5,
15574
- color: "hsl(var(--foreground))",
15575
- textTransform: "none",
15576
- fontSize: "0.8rem",
15577
- px: 1.5,
15578
- "&:hover": { bgcolor: "hsl(var(--muted))" }
15579
- },
15580
- children: "Edit"
15581
- }
15582
- ),
15583
- /* @__PURE__ */ jsx21(
15584
- Button8,
15585
- {
15586
- size: "small",
15587
- startIcon: /* @__PURE__ */ jsx21(StopCircle, { size: 14 }),
15588
- onClick: () => setStopOpen(true),
15589
- sx: {
15590
- height: 36,
15591
- border: "1px solid hsl(var(--severity-critical, 0 72% 55%) / 0.4)",
15592
- borderRadius: 1.5,
15593
- color: "hsl(var(--severity-critical, 0 72% 55%))",
15594
- textTransform: "none",
15595
- fontSize: "0.8rem",
15596
- px: 1.5,
15597
- "&:hover": { bgcolor: "hsla(var(--severity-critical, 0 72% 55%) / 0.08)" }
15598
- },
15599
- children: "Stop schedule"
15600
- }
15601
- )
15602
- ]
15603
- }
15604
- ),
15605
- isLoading && runs.length === 0 ? /* @__PURE__ */ jsx21(Box14, { sx: { display: "flex", justifyContent: "center", py: 8 }, children: /* @__PURE__ */ jsx21(CircularProgress5, { size: 28, sx: { color: "hsl(var(--primary))" } }) }) : error ? /* @__PURE__ */ jsxs15(Box14, { sx: { display: "flex", flexDirection: "column", alignItems: "center", textAlign: "center", py: 6 }, children: [
15606
- /* @__PURE__ */ jsx21(AlertCircle, { size: 28, style: { color: "hsl(var(--severity-critical, 0 72% 55%))", marginBottom: 8 } }),
15607
- /* @__PURE__ */ jsx21(
15608
- Typography12,
15609
- {
15610
- sx: { color: "hsl(var(--severity-critical, 0 72% 55%))", fontSize: "0.9rem", mb: 1 },
15611
- children: error
15612
- }
15613
- ),
15614
- /* @__PURE__ */ jsx21(
15615
- Button8,
15616
- {
15617
- size: "small",
15618
- onClick: () => fetchRuns(false, ""),
15619
- sx: { color: "hsl(var(--primary))", textTransform: "none" },
15620
- children: "Try again"
15621
- }
15622
- )
15623
- ] }) : filteredRuns.length === 0 ? /* @__PURE__ */ jsxs15(Box14, { sx: { display: "flex", flexDirection: "column", alignItems: "center", textAlign: "center", py: 8 }, children: [
15624
- /* @__PURE__ */ jsx21(
15625
- Activity,
15626
- {
15627
- size: 40,
15628
- style: { color: "hsl(var(--muted-foreground))", marginBottom: 12 }
15629
- }
15630
- ),
15631
- /* @__PURE__ */ jsx21(Typography12, { sx: { color: "hsl(var(--muted-foreground))", fontSize: "0.9rem" }, children: emptyTitle }),
15632
- /* @__PURE__ */ jsx21(
15633
- Typography12,
15634
- {
15635
- sx: { color: "hsl(var(--muted-foreground))", fontSize: "0.8rem", mt: 0.5, opacity: 0.7 },
15636
- children: emptySubtitle
15637
- }
15638
- )
15639
- ] }) : /* @__PURE__ */ jsxs15(Box14, { sx: { display: "flex", flexDirection: "column", gap: 1.5 }, children: [
15640
- filteredRuns.map((run2, idx) => /* @__PURE__ */ jsx21(
15641
- AgentRunRow,
15642
- {
15643
- run: run2,
15644
- onClick: () => {
15645
- setLastOpenedAgentRun(run2.execution_id);
15646
- setLastOpenedRunId(run2.execution_id || null);
15647
- onRunClick?.(run2);
15648
- },
15649
- sx: [
15650
- ...Array.isArray(rowSx) ? rowSx : rowSx ? [rowSx] : [],
15651
- ...run2.execution_id && run2.execution_id === lastOpenedRunId ? [{
15652
- borderColor: "hsl(var(--primary) / 0.55)",
15653
- bgcolor: "hsl(var(--primary) / 0.06)"
15654
- }] : []
15655
- ],
15656
- appIcons,
15657
- onAppClick: (app2) => setAppDrawer(app2),
15658
- apiKey,
15659
- apiBaseUrl,
15660
- orgId,
15661
- abortingIds,
15662
- onAbort: handleAbort
15663
- },
15664
- run2.execution_id || idx
15665
- )),
15666
- hasMore && /* @__PURE__ */ jsx21(Box14, { sx: { display: "flex", justifyContent: "center", mt: 1 }, children: /* @__PURE__ */ jsxs15(
15667
- Button8,
15668
- {
15669
- size: "small",
15670
- onClick: loadMore,
15671
- disabled: isLoading,
15672
- sx: { color: "hsl(var(--primary))", textTransform: "none", fontSize: "0.8rem" },
15673
- children: [
15674
- isLoading ? /* @__PURE__ */ jsx21(CircularProgress5, { size: 14, sx: { mr: 1 } }) : null,
15675
- "Load more"
15676
- ]
15677
- }
15678
- ) })
15679
- ] }),
15680
- /* @__PURE__ */ jsxs15(
15681
- Dialog2,
15682
- {
15683
- open: stopOpen,
15684
- onClose: () => stopLoading ? null : setStopOpen(false),
15685
- slotProps: { paper: { sx: { bgcolor: "hsl(var(--card))", color: "hsl(var(--foreground))", border: "1px solid hsl(var(--border))" } } },
15686
- children: [
15687
- /* @__PURE__ */ jsx21(DialogTitle2, { sx: { fontSize: "1rem", fontWeight: 600 }, children: "Stop schedule?" }),
15688
- /* @__PURE__ */ jsx21(DialogContent2, { children: /* @__PURE__ */ jsxs15(Typography12, { sx: { fontSize: "0.85rem", color: "hsl(var(--muted-foreground))" }, children: [
15689
- 'This will stop "',
15690
- selectedAgentWorkflow?.name,
15691
- '" and delete the scheduled workflow. Past executions remain visible. This cannot be undone.'
15692
- ] }) }),
15693
- /* @__PURE__ */ jsxs15(DialogActions, { sx: { px: 3, pb: 2 }, children: [
15694
- /* @__PURE__ */ jsx21(Button8, { onClick: () => setStopOpen(false), disabled: stopLoading, sx: { textTransform: "none", color: "hsl(var(--muted-foreground))" }, children: "Cancel" }),
15695
- /* @__PURE__ */ jsxs15(
15696
- Button8,
15697
- {
15698
- onClick: confirmStop,
15699
- disabled: stopLoading,
15700
- sx: { textTransform: "none", bgcolor: "hsl(var(--severity-critical, 0 72% 55%))", color: "hsl(var(--primary-foreground))", "&:hover": { bgcolor: "hsla(var(--severity-critical, 0 72% 55%) / 0.9)" } },
15701
- children: [
15702
- stopLoading ? /* @__PURE__ */ jsx21(CircularProgress5, { size: 16, sx: { color: "hsl(var(--primary-foreground))", mr: 1 } }) : null,
15703
- "Stop schedule"
15704
- ]
15705
- }
15706
- )
15707
- ] })
15708
- ]
15709
- }
15710
- ),
15711
- renderAppDetailDrawer ? renderAppDetailDrawer({
15712
- open: !!appDrawer,
15713
- onClose: () => setAppDrawer(null),
15714
- appName: appDrawer?.name || null,
15715
- appId: appDrawer?.id || null,
15716
- activeOrgId: orgId || null,
15717
- globalUrl: globalUrl || apiBaseUrl,
15718
- theme,
15719
- colorMode
15720
- }) : null
15721
- ]
15722
- }
15723
- );
15724
- };
15725
- var AgentActivityList_default = AgentActivityList;
15726
-
15727
- // ../components/docs/tocUtils.ts
15728
- var isTocHeading = (text2) => {
15729
- const norm4 = text2.toLowerCase().replace(/[^a-z0-9]/g, "");
15730
- return norm4 === "tableofcontents" || norm4 === "tableofcontent" || norm4 === "toc";
15731
- };
15732
- var stripMarkdownInline = (value) => value.replace(/!\[[^\]]*\]\([^)]*\)/g, "").replace(/\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/<[^>]+>/g, "").replace(/[`*_~#>]/g, "").replace(/\{#[^}]+\}/g, "").replace(/\s+/g, " ").trim();
15733
- var stripInContentToc = (markdown) => {
15734
- if (!markdown) return "";
15735
- const lines = markdown.split("\n");
15736
- const result = [];
15737
- let inToc = false;
15738
- for (let i = 0; i < lines.length; i++) {
15739
- const line = lines[i];
15740
- const trimmed = line.trim();
15741
- const headingMatch = trimmed.match(/^(#{1,4})\s+(.+)$/);
15742
- if (headingMatch) {
15743
- const headingText = stripMarkdownInline(headingMatch[2]);
15744
- if (isTocHeading(headingText)) {
15745
- inToc = true;
15746
- continue;
15747
- } else if (inToc) {
15748
- inToc = false;
15749
- }
15750
- }
15751
- if (inToc) {
15752
- if (!trimmed || /^[-*+]\s+/.test(trimmed) || /^\d+\.\s+/.test(trimmed) || /^[-*_]{3,}$/.test(trimmed) || /^>/.test(trimmed) || /^<ul[\s\S]*?<\/ul>$/i.test(trimmed)) {
15753
- continue;
15754
- } else {
15755
- inToc = false;
15756
- }
15757
- }
15758
- result.push(line);
15759
- }
15760
- return result.join("\n");
15761
- };
15762
-
15763
- // ../lib/docGroups.ts
15764
- var DOC_GROUPS = [
15765
- {
15766
- id: "usability",
15767
- label: "Usability",
15768
- description: "Platform overview, getting started, tenants, features, and API reference.",
15769
- slugs: ["getting_started", "getting-started", "about", "tenants", "features", "api"]
15770
- },
15771
- {
15772
- id: "automation",
15773
- label: "Automation",
15774
- description: "Workflows, apps, AI agents, and playbook automations.",
15775
- slugs: ["workflows", "apps", "ai"]
15776
- },
15777
- {
15778
- id: "security",
15779
- label: "Security",
15780
- description: "Incident triage, vulnerability management, host posture, and response.",
15781
- slugs: ["incidents", "vulnerabilities", "monitors", "host-monitors", "detection", "sigma"]
15782
- },
15783
- {
15784
- id: "infrastructure",
15785
- label: "Infrastructure",
15786
- description: "Architecture, configuration, extensions, and troubleshooting.",
15787
- slugs: ["architecture", "configuration", "extensions", "troubleshooting"]
15788
- }
15789
- ];
15790
- var DOC_LABEL_OVERRIDES = {
15791
- ai: "Agents & AI",
15792
- api: "API Reference",
15793
- getting_started: "Getting Started",
15794
- incidents: "Incidents & Cases",
15795
- vulnerabilities: "Vulnerabilities",
15796
- monitors: "Host Monitors",
15797
- "host-monitors": "Host Monitors",
15798
- about: "About Shuffle",
15799
- tenants: "Tenants & Multi-Tenancy",
15800
- features: "Features Overview",
15801
- workflows: "Workflows",
15802
- apps: "Apps & Integrations",
15803
- extensions: "Extensions",
15804
- architecture: "Architecture",
15805
- configuration: "Configuration",
15806
- troubleshooting: "Troubleshooting"
15807
- };
15808
- var getDocGroup = (slug) => {
15809
- const usabilityGroup = DOC_GROUPS.find((g) => g.id === "usability") || DOC_GROUPS[0];
15810
- if (!slug || slug === "index") return usabilityGroup;
15811
- const clean = slug.toLowerCase().replace(/_+/g, "-");
15812
- for (const group of DOC_GROUPS) {
15813
- if (group.slugs.includes(clean)) {
15814
- return group;
15815
- }
15816
- }
15817
- return usabilityGroup;
15818
- };
15819
- var getDocDisplayLabel = (slug, fallbackName) => {
15820
- const clean = (slug || "").toLowerCase().replace(/_+/g, "-");
15821
- if (DOC_LABEL_OVERRIDES[clean]) {
15822
- return DOC_LABEL_OVERRIDES[clean];
15823
- }
15824
- const raw = fallbackName || slug || "";
15825
- return raw.replace(/[_-]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
15826
- };
15827
-
15828
- // ../components/docs/docGroups.ts
15829
- var groupDocsCache = /* @__PURE__ */ new Map();
15830
- var getCachedGroupDocs = (groupId, folder) => {
15831
- const cacheKey2 = `${folder || "docs"}:${groupId}`;
15832
- const cached = groupDocsCache.get(cacheKey2);
15833
- if (!cached) return [];
15834
- return Object.entries(cached).map(([slug, content]) => ({
15835
- slug,
15836
- title: getDocDisplayLabel(slug),
15837
- content
15838
- }));
15839
- };
15840
-
15841
- // ../lib/incidentPromptContext.ts
15842
- var GLOBAL_INCIDENT_KEY = "__shuffleActiveIncidentContext";
15843
- var getGlobalScope = () => {
15844
- if (typeof globalThis !== "undefined") return globalThis;
15845
- if (typeof window !== "undefined") return window;
15846
- if (typeof global !== "undefined") return global;
15847
- return null;
15848
- };
15849
- var getActiveIncidentPromptContext = () => {
15850
- const scope2 = getGlobalScope();
15851
- if (!scope2) return null;
15852
- const info = scope2[GLOBAL_INCIDENT_KEY];
15853
- if (info && typeof info === "object") {
15854
- return info;
15855
- }
15856
- return null;
15857
- };
15858
- var INCIDENT_PROMPT_DELIMITER_START = "=== INCIDENT CONTEXT ===";
15859
- var INCIDENT_PROMPT_DELIMITER_END = "=== END INCIDENT CONTEXT ===";
15860
- var formatObservableType = (type) => {
15861
- if (!type) return "Indicator";
15862
- const clean = type.toLowerCase().trim();
15863
- switch (clean) {
15864
- case "ip":
15865
- case "ipv4":
15866
- case "ip_address":
15867
- return "IPv4";
15868
- case "ipv6":
15869
- return "IPv6";
15870
- case "hash":
15871
- case "sha256":
15872
- case "file_hash":
15873
- return "SHA256";
15874
- case "md5":
15875
- return "MD5";
15876
- case "sha1":
15877
- return "SHA1";
15878
- case "hostname":
15879
- case "host":
15880
- case "endpoint":
15881
- return "Hostname";
15882
- case "domain":
15883
- return "Domain";
15884
- case "url":
15885
- return "URL";
15886
- case "user":
15887
- case "username":
15888
- case "account":
15889
- return "User";
15890
- case "email":
15891
- return "Email";
15892
- case "process":
15893
- case "process_name":
15894
- return "Process";
15895
- case "file":
15896
- case "filename":
15897
- return "File";
15898
- default:
15899
- return type.charAt(0).toUpperCase() + type.slice(1);
15900
- }
15901
- };
15902
- var isIncidentInjectedPrompt = (prompt) => {
15903
- if (!prompt || typeof prompt !== "string") return false;
15904
- return prompt.includes(INCIDENT_PROMPT_DELIMITER_START) || prompt.includes("OPERATIONAL DIRECTIVES:") || prompt.includes("INCIDENT OBSERVABLES / IOCs:");
15905
- };
15906
- var extractCleanIncidentPrompt = (prompt) => {
15907
- if (!prompt || typeof prompt !== "string") return "";
15908
- const trimmed = prompt.trim();
15909
- const taskMatch = trimmed.match(/TASK TO EXECUTE:\s*\n\s*-\s*Task:\s*([^\n]+)/i);
15910
- if (taskMatch && taskMatch[1]?.trim()) {
15911
- return `Task: ${taskMatch[1].trim()}`;
15912
- }
15913
- const legacyMatch = trimmed.match(/\n\s*Task:\s*([^\n]+)/i);
15914
- if (legacyMatch && legacyMatch[1]?.trim()) {
15915
- return `Task: ${legacyMatch[1].trim()}`;
15916
- }
15917
- if (trimmed.includes(INCIDENT_PROMPT_DELIMITER_START)) {
15918
- const before = trimmed.split(INCIDENT_PROMPT_DELIMITER_START)[0]?.trim();
15919
- if (before) return before;
15920
- }
15921
- return trimmed;
15922
- };
15923
- var composeIncidentPromptInput = (rawInput, context) => {
15924
- if (!rawInput || typeof rawInput !== "string") return "";
15925
- const trimmed = rawInput.trim();
15926
- if (isIncidentInjectedPrompt(trimmed)) {
15927
- return trimmed;
15928
- }
15929
- const ctx = context || getActiveIncidentPromptContext();
15930
- if (!ctx || !ctx.id) {
15931
- return trimmed;
15932
- }
15933
- const incidentRef = ctx.id ? `#${ctx.id}` : "";
15934
- const incidentTitle = ctx.title?.trim() || "Incident";
15935
- const severity = ctx.severity || "Medium";
15936
- const status = ctx.status || "In Progress";
15937
- const description = ctx.description?.trim() || "No description provided.";
15938
- const seenObservables = /* @__PURE__ */ new Set();
15939
- const formattedObservables = [];
15940
- if (Array.isArray(ctx.observables)) {
15941
- for (const obs of ctx.observables) {
15942
- if (!obs || obs.archived || !obs.value?.trim()) continue;
15943
- const val = obs.value.trim();
15944
- const typeLabel = formatObservableType(obs.type);
15945
- const key = `${typeLabel.toLowerCase()}:${val.toLowerCase()}`;
15946
- if (seenObservables.has(key)) continue;
15947
- seenObservables.add(key);
15948
- formattedObservables.push(`- ${typeLabel}: ${val}`);
15949
- }
15950
- }
15951
- const observablesBlock = formattedObservables.length > 0 ? formattedObservables.slice(0, 50).join("\n") : "- None recorded yet";
15952
- return `${trimmed}
15953
-
15954
- ${INCIDENT_PROMPT_DELIMITER_START}
15955
- INCIDENT CONTEXT:
15956
- - Incident: ${incidentRef} ("${incidentTitle}")
15957
- - Severity: ${severity} | Status: ${status}
15958
- - Summary: ${description}
15959
-
15960
- INCIDENT OBSERVABLES / IOCs:
15961
- ${observablesBlock}
15962
- ${INCIDENT_PROMPT_DELIMITER_END}
15963
-
15964
- DIRECTIVES:
15965
- - Relevant incident details and observables are provided above. Do not call "get_incident" unless raw external logs are required.`;
15966
- };
15967
-
15968
- // ../lib/usecasePromptContext.ts
15969
- var GLOBAL_KEY = "__shuffleActiveUsecaseContext";
15970
- var GLOBAL_OVERVIEW_KEY = "__shuffleActiveUsecasesOverviewContext";
15971
- var USECASE_CONTEXT_EVENT = "shuffle-usecase-context-changed";
15972
- var scope = () => typeof globalThis !== "undefined" ? globalThis : null;
15973
- var getActiveUsecasePromptContext = () => {
15974
- const v = scope()?.[GLOBAL_KEY];
15975
- return v && typeof v === "object" ? v : null;
15976
- };
15977
- var getActiveUsecasesOverviewContext = () => {
15978
- const v = scope()?.[GLOBAL_OVERVIEW_KEY];
15979
- return v && typeof v === "object" ? v : null;
15980
- };
15981
- var humanizeUsecaseId = (id) => id.replace(/_\d+$/, "").replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()).trim();
15982
- var getUsecaseDisplayName = (flowId) => {
15983
- const ctx = getActiveUsecasePromptContext();
15984
- if (ctx?.label) return ctx.label;
15985
- return flowId ? humanizeUsecaseId(flowId) : "Usecases";
15986
- };
15987
- var USECASE_PROMPT_START = "=== USECASE CONTEXT ===";
15988
- var USECASE_PROMPT_END = "=== END USECASE CONTEXT ===";
15989
- var isUsecaseInjectedPrompt = (prompt) => {
15990
- if (!prompt || typeof prompt !== "string") return false;
15991
- return prompt.includes(USECASE_PROMPT_START);
15992
- };
15993
- var extractCleanUsecasePrompt = (prompt) => {
15994
- if (!prompt || typeof prompt !== "string") return "";
15995
- if (!isUsecaseInjectedPrompt(prompt)) return prompt;
15996
- return prompt.replace(/\n*=== USECASE CONTEXT ===[\s\S]*?(?:=== END USECASE CONTEXT ===|$)/g, "").trim();
14402
+ var extractCleanUsecasePrompt = (prompt) => {
14403
+ if (!prompt || typeof prompt !== "string") return "";
14404
+ if (!isUsecaseInjectedPrompt(prompt)) return prompt;
14405
+ return prompt.replace(/\n*=== USECASE CONTEXT ===[\s\S]*?(?:=== END USECASE CONTEXT ===|$)/g, "").trim();
15997
14406
  };
15998
14407
  var USECASE_REQUIRED_APIS = [
15999
14408
  "GET /api/v1/workflows/usecases (list all usecase definitions, steps, source/target categories, and workflow templates)",
@@ -16340,392 +14749,1984 @@ var DEFAULT_USECASE_CATALOG = [
16340
14749
  automationLabel: "Block Threat Intel IOCs",
16341
14750
  automationCategory: "intel",
16342
14751
  automationArea: "Threat Intelligence (TIP)",
16343
- summary: "Automatically feeds high-confidence Threat Intelligence indicators directly into network firewalls and perimeter blocklists."
14752
+ summary: "Automatically feeds high-confidence Threat Intelligence indicators directly into network firewalls and perimeter blocklists."
14753
+ },
14754
+ {
14755
+ id: "intel_to_edr_1",
14756
+ slug: "push_detections",
14757
+ label: "Push detections",
14758
+ phase: "Step 3: Agents & Response Actions",
14759
+ source: "threat_intel",
14760
+ target: "edr",
14761
+ automationLabel: "Push Threat Intel to EDR",
14762
+ automationCategory: "intel",
14763
+ automationArea: "Threat Intelligence (TIP)",
14764
+ summary: "Pushes file hashes and C2 indicators from Threat Intelligence feeds into EDR custom IOC prevention and hunting lists."
14765
+ }
14766
+ ];
14767
+ var USECASE_TOOL_CATEGORIES = [
14768
+ "case_management (Cases / SOAR): Shuffle Security Incidents, TheHive, Jira, ServiceNow, PagerDuty, Linear",
14769
+ "siem (SIEM & Log Analytics): Wazuh, Splunk, Elastic Security, Microsoft Sentinel, QRadar, Google Chronicle, Graylog",
14770
+ "edr (EDR / Endpoint): CrowdStrike Falcon, SentinelOne, Microsoft Defender for Endpoint, Wazuh, Carbon Black, Velociraptor, Shuffle Host-Monitors",
14771
+ "email (Email Security): Microsoft Office 365, Google Workspace / Gmail, IMAP/SMTP, Proofpoint, Mimecast",
14772
+ "threat_intel (Threat Intelligence): MISP, OpenCTI, VirusTotal, AbuseIPDB, Shodan, GreyNoise, AlienVault OTX, URLhaus",
14773
+ "asset_management (Assets & Vulnerabilities): Shuffle Assets & Vulnerabilities, Tenable, Qualys, Rapid7, NetBox, Snipe-IT, ServiceNow CMDB",
14774
+ "iam (Identity & Access Management): Okta, Microsoft Entra ID (Azure AD), Active Directory LDAP, Google Workspace Admin, Duo, Keycloak",
14775
+ "network (Network & Firewalls): Palo Alto PAN-OS, Fortinet FortiGate, Cloudflare, Suricata, Zeek, Zscaler, Cisco Meraki/ASA",
14776
+ "cloud (Cloud Security): AWS (GuardDuty, CloudTrail, EC2, IAM), Azure Security Center, Google Cloud SCC, Wiz, Prisma Cloud",
14777
+ "communication (Notifications & On-Call): Slack, Microsoft Teams, Email, PagerDuty, Opsgenie, Discord, Shuffle Mobile App"
14778
+ ];
14779
+ var USECASE_ARCHITECTURE_OVERVIEW = [
14780
+ "How Usecases Work in Shuffle Security:",
14781
+ " 1. Three-Phase Operational Pipeline:",
14782
+ " - Step 1: Ingest & Tool Setup (`ingest`) \u2014 Connect external security sources (SIEM, EDR, Email, Threat Intel, Vulnerability Scanners, Host-Monitors, Network, Cloud) or the built-in Ingestion Webhook to normalize alerts and findings into Shuffle Security datastores (`shuffle-security_incidents`, `shuffle-security_vulnerabilities`, `shuffle-security_assets`, `shuffle-security_threat_intel`).",
14783
+ " - Step 2: Context & Correlation (`correlation`) \u2014 Automatically evaluate Incident Routing Rules, enrich IOCs against Threat Intel, attach Asset/IAM context, and correlate Vulnerabilities with active incidents when records enter the datastore.",
14784
+ " - Step 3: Agents & Response Actions (`response`) \u2014 Trigger autonomous AI Incident Handling agents (`Run AI Agent`), execute host/network/IAM/email/cloud containment, synchronize external tickets (`Forward Tickets`), send Slack/Teams/Email notifications, and escalate to on-call schedules (`Schedules & Phone Notifications`).",
14785
+ " 2. Enabling & Configuring a Usecase:",
14786
+ " - Standard workflow usecases are generated/enabled via `POST /api/v2/workflows/generate` with `{ label, category, app_name }` after the user authenticates their chosen source/target apps (`GET /api/v1/apps/authentication`).",
14787
+ " - Datastore-driven usecases (Incident Routing Rules, Enrichment, Asset context, Vulnerability Correlation, AI Incident Handling) bind to datastore categories (`shuffle-security_incidents`, `shuffle-security_vulnerabilities`, `shuffle-security_assets`, `shuffle-security_threat_intel`) via `POST /api/v2/datastore/automate` and run automatically whenever matching records are created or updated.",
14788
+ " - Special built-in usecases also have dedicated management views: Incident Routing Rules (`/incidents?settings=routing_rules`), AI Incident Handling (`/incidents?modal=ai_agent_prompts`), Host-Monitors (`/monitors?add_host=true`), Threat Intel Feeds (`/threat-intel/feeds`), Schedules & Phone Notifications (`/Detection?tab=schedules`), and Vulnerability Management (`/vulnerabilities`).",
14789
+ " - Any external source can push JSON events directly into Shuffle Security via the tenant Ingestion Webhook (`POST /api/v1/hooks/webhook_<id>`).",
14790
+ " 3. How You (the Agent) Should Help the User:",
14791
+ " - Recommend which usecases to enable next based on the tenant's currently enabled usecases and connected/authenticated tools.",
14792
+ " - Explain step-by-step how to configure or troubleshoot any usecase, including required app credentials, webhook setup, or datastore triggers.",
14793
+ " - Inspect or generate workflows, check authentication status, and verify datastore automations using the Shuffle APIs below when requested."
14794
+ ].join("\n");
14795
+ var list = (rec) => Object.entries(rec || {}).filter(([, v]) => Array.isArray(v) && v.length > 0).map(([k, v]) => ` - ${k}: ${v.slice(0, 25).join(", ")}${v.length > 25 ? ` (+${v.length - 25} more)` : ""}`).join("\n");
14796
+ var findCatalogUsecase = (flowId) => {
14797
+ if (!flowId) return void 0;
14798
+ const norm4 = flowId.trim().toLowerCase().replace(/[\s-]+/g, "_");
14799
+ return DEFAULT_USECASE_CATALOG.find(
14800
+ (u) => u.id.toLowerCase() === norm4 || u.slug.toLowerCase() === norm4 || u.label.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "") === norm4
14801
+ );
14802
+ };
14803
+ var composeUsecasePromptInput = (rawInput, flowId) => {
14804
+ const trimmed = (rawInput || "").trim();
14805
+ if (!trimmed || trimmed.includes(USECASE_PROMPT_START)) return trimmed;
14806
+ const ctx = getActiveUsecasePromptContext();
14807
+ const overviewCtx = getActiveUsecasesOverviewContext();
14808
+ const catalogMatch = findCatalogUsecase(ctx?.id || flowId);
14809
+ const lines = [USECASE_PROMPT_START];
14810
+ if (ctx) {
14811
+ lines.push(`Active View: Specific Usecase Drawer (/usecases/${flowId || ctx.id})`);
14812
+ lines.push(`Usecase: ${ctx.label} (id: ${ctx.id})`);
14813
+ if (ctx.source || ctx.target || catalogMatch) {
14814
+ lines.push(`Data flow: ${ctx.source || catalogMatch?.source || "unknown"} -> ${ctx.target || catalogMatch?.target || "unknown"}`);
14815
+ }
14816
+ if (ctx.phase || catalogMatch?.phase) lines.push(`Phase: ${ctx.phase || catalogMatch?.phase}`);
14817
+ if (ctx.automationArea || catalogMatch?.automationArea) {
14818
+ lines.push(`Automation area: ${ctx.automationArea || catalogMatch?.automationArea}`);
14819
+ }
14820
+ if (ctx.tags?.length) lines.push(`Tags: ${ctx.tags.join(", ")}`);
14821
+ if (typeof ctx.enabled === "boolean") lines.push(`Status for this tenant: ${ctx.enabled ? "Enabled" : "Disabled"}`);
14822
+ if (ctx.healthStatus) lines.push(`Health / diagnostics: ${ctx.healthStatus}`);
14823
+ if (typeof ctx.manualVerification === "boolean") {
14824
+ lines.push(`Verification mode: ${ctx.manualVerification ? "Manual / UI-configured flow" : "Automated workflow generation via POST /api/v2/workflows/generate"}`);
14825
+ }
14826
+ if (ctx.customAction || catalogMatch?.customRoute) {
14827
+ const route = ctx.customAction?.href || ctx.customAction?.url || catalogMatch?.customRoute;
14828
+ const actionLabel = ctx.customAction?.label || "Open configuration view";
14829
+ lines.push(`Dedicated UI action: ${actionLabel}${route ? ` (${route})` : ""}${ctx.customAction?.description ? ` \u2014 ${ctx.customAction.description}` : ""}`);
14830
+ }
14831
+ if (ctx.automationLabel || catalogMatch?.automationLabel) {
14832
+ const autoCat = ctx.automationCategory || catalogMatch?.automationCategory;
14833
+ lines.push(`Automation label: ${ctx.automationLabel || catalogMatch?.automationLabel}${autoCat ? ` (category: ${autoCat})` : ""}`);
14834
+ }
14835
+ if (ctx.description || catalogMatch?.summary) {
14836
+ lines.push(`Description: ${ctx.description || catalogMatch?.summary}`);
14837
+ }
14838
+ if (ctx.agenticDescription) lines.push(`Agentic behavior: ${ctx.agenticDescription}`);
14839
+ const connected = list(ctx.connectedApps);
14840
+ if (connected) {
14841
+ lines.push(`Connected tools with validated authentication:
14842
+ ${connected}`);
14843
+ } else {
14844
+ lines.push("Connected tools with validated authentication: None detected yet for this usecase category.");
14845
+ }
14846
+ const apps = list(ctx.categoryApps);
14847
+ if (apps) lines.push(`Supported apps per category:
14848
+ ${apps}`);
14849
+ if (ctx.linkedWorkflows?.length) {
14850
+ lines.push(`Linked workflows: ${ctx.linkedWorkflows.join(", ")}`);
14851
+ } else {
14852
+ lines.push("Linked workflows: None linked yet.");
14853
+ }
14854
+ lines.push("");
14855
+ lines.push(USECASE_ARCHITECTURE_OVERVIEW);
14856
+ } else if (flowId) {
14857
+ lines.push(`Active View: Specific Usecase (/usecases/${flowId})`);
14858
+ if (catalogMatch) {
14859
+ lines.push(`Usecase: ${catalogMatch.label} (id: ${catalogMatch.id}, slug: ${catalogMatch.slug})`);
14860
+ lines.push(`Phase: ${catalogMatch.phase}`);
14861
+ lines.push(`Data flow: ${catalogMatch.source} -> ${catalogMatch.target}`);
14862
+ if (catalogMatch.automationArea) lines.push(`Automation area: ${catalogMatch.automationArea}`);
14863
+ if (catalogMatch.customRoute) lines.push(`Dedicated configuration route: ${catalogMatch.customRoute}`);
14864
+ lines.push(`Automation label: ${catalogMatch.automationLabel}${catalogMatch.automationCategory ? ` (category: ${catalogMatch.automationCategory})` : ""}`);
14865
+ lines.push(`Description: ${catalogMatch.summary}`);
14866
+ } else {
14867
+ lines.push(`Usecase id/slug: ${flowId} (${humanizeUsecaseId(flowId)})`);
14868
+ }
14869
+ lines.push("");
14870
+ lines.push(USECASE_ARCHITECTURE_OVERVIEW);
14871
+ } else {
14872
+ lines.push("Page: Usecases overview (/usecases \u2014 interactive 3-phase pipeline of all security automation usecases in Shuffle Security).");
14873
+ if (overviewCtx) {
14874
+ lines.push("");
14875
+ lines.push("Current Tenant Live State on /usecases:");
14876
+ if (overviewCtx.enabledUsecases?.length) {
14877
+ lines.push(` - Enabled usecases (${overviewCtx.enabledUsecases.length}): ${overviewCtx.enabledUsecases.map((u) => `${u.label} (${u.id}: ${u.source} -> ${u.target})`).join(", ")}`);
14878
+ } else {
14879
+ lines.push(" - Enabled usecases: None enabled yet.");
14880
+ }
14881
+ if (overviewCtx.disabledUsecases?.length) {
14882
+ lines.push(` - Disabled / available usecases (${overviewCtx.disabledUsecases.length}): ${overviewCtx.disabledUsecases.map((u) => `${u.label} (${u.id})`).join(", ")}`);
14883
+ }
14884
+ if (overviewCtx.validatedApps?.length) {
14885
+ lines.push(` - Connected & authenticated apps: ${overviewCtx.validatedApps.slice(0, 30).join(", ")}`);
14886
+ } else {
14887
+ lines.push(" - Connected & authenticated apps: None authenticated yet.");
14888
+ }
14889
+ if (overviewCtx.validatedCategories?.length) {
14890
+ lines.push(` - Authenticated tool categories: ${overviewCtx.validatedCategories.join(", ")}`);
14891
+ }
14892
+ if (overviewCtx.activeWorkflows?.length) {
14893
+ lines.push(` - Active tenant workflows (${overviewCtx.activeWorkflows.length}): ${overviewCtx.activeWorkflows.slice(0, 20).join(", ")}${overviewCtx.activeWorkflows.length > 20 ? ` (+${overviewCtx.activeWorkflows.length - 20} more)` : ""}`);
14894
+ }
14895
+ if (overviewCtx.activeFilters) {
14896
+ const f = overviewCtx.activeFilters;
14897
+ const activeFilterParts = [
14898
+ f.phase && f.phase !== "all" ? `phase=${f.phase}` : "",
14899
+ f.category && f.category !== "all" ? `category=${f.category}` : "",
14900
+ f.area && f.area !== "all" ? `area=${f.area}` : "",
14901
+ f.tag && f.tag !== "all" ? `tag=${f.tag}` : "",
14902
+ f.search ? `search="${f.search}"` : ""
14903
+ ].filter(Boolean);
14904
+ if (activeFilterParts.length > 0) {
14905
+ lines.push(` - User's active UI filters: ${activeFilterParts.join(", ")}`);
14906
+ }
14907
+ }
14908
+ }
14909
+ lines.push("");
14910
+ lines.push(USECASE_ARCHITECTURE_OVERVIEW);
14911
+ lines.push("");
14912
+ lines.push(`Supported Tool Categories:
14913
+ ${USECASE_TOOL_CATEGORIES.map((c) => ` - ${c}`).join("\n")}`);
14914
+ lines.push("");
14915
+ lines.push("Complete Shuffle Security Usecases Catalog (All 3 Phases):");
14916
+ for (const item of DEFAULT_USECASE_CATALOG) {
14917
+ lines.push(
14918
+ ` - [${item.phase}] ${item.label} (id: ${item.id}, slug: /usecases/${item.slug}, flow: ${item.source} -> ${item.target}, automation: ${item.automationLabel}${item.automationCategory ? `, category: ${item.automationCategory}` : ""}${item.customRoute ? `, route: ${item.customRoute}` : ""}): ${item.summary}`
14919
+ );
14920
+ }
14921
+ }
14922
+ lines.push("");
14923
+ lines.push(`Required Shuffle APIs:
14924
+ ${USECASE_REQUIRED_APIS.map((a) => ` - ${a}`).join("\n")}`);
14925
+ lines.push(USECASE_PROMPT_END);
14926
+ return `${trimmed}
14927
+
14928
+ ${lines.join("\n")}`;
14929
+ };
14930
+
14931
+ // ../lib/docsPromptContext.ts
14932
+ var DOC_SELECTION_EVENT = "shuffle:doc-selection-change";
14933
+ var GLOBAL_DOC_SELECTION_KEY = "__shuffleActiveDocSelection";
14934
+ var truncateDocSelectionPreview = (text2, maxChars = 36) => {
14935
+ const clean = (text2 || "").replace(/\s+/g, " ").trim();
14936
+ if (!clean) return "";
14937
+ if (clean.length <= maxChars) return clean;
14938
+ return `${clean.slice(0, maxChars).trimEnd()}\u2026`;
14939
+ };
14940
+ var GLOBAL_DOC_KEY = "__shuffleActiveDocInfo";
14941
+ var getGlobalScope2 = () => {
14942
+ if (typeof globalThis !== "undefined") return globalThis;
14943
+ if (typeof window !== "undefined") return window;
14944
+ if (typeof global !== "undefined") return global;
14945
+ return null;
14946
+ };
14947
+ var setActiveDocSelectedText = (selectedText) => {
14948
+ const scope2 = getGlobalScope2();
14949
+ if (!scope2) return;
14950
+ const cleaned = (selectedText || "").trim();
14951
+ if (cleaned) {
14952
+ scope2[GLOBAL_DOC_SELECTION_KEY] = cleaned;
14953
+ if (scope2[GLOBAL_DOC_KEY] && typeof scope2[GLOBAL_DOC_KEY] === "object") {
14954
+ scope2[GLOBAL_DOC_KEY].selectedText = cleaned;
14955
+ }
14956
+ } else {
14957
+ delete scope2[GLOBAL_DOC_SELECTION_KEY];
14958
+ if (scope2[GLOBAL_DOC_KEY] && typeof scope2[GLOBAL_DOC_KEY] === "object") {
14959
+ delete scope2[GLOBAL_DOC_KEY].selectedText;
14960
+ }
14961
+ }
14962
+ if (typeof window !== "undefined") {
14963
+ try {
14964
+ window.dispatchEvent(
14965
+ new CustomEvent(DOC_SELECTION_EVENT, {
14966
+ detail: { selectedText: cleaned || null }
14967
+ })
14968
+ );
14969
+ } catch {
14970
+ }
14971
+ }
14972
+ };
14973
+ var getActiveDocSelectedText = () => {
14974
+ const scope2 = getGlobalScope2();
14975
+ if (!scope2) return null;
14976
+ const sel = scope2[GLOBAL_DOC_SELECTION_KEY] || scope2[GLOBAL_DOC_KEY]?.selectedText;
14977
+ return typeof sel === "string" && sel.trim().length > 0 ? sel.trim() : null;
14978
+ };
14979
+ var getActiveDocPromptContext = () => {
14980
+ const scope2 = getGlobalScope2();
14981
+ if (!scope2) return null;
14982
+ const info = scope2[GLOBAL_DOC_KEY];
14983
+ if (info && typeof info === "object") {
14984
+ return info;
14985
+ }
14986
+ return null;
14987
+ };
14988
+ var isDocsRoute = (pathname) => {
14989
+ if (!pathname) return false;
14990
+ return pathname === "/docs" || pathname.startsWith("/docs/") || pathname === "/legal" || pathname.startsWith("/legal/") || pathname === "/articles" || pathname.startsWith("/articles/");
14991
+ };
14992
+ var DOC_PROMPT_DELIMITER_START = "--- BEGIN DOCUMENTATION ---";
14993
+ var DOC_PROMPT_DELIMITER_END = "--- END DOCUMENTATION ---";
14994
+ var DOC_PROMPT_PREFIX_REGEX = /^Answer the users? question about (?:["']?)(.*?)(?:["']?) based on(?: on)? the following (?:documentation )?content:/i;
14995
+ var sanitizeDocMarkdown = (markdown, maxChars = 35e3) => {
14996
+ if (!markdown || typeof markdown !== "string") return "";
14997
+ let cleaned = markdown;
14998
+ cleaned = cleaned.replace(/^---\s*\r?\n[\s\S]*?\r?\n---\s*(?:\r?\n|$)/, "");
14999
+ cleaned = stripInContentToc(cleaned);
15000
+ cleaned = cleaned.replace(/<!--[\s\S]*?-->/g, "");
15001
+ cleaned = cleaned.replace(/<(script|style|iframe)[^>]*>[\s\S]*?<\/\1>/gi, "");
15002
+ cleaned = cleaned.replace(/data:image\/[a-zA-Z0-9+.-]+;base64,[A-Za-z0-9+/=]+/g, "[embedded image]");
15003
+ cleaned = cleaned.replace(/\n{3,}/g, "\n\n");
15004
+ cleaned = cleaned.trim();
15005
+ if (cleaned.length > maxChars) {
15006
+ const cut = cleaned.lastIndexOf("\n\n", maxChars);
15007
+ const splitIndex = cut > maxChars * 0.7 ? cut : maxChars;
15008
+ cleaned = `${cleaned.slice(0, splitIndex).trim()}
15009
+
15010
+ [Documentation content truncated for length...]`;
15011
+ }
15012
+ return cleaned;
15013
+ };
15014
+ var isDocInjectedPrompt = (prompt) => {
15015
+ if (!prompt || typeof prompt !== "string") return false;
15016
+ return prompt.includes(DOC_PROMPT_DELIMITER_START) || DOC_PROMPT_PREFIX_REGEX.test(prompt);
15017
+ };
15018
+ var stripSystemNotes = (text2) => text2.replace(/\s*\[System note:[^\]]*\]/g, "");
15019
+ var extractCleanDisplayPrompt = (prompt) => {
15020
+ if (!prompt || typeof prompt !== "string") return "";
15021
+ let trimmed = stripSystemNotes(prompt).trim();
15022
+ if (isUsecaseInjectedPrompt(trimmed)) {
15023
+ trimmed = extractCleanUsecasePrompt(trimmed);
15024
+ }
15025
+ if (isIncidentInjectedPrompt(trimmed)) {
15026
+ return extractCleanIncidentPrompt(trimmed);
15027
+ }
15028
+ if (!isDocInjectedPrompt(trimmed)) return trimmed;
15029
+ if (trimmed.includes(DOC_PROMPT_DELIMITER_END)) {
15030
+ const parts = trimmed.split(DOC_PROMPT_DELIMITER_END);
15031
+ const after = parts[parts.length - 1]?.trim() || "";
15032
+ const cleaned = after.replace(/^(?:User\s+Question:\s*|Question:\s*)/i, "").trim();
15033
+ if (cleaned) return cleaned;
15034
+ }
15035
+ if (DOC_PROMPT_PREFIX_REGEX.test(trimmed)) {
15036
+ const paragraphs = trimmed.split(/\n\s*\n/).filter(Boolean);
15037
+ if (paragraphs.length > 1) {
15038
+ const last = paragraphs[paragraphs.length - 1].trim();
15039
+ const cleaned = last.replace(/^(?:User\s+Question:\s*|Question:\s*)/i, "").trim();
15040
+ if (cleaned) return cleaned;
15041
+ }
15042
+ }
15043
+ return trimmed;
15044
+ };
15045
+ var normalizeDocPromptTitle = (title) => {
15046
+ const t = (title || "").trim();
15047
+ if (!t || t.toLowerCase() === "index" || t.toLowerCase() === "documentation" || t.toLowerCase() === "docs") {
15048
+ return "Shuffle documentation";
15049
+ }
15050
+ const lower = t.toLowerCase();
15051
+ if (lower === "about" || lower === "about shuffle" || lower === "about us" || lower === "about-shuffle" || lower === "about-us") {
15052
+ return "Shuffle";
15053
+ }
15054
+ if (/^about\s+/i.test(t)) {
15055
+ const stripped = t.replace(/^about\s+/i, "").trim();
15056
+ return stripped || "Shuffle";
15057
+ }
15058
+ return t;
15059
+ };
15060
+ var composeDocPromptInput = (userQuestion, pathname, fallbackTitle, overrideDocInfo) => {
15061
+ const trimmedQuestion = (userQuestion || "").trim();
15062
+ if (!trimmedQuestion) return "";
15063
+ if (isDocInjectedPrompt(trimmedQuestion)) {
15064
+ return trimmedQuestion;
15065
+ }
15066
+ const activeDoc = overrideDocInfo ?? getActiveDocPromptContext();
15067
+ const rawTitle = (activeDoc?.title || fallbackTitle || getGlobalScope2()?.__shuffleActiveEntityTitle || "Shuffle documentation").trim();
15068
+ const title = normalizeDocPromptTitle(rawTitle);
15069
+ const groupLabel = activeDoc?.groupLabel;
15070
+ const groupId = activeDoc?.groupId;
15071
+ const currentSlug = (activeDoc?.slug || "").toLowerCase().replace(/_+/g, "-");
15072
+ let categoryDocs = activeDoc?.groupDocs || [];
15073
+ if (categoryDocs.length === 0 && groupId) {
15074
+ categoryDocs = getCachedGroupDocs(groupId);
15075
+ }
15076
+ if (categoryDocs.length === 0 && currentSlug) {
15077
+ const groupDef2 = getDocGroup(currentSlug);
15078
+ if (groupDef2) {
15079
+ categoryDocs = getCachedGroupDocs(groupDef2.id);
15080
+ }
15081
+ }
15082
+ const groupDef = currentSlug ? getDocGroup(currentSlug) : null;
15083
+ const categoryName = groupLabel || groupDef?.label || title;
15084
+ const rawMarkdown = activeDoc?.content || "";
15085
+ const renderedDocs = [];
15086
+ if (categoryDocs.length > 0) {
15087
+ for (const doc of categoryDocs) {
15088
+ const docSlugClean = (doc.slug || "").toLowerCase().replace(/_+/g, "-");
15089
+ const isCurrent = docSlugClean === currentSlug;
15090
+ const contentToUse = isCurrent && rawMarkdown ? rawMarkdown : doc.content;
15091
+ renderedDocs.push({
15092
+ title: doc.title || title,
15093
+ slug: doc.slug,
15094
+ content: sanitizeDocMarkdown(contentToUse, 16e3),
15095
+ isCurrent
15096
+ });
15097
+ }
15098
+ } else if (rawMarkdown) {
15099
+ renderedDocs.push({
15100
+ title,
15101
+ slug: activeDoc?.slug,
15102
+ content: sanitizeDocMarkdown(rawMarkdown, 3e4),
15103
+ isCurrent: true
15104
+ });
15105
+ }
15106
+ let docSection = "";
15107
+ if (renderedDocs.length > 0) {
15108
+ const docBlocks = renderedDocs.filter((d) => d.content && d.content.trim().length > 0).map((d) => {
15109
+ const marker = d.isCurrent ? " (Currently Viewed)" : "";
15110
+ return `## Document: ${d.title}${marker}
15111
+
15112
+ ${d.content}`;
15113
+ }).join("\n\n---\n\n");
15114
+ docSection = `# Documentation Category: ${categoryName}
15115
+
15116
+ ${docBlocks}`;
15117
+ }
15118
+ const markedExcerpt = (activeDoc?.selectedText || getActiveDocSelectedText() || "").trim();
15119
+ const excerptBlock = markedExcerpt ? `
15120
+
15121
+ ## User-Marked Excerpt (Focus of Question)
15122
+ The user highlighted the following text in "${title}":
15123
+ """
15124
+ ${markedExcerpt.slice(0, 4e3)}
15125
+ """` : "";
15126
+ if (docSection) {
15127
+ return `Answer the users question about Shuffle ${categoryName} based on the following complete documentation for the ${categoryName} category:
15128
+ ${DOC_PROMPT_DELIMITER_START}
15129
+ ${docSection}${excerptBlock}
15130
+ ${DOC_PROMPT_DELIMITER_END}
15131
+
15132
+ User Question:
15133
+ ${trimmedQuestion}`;
15134
+ }
15135
+ return `Answer the users question about Shuffle ${categoryName}:${excerptBlock}
15136
+
15137
+ ${trimmedQuestion}`;
15138
+ };
15139
+
15140
+ // components/AgentActivityList.tsx
15141
+ import { Pencil, StopCircle, AlertTriangle } from "lucide-react";
15142
+
15143
+ // ../Shuffle-Core/components/SegmentedControl.tsx
15144
+ import * as React6 from "react";
15145
+ import { motion as motion6, LayoutGroup } from "framer-motion";
15146
+ import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
15147
+ var SIZE = {
15148
+ sm: {
15149
+ containerPadding: "2px",
15150
+ fontSize: "11px",
15151
+ itemPaddingY: "4px",
15152
+ itemPaddingX: "10px",
15153
+ itemGap: "6px",
15154
+ countMin: "16px",
15155
+ countH: "16px",
15156
+ countPx: "4px",
15157
+ countFs: "10px",
15158
+ dividerH: "16px",
15159
+ dividerMx: "2px"
16344
15160
  },
16345
- {
16346
- id: "intel_to_edr_1",
16347
- slug: "push_detections",
16348
- label: "Push detections",
16349
- phase: "Step 3: Agents & Response Actions",
16350
- source: "threat_intel",
16351
- target: "edr",
16352
- automationLabel: "Push Threat Intel to EDR",
16353
- automationCategory: "intel",
16354
- automationArea: "Threat Intelligence (TIP)",
16355
- summary: "Pushes file hashes and C2 indicators from Threat Intelligence feeds into EDR custom IOC prevention and hunting lists."
15161
+ md: {
15162
+ containerPadding: "4px",
15163
+ fontSize: "12px",
15164
+ itemPaddingY: "6px",
15165
+ itemPaddingX: "12px",
15166
+ itemGap: "6px",
15167
+ countMin: "18px",
15168
+ countH: "18px",
15169
+ countPx: "6px",
15170
+ countFs: "10px",
15171
+ dividerH: "20px",
15172
+ dividerMx: "4px"
15173
+ }
15174
+ };
15175
+ var uid = 0;
15176
+ function useUniqueId(prefix) {
15177
+ const ref = React6.useRef(void 0);
15178
+ if (!ref.current) {
15179
+ uid += 1;
15180
+ ref.current = `${prefix}-${uid}`;
15181
+ }
15182
+ return ref.current;
15183
+ }
15184
+ function SegmentedControl({
15185
+ options,
15186
+ value,
15187
+ onChange,
15188
+ size = "md",
15189
+ variant = "outline",
15190
+ className,
15191
+ ariaLabel,
15192
+ layoutId,
15193
+ trailing
15194
+ }) {
15195
+ const autoId = useUniqueId("segmented");
15196
+ const groupId = layoutId ?? autoId;
15197
+ const s = SIZE[size];
15198
+ return /* @__PURE__ */ jsx20(LayoutGroup, { id: groupId, children: /* @__PURE__ */ jsxs14(
15199
+ "div",
15200
+ {
15201
+ role: "tablist",
15202
+ "aria-label": ariaLabel,
15203
+ className,
15204
+ style: {
15205
+ display: "inline-flex",
15206
+ alignItems: "center",
15207
+ gap: "2px",
15208
+ borderRadius: 9999,
15209
+ padding: s.containerPadding,
15210
+ fontSize: s.fontSize,
15211
+ border: variant === "outline" ? "1px solid hsl(var(--border))" : "1px solid transparent",
15212
+ background: variant === "outline" ? "transparent" : "hsl(var(--muted) / 0.4)"
15213
+ },
15214
+ children: [
15215
+ options.map((opt, i) => {
15216
+ if (opt.type === "divider") {
15217
+ return /* @__PURE__ */ jsx20(
15218
+ "span",
15219
+ {
15220
+ "aria-hidden": true,
15221
+ style: {
15222
+ width: 1,
15223
+ height: s.dividerH,
15224
+ margin: `0 ${s.dividerMx}`,
15225
+ background: "hsl(var(--border))",
15226
+ alignSelf: "center"
15227
+ }
15228
+ },
15229
+ opt.key ?? `divider-${i}`
15230
+ );
15231
+ }
15232
+ const isAction2 = opt.type === "action";
15233
+ const active2 = !isAction2 && opt.value === value;
15234
+ const key = isAction2 ? opt.key : opt.value;
15235
+ return /* @__PURE__ */ jsxs14(
15236
+ "button",
15237
+ {
15238
+ type: "button",
15239
+ "data-tour": opt.dataTour,
15240
+ "data-active": active2 ? "true" : "false",
15241
+ role: isAction2 ? "button" : "tab",
15242
+ "aria-selected": isAction2 ? void 0 : active2,
15243
+ title: opt.title,
15244
+ disabled: opt.disabled,
15245
+ onClick: () => {
15246
+ if (opt.disabled) return;
15247
+ if (isAction2) opt.onClick();
15248
+ else onChange(opt.value);
15249
+ },
15250
+ style: {
15251
+ position: "relative",
15252
+ display: "inline-flex",
15253
+ alignItems: "center",
15254
+ gap: s.itemGap,
15255
+ padding: `${s.itemPaddingY} ${s.itemPaddingX}`,
15256
+ borderRadius: 9999,
15257
+ fontWeight: 500,
15258
+ fontSize: s.fontSize,
15259
+ background: "transparent",
15260
+ border: "none",
15261
+ cursor: opt.disabled ? "not-allowed" : "pointer",
15262
+ color: active2 ? "hsl(var(--foreground))" : "hsl(var(--muted-foreground))",
15263
+ opacity: opt.disabled ? 0.5 : 1,
15264
+ transition: "color 200ms ease",
15265
+ outline: "none",
15266
+ fontFamily: "inherit"
15267
+ },
15268
+ onMouseEnter: (e) => {
15269
+ if (!active2 && !opt.disabled) {
15270
+ e.currentTarget.style.color = "hsl(var(--foreground))";
15271
+ }
15272
+ },
15273
+ onMouseLeave: (e) => {
15274
+ if (!active2 && !opt.disabled) {
15275
+ e.currentTarget.style.color = "hsl(var(--muted-foreground))";
15276
+ }
15277
+ },
15278
+ children: [
15279
+ active2 && /* @__PURE__ */ jsx20(
15280
+ motion6.span,
15281
+ {
15282
+ layoutId: `${groupId}-pill`,
15283
+ style: {
15284
+ position: "absolute",
15285
+ inset: 0,
15286
+ borderRadius: 9999,
15287
+ background: "hsl(var(--muted))",
15288
+ border: "1px solid hsl(var(--border))"
15289
+ },
15290
+ initial: false,
15291
+ transition: {
15292
+ type: "spring",
15293
+ stiffness: 260,
15294
+ damping: 30
15295
+ }
15296
+ }
15297
+ ),
15298
+ /* @__PURE__ */ jsx20(
15299
+ "span",
15300
+ {
15301
+ style: {
15302
+ position: "relative",
15303
+ zIndex: 1,
15304
+ display: "inline-flex",
15305
+ alignItems: "center",
15306
+ gap: s.itemGap
15307
+ },
15308
+ children: opt.label
15309
+ }
15310
+ ),
15311
+ !isAction2 && (typeof opt.count === "number" || typeof opt.count === "string") && /* @__PURE__ */ jsx20(
15312
+ "span",
15313
+ {
15314
+ style: {
15315
+ position: "relative",
15316
+ zIndex: 1,
15317
+ display: "inline-flex",
15318
+ alignItems: "center",
15319
+ justifyContent: "center",
15320
+ minWidth: s.countMin,
15321
+ height: s.countH,
15322
+ padding: `0 ${s.countPx}`,
15323
+ fontSize: s.countFs,
15324
+ fontWeight: 600,
15325
+ fontVariantNumeric: "tabular-nums",
15326
+ borderRadius: 6,
15327
+ border: "1px solid hsl(var(--border) / 0.6)",
15328
+ color: active2 ? "hsl(var(--foreground))" : "hsl(var(--muted-foreground))",
15329
+ background: "transparent"
15330
+ },
15331
+ children: opt.count
15332
+ }
15333
+ )
15334
+ ]
15335
+ },
15336
+ key
15337
+ );
15338
+ }),
15339
+ trailing != null && /* @__PURE__ */ jsx20(
15340
+ "span",
15341
+ {
15342
+ style: {
15343
+ display: "inline-flex",
15344
+ alignItems: "center",
15345
+ alignSelf: "center"
15346
+ },
15347
+ children: trailing
15348
+ }
15349
+ )
15350
+ ]
15351
+ }
15352
+ ) });
15353
+ }
15354
+
15355
+ // components/AgentActivityList.tsx
15356
+ import { Fragment as Fragment5, jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
15357
+ var STATUS_CONFIG = {
15358
+ FINISHED: { icon: /* @__PURE__ */ jsx21(CheckCircle, { size: 16 }), color: "hsl(var(--severity-low, 142 71% 45%))", label: "Completed" },
15359
+ SUCCESS: { icon: /* @__PURE__ */ jsx21(CheckCircle, { size: 16 }), color: "hsl(var(--severity-low, 142 71% 45%))", label: "Completed" },
15360
+ FAILED: { icon: /* @__PURE__ */ jsx21(XCircle, { size: 16 }), color: "hsl(var(--severity-critical, 0 72% 55%))", label: "Failed" },
15361
+ ABORTED: { icon: /* @__PURE__ */ jsx21(XCircle, { size: 16 }), color: "hsl(var(--severity-critical, 0 72% 55%))", label: "Aborted" },
15362
+ EXECUTING: { icon: /* @__PURE__ */ jsx21(Loader2, { size: 16 }), color: "hsl(var(--severity-medium, 38 92% 50%))", label: "Running" },
15363
+ RUNNING: { icon: /* @__PURE__ */ jsx21(Loader2, { size: 16 }), color: "hsl(var(--severity-medium, 38 92% 50%))", label: "Running" },
15364
+ WAITING: { icon: /* @__PURE__ */ jsx21(Clock, { size: 16 }), color: "hsl(var(--severity-info, 217 91% 60%))", label: "Waiting" },
15365
+ LIMIT_REACHED: { icon: /* @__PURE__ */ jsx21(AlertTriangle, { size: 16 }), color: "hsl(var(--severity-critical, 0 72% 55%))", label: "Limit reached" }
15366
+ };
15367
+ var getEffectiveStatus = (run2) => {
15368
+ const raw = (run2.status || "").toUpperCase();
15369
+ if (raw === "FINISHED" || raw === "SUCCESS" || raw === "FAILED" || raw === "ABORTED") {
15370
+ try {
15371
+ const d = diagnoseOutputWarning(run2);
15372
+ if (d?.kind === "token_limit") return "LIMIT_REACHED";
15373
+ } catch {
15374
+ }
15375
+ }
15376
+ return raw;
15377
+ };
15378
+ var SOURCE_ICON_SIZE = 18;
15379
+ var looksLikeExecutionId = (s) => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s.trim());
15380
+ var DATASTORE_KEY_RE = /\bKey:\s*(\S+)/i;
15381
+ var DATASTORE_CATEGORY_RE = /\bCategory:\s*(\S+)/i;
15382
+ var parseDatastoreTask = (arg) => {
15383
+ if (!arg) return null;
15384
+ const k = arg.match(DATASTORE_KEY_RE);
15385
+ const c = arg.match(DATASTORE_CATEGORY_RE);
15386
+ if (!k || !c) return null;
15387
+ const key = (k[1] || "").trim();
15388
+ const category = (c[1] || "").trim();
15389
+ if (!key || !category) return null;
15390
+ return { key, category };
15391
+ };
15392
+ var isAIAgentResult = (result) => String(result?.action?.app_name || "").trim().toLowerCase() === "ai agent";
15393
+ var getAIAgentResultPayload = (run2) => {
15394
+ const results = Array.isArray(run2.results) ? run2.results : [];
15395
+ const agentResult = results.find(isAIAgentResult);
15396
+ if (!agentResult || typeof agentResult !== "object" || !("result" in agentResult)) return null;
15397
+ return tryParseJson(agentResult.result);
15398
+ };
15399
+ var getDirectAgentInput = (payload) => {
15400
+ if (!payload || typeof payload !== "object") return null;
15401
+ const obj = payload;
15402
+ if (typeof obj.original_input === "string" && obj.original_input.trim()) {
15403
+ return obj.original_input.trim();
15404
+ }
15405
+ if (typeof obj.input === "string" && obj.input.trim()) {
15406
+ return obj.input.trim();
15407
+ }
15408
+ return null;
15409
+ };
15410
+ var findDatastoreTaskInRun = (run2) => {
15411
+ const agentInput = getDirectAgentInput(getAIAgentResultPayload(run2));
15412
+ const agentHit = parseDatastoreTask(agentInput || void 0);
15413
+ if (agentHit) return agentHit;
15414
+ const anyRun = run2;
15415
+ const candidates = [];
15416
+ if (typeof anyRun.original_input === "string") candidates.push(anyRun.original_input);
15417
+ if (typeof anyRun.input === "string") candidates.push(anyRun.input);
15418
+ if (run2.execution_argument) candidates.push(run2.execution_argument);
15419
+ if (run2.result) candidates.push(run2.result);
15420
+ if (Array.isArray(anyRun.results)) {
15421
+ try {
15422
+ candidates.push(JSON.stringify(anyRun.results));
15423
+ } catch {
15424
+ }
15425
+ }
15426
+ for (const c of candidates) {
15427
+ const hit = parseDatastoreTask(c);
15428
+ if (hit) return hit;
15429
+ }
15430
+ return null;
15431
+ };
15432
+ var classifyRunSource = (run2) => {
15433
+ const raw = (run2.execution_source || "").trim();
15434
+ const src = raw.toLowerCase();
15435
+ const sourceParts = raw.split("|");
15436
+ const fromSource = src.startsWith("datastore") && sourceParts.length > 2 ? { category: sourceParts[1].trim(), key: sourceParts[2].trim() } : null;
15437
+ const ds = findDatastoreTaskInRun(run2) || fromSource;
15438
+ if (ds || src.includes("datastore") || src.includes("enrichment") || src.includes("automation")) {
15439
+ return {
15440
+ kind: "datastore",
15441
+ label: "Datastore automation",
15442
+ reason: ds ? `Category: ${ds.category}
15443
+ Key: ${ds.key}
15444
+ Click to open it in the Datastore.` : "Started by a datastore automation (e.g. enrichment trigger).",
15445
+ icon: /* @__PURE__ */ jsx21(Database, { size: SOURCE_ICON_SIZE }),
15446
+ datastoreKey: ds?.key,
15447
+ datastoreCategory: ds?.category
15448
+ };
15449
+ }
15450
+ if (src.includes("schedule") || src.includes("cron")) {
15451
+ return {
15452
+ kind: "schedule",
15453
+ label: "Schedule",
15454
+ reason: "Started by a scheduled trigger (cron).",
15455
+ icon: /* @__PURE__ */ jsx21(Clock, { size: SOURCE_ICON_SIZE })
15456
+ };
15457
+ }
15458
+ if (src.includes("webhook") || src.includes("http")) {
15459
+ return {
15460
+ kind: "webhook",
15461
+ label: "Webhook",
15462
+ reason: "Started by an incoming webhook / HTTP trigger.",
15463
+ icon: /* @__PURE__ */ jsx21(Globe, { size: SOURCE_ICON_SIZE })
15464
+ };
15465
+ }
15466
+ if (src.includes("form")) {
15467
+ return {
15468
+ kind: "form",
15469
+ label: "Form",
15470
+ reason: "Started by a form submission.",
15471
+ icon: /* @__PURE__ */ jsx21(FileText, { size: SOURCE_ICON_SIZE })
15472
+ };
15473
+ }
15474
+ if (raw && looksLikeExecutionId(raw)) {
15475
+ return {
15476
+ kind: "workflow",
15477
+ label: "Workflow",
15478
+ reason: "Started by another workflow as a subflow.",
15479
+ icon: /* @__PURE__ */ jsx21(WorkflowIcon, { size: SOURCE_ICON_SIZE })
15480
+ };
15481
+ }
15482
+ if (src === "" || src === "manual" || src === "default" || src === "demo") {
15483
+ return {
15484
+ kind: "manual",
15485
+ label: "Manual",
15486
+ reason: "Started manually from the Agents UI.",
15487
+ icon: /* @__PURE__ */ jsx21(Hand, { size: SOURCE_ICON_SIZE })
15488
+ };
15489
+ }
15490
+ return {
15491
+ kind: "unknown",
15492
+ label: raw || "Unknown",
15493
+ reason: raw ? `Started by "${raw}".` : "Trigger source unknown.",
15494
+ icon: /* @__PURE__ */ jsx21(Zap, { size: SOURCE_ICON_SIZE })
15495
+ };
15496
+ };
15497
+ var getRunIconColor = (run2) => {
15498
+ const status = getEffectiveStatus(run2);
15499
+ if (status === "FINISHED" || status === "SUCCESS") return "hsl(var(--severity-low, 142 71% 45%))";
15500
+ if (status === "FAILED" || status === "ABORTED") return "hsl(var(--severity-critical, 0 72% 55%))";
15501
+ if (status === "LIMIT_REACHED") return "hsl(var(--severity-critical, 0 72% 55%))";
15502
+ if (status === "EXECUTING" || status === "RUNNING") return "hsl(var(--severity-medium, 38 92% 50%))";
15503
+ return "hsl(var(--primary, 24 100% 50%))";
15504
+ };
15505
+ var formatDuration = (run2) => {
15506
+ if (run2.started_at && run2.completed_at) {
15507
+ const start = Number(run2.started_at);
15508
+ const end = Number(run2.completed_at);
15509
+ if (!isNaN(start) && !isNaN(end)) {
15510
+ const toMs = (n) => n > 1e12 ? n : n * 1e3;
15511
+ const ms = Math.max(0, toMs(end) - toMs(start));
15512
+ if (ms < 1e3) return `${(ms / 1e3).toFixed(2)}s`;
15513
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(2)}s`;
15514
+ return `${Math.floor(ms / 6e4)}m ${Math.floor(ms % 6e4 / 1e3)}s`;
15515
+ }
15516
+ }
15517
+ if (run2.duration) return `${run2.duration.toFixed(2)}s`;
15518
+ return "";
15519
+ };
15520
+ var getTimeAgo = (dateStr) => {
15521
+ try {
15522
+ const ts = isNaN(Number(dateStr)) ? new Date(dateStr).getTime() : Number(dateStr) * 1e3;
15523
+ if (isNaN(ts)) return dateStr;
15524
+ const diffSec = Math.max(0, Math.floor((Date.now() - ts) / 1e3));
15525
+ if (diffSec < 60) return `${diffSec}s ago`;
15526
+ if (diffSec < 3600) return `${Math.floor(diffSec / 60)}m ago`;
15527
+ if (diffSec < 86400) return `${Math.floor(diffSec / 3600)}h ago`;
15528
+ return `${Math.floor(diffSec / 86400)}d ago`;
15529
+ } catch {
15530
+ return dateStr;
16356
15531
  }
16357
- ];
16358
- var USECASE_TOOL_CATEGORIES = [
16359
- "case_management (Cases / SOAR): Shuffle Security Incidents, TheHive, Jira, ServiceNow, PagerDuty, Linear",
16360
- "siem (SIEM & Log Analytics): Wazuh, Splunk, Elastic Security, Microsoft Sentinel, QRadar, Google Chronicle, Graylog",
16361
- "edr (EDR / Endpoint): CrowdStrike Falcon, SentinelOne, Microsoft Defender for Endpoint, Wazuh, Carbon Black, Velociraptor, Shuffle Host-Monitors",
16362
- "email (Email Security): Microsoft Office 365, Google Workspace / Gmail, IMAP/SMTP, Proofpoint, Mimecast",
16363
- "threat_intel (Threat Intelligence): MISP, OpenCTI, VirusTotal, AbuseIPDB, Shodan, GreyNoise, AlienVault OTX, URLhaus",
16364
- "asset_management (Assets & Vulnerabilities): Shuffle Assets & Vulnerabilities, Tenable, Qualys, Rapid7, NetBox, Snipe-IT, ServiceNow CMDB",
16365
- "iam (Identity & Access Management): Okta, Microsoft Entra ID (Azure AD), Active Directory LDAP, Google Workspace Admin, Duo, Keycloak",
16366
- "network (Network & Firewalls): Palo Alto PAN-OS, Fortinet FortiGate, Cloudflare, Suricata, Zeek, Zscaler, Cisco Meraki/ASA",
16367
- "cloud (Cloud Security): AWS (GuardDuty, CloudTrail, EC2, IAM), Azure Security Center, Google Cloud SCC, Wiz, Prisma Cloud",
16368
- "communication (Notifications & On-Call): Slack, Microsoft Teams, Email, PagerDuty, Opsgenie, Discord, Shuffle Mobile App"
16369
- ];
16370
- var USECASE_ARCHITECTURE_OVERVIEW = [
16371
- "How Usecases Work in Shuffle Security:",
16372
- " 1. Three-Phase Operational Pipeline:",
16373
- " - Step 1: Ingest & Tool Setup (`ingest`) \u2014 Connect external security sources (SIEM, EDR, Email, Threat Intel, Vulnerability Scanners, Host-Monitors, Network, Cloud) or the built-in Ingestion Webhook to normalize alerts and findings into Shuffle Security datastores (`shuffle-security_incidents`, `shuffle-security_vulnerabilities`, `shuffle-security_assets`, `shuffle-security_threat_intel`).",
16374
- " - Step 2: Context & Correlation (`correlation`) \u2014 Automatically evaluate Incident Routing Rules, enrich IOCs against Threat Intel, attach Asset/IAM context, and correlate Vulnerabilities with active incidents when records enter the datastore.",
16375
- " - Step 3: Agents & Response Actions (`response`) \u2014 Trigger autonomous AI Incident Handling agents (`Run AI Agent`), execute host/network/IAM/email/cloud containment, synchronize external tickets (`Forward Tickets`), send Slack/Teams/Email notifications, and escalate to on-call schedules (`Schedules & Phone Notifications`).",
16376
- " 2. Enabling & Configuring a Usecase:",
16377
- " - Standard workflow usecases are generated/enabled via `POST /api/v2/workflows/generate` with `{ label, category, app_name }` after the user authenticates their chosen source/target apps (`GET /api/v1/apps/authentication`).",
16378
- " - Datastore-driven usecases (Incident Routing Rules, Enrichment, Asset context, Vulnerability Correlation, AI Incident Handling) bind to datastore categories (`shuffle-security_incidents`, `shuffle-security_vulnerabilities`, `shuffle-security_assets`, `shuffle-security_threat_intel`) via `POST /api/v2/datastore/automate` and run automatically whenever matching records are created or updated.",
16379
- " - Special built-in usecases also have dedicated management views: Incident Routing Rules (`/incidents?settings=routing_rules`), AI Incident Handling (`/incidents?modal=ai_agent_prompts`), Host-Monitors (`/monitors?add_host=true`), Threat Intel Feeds (`/threat-intel/feeds`), Schedules & Phone Notifications (`/Detection?tab=schedules`), and Vulnerability Management (`/vulnerabilities`).",
16380
- " - Any external source can push JSON events directly into Shuffle Security via the tenant Ingestion Webhook (`POST /api/v1/hooks/webhook_<id>`).",
16381
- " 3. How You (the Agent) Should Help the User:",
16382
- " - Recommend which usecases to enable next based on the tenant's currently enabled usecases and connected/authenticated tools.",
16383
- " - Explain step-by-step how to configure or troubleshoot any usecase, including required app credentials, webhook setup, or datastore triggers.",
16384
- " - Inspect or generate workflows, check authentication status, and verify datastore automations using the Shuffle APIs below when requested."
16385
- ].join("\n");
16386
- var list = (rec) => Object.entries(rec || {}).filter(([, v]) => Array.isArray(v) && v.length > 0).map(([k, v]) => ` - ${k}: ${v.slice(0, 25).join(", ")}${v.length > 25 ? ` (+${v.length - 25} more)` : ""}`).join("\n");
16387
- var findCatalogUsecase = (flowId) => {
16388
- if (!flowId) return void 0;
16389
- const norm4 = flowId.trim().toLowerCase().replace(/[\s-]+/g, "_");
16390
- return DEFAULT_USECASE_CATALOG.find(
16391
- (u) => u.id.toLowerCase() === norm4 || u.slug.toLowerCase() === norm4 || u.label.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "") === norm4
16392
- );
16393
15532
  };
16394
- var composeUsecasePromptInput = (rawInput, flowId) => {
16395
- const trimmed = (rawInput || "").trim();
16396
- if (!trimmed || trimmed.includes(USECASE_PROMPT_START)) return trimmed;
16397
- const ctx = getActiveUsecasePromptContext();
16398
- const overviewCtx = getActiveUsecasesOverviewContext();
16399
- const catalogMatch = findCatalogUsecase(ctx?.id || flowId);
16400
- const lines = [USECASE_PROMPT_START];
16401
- if (ctx) {
16402
- lines.push(`Active View: Specific Usecase Drawer (/usecases/${flowId || ctx.id})`);
16403
- lines.push(`Usecase: ${ctx.label} (id: ${ctx.id})`);
16404
- if (ctx.source || ctx.target || catalogMatch) {
16405
- lines.push(`Data flow: ${ctx.source || catalogMatch?.source || "unknown"} -> ${ctx.target || catalogMatch?.target || "unknown"}`);
15533
+ var PROMPT_KEYS = [
15534
+ "original_input",
15535
+ "originalinput",
15536
+ "input",
15537
+ "prompt",
15538
+ "user_input",
15539
+ "userinput",
15540
+ "user_prompt",
15541
+ "userprompt",
15542
+ "question",
15543
+ "query",
15544
+ "message",
15545
+ "text",
15546
+ "content",
15547
+ "task",
15548
+ "instruction",
15549
+ "instructions"
15550
+ ];
15551
+ var deepFindPrompt = (value, depth = 0) => {
15552
+ if (value == null || depth > 6) return null;
15553
+ if (typeof value === "string") {
15554
+ const s = value.trim();
15555
+ if (s.startsWith("{") && s.endsWith("}") || s.startsWith("[") && s.endsWith("]")) {
15556
+ try {
15557
+ return deepFindPrompt(JSON.parse(s), depth + 1);
15558
+ } catch {
15559
+ }
16406
15560
  }
16407
- if (ctx.phase || catalogMatch?.phase) lines.push(`Phase: ${ctx.phase || catalogMatch?.phase}`);
16408
- if (ctx.automationArea || catalogMatch?.automationArea) {
16409
- lines.push(`Automation area: ${ctx.automationArea || catalogMatch?.automationArea}`);
15561
+ return null;
15562
+ }
15563
+ if (Array.isArray(value)) {
15564
+ for (const item of value) {
15565
+ const hit = deepFindPrompt(item, depth + 1);
15566
+ if (hit) return hit;
16410
15567
  }
16411
- if (ctx.tags?.length) lines.push(`Tags: ${ctx.tags.join(", ")}`);
16412
- if (typeof ctx.enabled === "boolean") lines.push(`Status for this tenant: ${ctx.enabled ? "Enabled" : "Disabled"}`);
16413
- if (ctx.healthStatus) lines.push(`Health / diagnostics: ${ctx.healthStatus}`);
16414
- if (typeof ctx.manualVerification === "boolean") {
16415
- lines.push(`Verification mode: ${ctx.manualVerification ? "Manual / UI-configured flow" : "Automated workflow generation via POST /api/v2/workflows/generate"}`);
15568
+ return null;
15569
+ }
15570
+ if (typeof value === "object") {
15571
+ const obj = value;
15572
+ for (const key of Object.keys(obj)) {
15573
+ if (PROMPT_KEYS.includes(key.toLowerCase())) {
15574
+ const v = obj[key];
15575
+ if (typeof v === "string" && v.trim()) return v.trim();
15576
+ const nested = deepFindPrompt(v, depth + 1);
15577
+ if (nested) return nested;
15578
+ }
16416
15579
  }
16417
- if (ctx.customAction || catalogMatch?.customRoute) {
16418
- const route = ctx.customAction?.href || ctx.customAction?.url || catalogMatch?.customRoute;
16419
- const actionLabel = ctx.customAction?.label || "Open configuration view";
16420
- lines.push(`Dedicated UI action: ${actionLabel}${route ? ` (${route})` : ""}${ctx.customAction?.description ? ` \u2014 ${ctx.customAction.description}` : ""}`);
15580
+ for (const key of Object.keys(obj)) {
15581
+ if (PROMPT_KEYS.includes(key.toLowerCase())) continue;
15582
+ const hit = deepFindPrompt(obj[key], depth + 1);
15583
+ if (hit) return hit;
16421
15584
  }
16422
- if (ctx.automationLabel || catalogMatch?.automationLabel) {
16423
- const autoCat = ctx.automationCategory || catalogMatch?.automationCategory;
16424
- lines.push(`Automation label: ${ctx.automationLabel || catalogMatch?.automationLabel}${autoCat ? ` (category: ${autoCat})` : ""}`);
15585
+ }
15586
+ return null;
15587
+ };
15588
+ var tryParseJson = (s) => {
15589
+ if (typeof s !== "string") return s;
15590
+ const trimmed = s.trim();
15591
+ if (!trimmed) return null;
15592
+ try {
15593
+ return JSON.parse(trimmed);
15594
+ } catch {
15595
+ return trimmed;
15596
+ }
15597
+ };
15598
+ var getRunPrompt = (run2) => {
15599
+ const anyRun = run2;
15600
+ const agentInput = getDirectAgentInput(getAIAgentResultPayload(run2));
15601
+ if (agentInput) return agentInput;
15602
+ const results = Array.isArray(anyRun.results) ? anyRun.results : null;
15603
+ if (results) {
15604
+ const agentResult = results.find(isAIAgentResult);
15605
+ if (agentResult?.result) {
15606
+ const hit = deepFindPrompt(tryParseJson(agentResult.result));
15607
+ if (hit) return hit;
16425
15608
  }
16426
- if (ctx.description || catalogMatch?.summary) {
16427
- lines.push(`Description: ${ctx.description || catalogMatch?.summary}`);
15609
+ for (const r of results) {
15610
+ if (r === agentResult) continue;
15611
+ const hit = deepFindPrompt(tryParseJson(r?.result));
15612
+ if (hit) return hit;
16428
15613
  }
16429
- if (ctx.agenticDescription) lines.push(`Agentic behavior: ${ctx.agenticDescription}`);
16430
- const connected = list(ctx.connectedApps);
16431
- if (connected) {
16432
- lines.push(`Connected tools with validated authentication:
16433
- ${connected}`);
16434
- } else {
16435
- lines.push("Connected tools with validated authentication: None detected yet for this usecase category.");
15614
+ }
15615
+ if (typeof anyRun.original_input === "string" && anyRun.original_input.trim()) {
15616
+ return anyRun.original_input.trim();
15617
+ }
15618
+ if (typeof anyRun.input === "string" && anyRun.input.trim()) {
15619
+ return anyRun.input.trim();
15620
+ }
15621
+ if (run2.result) {
15622
+ const hit = deepFindPrompt(tryParseJson(run2.result));
15623
+ if (hit) return hit;
15624
+ }
15625
+ if (run2.execution_argument) {
15626
+ const parsed = tryParseJson(run2.execution_argument);
15627
+ const hit = deepFindPrompt(parsed);
15628
+ if (hit) return hit;
15629
+ if (typeof parsed === "string" && parsed.length < 240) return parsed;
15630
+ }
15631
+ return null;
15632
+ };
15633
+ var stripMarkdown = (input) => {
15634
+ if (!input) return "";
15635
+ let s = stripSystemNotes(input).replace(/\n*=== USECASE CONTEXT ===[\s\S]*?(?:=== END USECASE CONTEXT ===|$)/g, "").replace(/\n*=== DOCS ARTICLE CONTEXT ===[\s\S]*?(?:=== END DOCS CONTEXT ===|$)/g, "");
15636
+ s = s.replace(/```[\s\S]*?```/g, " ");
15637
+ s = s.replace(/`([^`]*)`/g, "$1");
15638
+ s = s.replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1");
15639
+ s = s.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1");
15640
+ s = s.replace(/(\*\*|__)(.*?)\1/g, "$2");
15641
+ s = s.replace(/(\*|_)(?=\S)([^*_\n]+?)\1/g, "$2");
15642
+ s = s.replace(/^\s{0,3}#{1,6}\s+/gm, "");
15643
+ s = s.replace(/^\s{0,3}>\s?/gm, "");
15644
+ s = s.replace(/^\s*[-*+]\s+/gm, "");
15645
+ s = s.replace(/^\s*\d+\.\s+/gm, "");
15646
+ s = s.replace(/\s+/g, " ").trim();
15647
+ return s;
15648
+ };
15649
+ var getRunTitle = (run2) => {
15650
+ const prompt = getRunPrompt(run2);
15651
+ if (prompt) {
15652
+ const oneLine = stripMarkdown(prompt);
15653
+ return oneLine.length > 80 ? oneLine.slice(0, 80) + "\u2026" : oneLine;
15654
+ }
15655
+ if (run2.workflow?.name) return stripMarkdown(run2.workflow.name);
15656
+ return `Execution ${run2.execution_id?.slice(0, 8) || "\u2014"}`;
15657
+ };
15658
+ var getDecisionCount = (run2) => {
15659
+ if (Array.isArray(run2.decisions)) return run2.decisions.length;
15660
+ return 0;
15661
+ };
15662
+ var getRunSubtitle = (run2) => {
15663
+ if (run2.result) {
15664
+ try {
15665
+ const p = JSON.parse(run2.result);
15666
+ if (p.output && typeof p.output === "string") {
15667
+ const out = stripMarkdown(p.output);
15668
+ return out.length > 120 ? out.slice(0, 120) + "\u2026" : out;
15669
+ }
15670
+ if (p.original_input && typeof p.original_input === "string") {
15671
+ const inp = stripMarkdown(p.original_input);
15672
+ return inp.length > 120 ? inp.slice(0, 120) + "\u2026" : inp;
15673
+ }
15674
+ if (p.message && typeof p.message === "string") return stripMarkdown(p.message);
15675
+ } catch {
15676
+ if (run2.result.length < 120) return stripMarkdown(run2.result);
16436
15677
  }
16437
- const apps = list(ctx.categoryApps);
16438
- if (apps) lines.push(`Supported apps per category:
16439
- ${apps}`);
16440
- if (ctx.linkedWorkflows?.length) {
16441
- lines.push(`Linked workflows: ${ctx.linkedWorkflows.join(", ")}`);
16442
- } else {
16443
- lines.push("Linked workflows: None linked yet.");
15678
+ }
15679
+ const status = getEffectiveStatus(run2);
15680
+ const isRunning = status === "EXECUTING" || status === "RUNNING" || status === "WAITING";
15681
+ return run2.execution_source || (isRunning ? "Agent still running\u2026" : "Agent execution");
15682
+ };
15683
+ var normalizeResultStatus = (s) => {
15684
+ const v = (s || "").toUpperCase();
15685
+ if (v === "SUCCESS" || v === "FINISHED") return "success";
15686
+ if (v === "FAILURE" || v === "FAILED" || v === "ABORTED") return "failure";
15687
+ if (v === "WAITING" || v === "SKIPPED") return "waiting";
15688
+ return "unknown";
15689
+ };
15690
+ var getRunTools = (run2) => {
15691
+ const map = /* @__PURE__ */ new Map();
15692
+ const ids = /* @__PURE__ */ new Map();
15693
+ const skip = (s) => /^(ai\s*agent|shuffle\s*agent|shuffle_agent)$/i.test(s);
15694
+ const rank = { failure: 3, waiting: 2, success: 1, unknown: 0 };
15695
+ const merge2 = (name, status, id) => {
15696
+ if (!name) return;
15697
+ const s = String(name).trim();
15698
+ if (!s || skip(s)) return;
15699
+ const next = status || "unknown";
15700
+ const prev = map.get(s);
15701
+ if (!prev || rank[next] > rank[prev]) map.set(s, next);
15702
+ if (id && !ids.has(s)) ids.set(s, id);
15703
+ };
15704
+ const allowed = run2.allowed_actions;
15705
+ if (Array.isArray(allowed) && allowed.length > 0) {
15706
+ for (const entry of allowed) {
15707
+ if (typeof entry !== "string") continue;
15708
+ const parts = entry.split(":");
15709
+ if (parts.length < 3 || parts[0] !== "app") continue;
15710
+ const id = parts[1];
15711
+ const name = parts.slice(2).join(":");
15712
+ merge2(name, "unknown", id);
15713
+ }
15714
+ const normalize3 = (s) => s.toLowerCase().replace(/[\s_-]+/g, "_");
15715
+ const known = /* @__PURE__ */ new Map();
15716
+ for (const real of map.keys()) known.set(normalize3(real), real);
15717
+ const apply = (name, status) => {
15718
+ if (!name) return;
15719
+ const hit = known.get(normalize3(name));
15720
+ if (hit) merge2(hit, status);
15721
+ };
15722
+ (run2.results || []).forEach((r) => {
15723
+ apply(r?.action?.app_name || r?.action?.label, normalizeResultStatus(r?.status));
15724
+ });
15725
+ (run2.decisions || []).forEach((d) => {
15726
+ if (typeof d?.tool === "string") apply(d.tool, normalizeResultStatus(d?.status));
15727
+ });
15728
+ } else {
15729
+ (run2.results || []).forEach((r) => {
15730
+ merge2(r?.action?.app_name || r?.action?.label, normalizeResultStatus(r?.status));
15731
+ });
15732
+ (run2.decisions || []).forEach((d) => {
15733
+ if (typeof d?.tool === "string") merge2(d.tool, normalizeResultStatus(d?.status));
15734
+ });
15735
+ }
15736
+ return Array.from(map.entries()).slice(0, 6).map(([name, status]) => ({ name, status, id: ids.get(name) }));
15737
+ };
15738
+ var TOOL_STATUS_RING = {
15739
+ success: "hsl(var(--severity-low, 142 71% 45%))",
15740
+ failure: "hsl(var(--severity-critical, 0 72% 55%))",
15741
+ waiting: "hsl(var(--severity-medium, 38 92% 50%))",
15742
+ unknown: "hsl(var(--border))"
15743
+ };
15744
+ var TOOL_STATUS_LABEL = {
15745
+ success: "ran successfully",
15746
+ failure: "failed",
15747
+ waiting: "needs input",
15748
+ unknown: "used"
15749
+ };
15750
+ var normToolKey = (s) => s.toLowerCase().replace(/[\s_\-]+/g, "_");
15751
+ var AgentRunRow = ({ run: run2, onClick, sx, appIcons, onAppClick, apiKey, apiBaseUrl, orgId, abortingIds, onAbort }) => {
15752
+ const navigate = useNavigate();
15753
+ const statusKey = getEffectiveStatus(run2);
15754
+ const iconColor = getRunIconColor(run2);
15755
+ const duration = formatDuration(run2);
15756
+ const tools = getRunTools(run2);
15757
+ const decisionCount = getDecisionCount(run2);
15758
+ const attachmentCount = collectLlmImageAttachments(run2).length;
15759
+ const isRunning = statusKey === "EXECUTING" || statusKey === "RUNNING" || statusKey === "WAITING";
15760
+ const isAborting = !!run2.execution_id && !!abortingIds?.has(run2.execution_id);
15761
+ const wfId = run2.workflow_id || run2?.workflow?.id;
15762
+ const canAbort = isRunning && !!run2.execution_id && !!wfId && !isAborting;
15763
+ return /* @__PURE__ */ jsxs15(
15764
+ Box14,
15765
+ {
15766
+ onClick,
15767
+ sx: [
15768
+ {
15769
+ borderRadius: 2,
15770
+ border: "1px solid hsl(var(--border))",
15771
+ bgcolor: "hsl(var(--card))",
15772
+ display: "flex",
15773
+ alignItems: "center",
15774
+ gap: 2,
15775
+ px: 2.5,
15776
+ py: 2,
15777
+ cursor: "pointer",
15778
+ transition: "background 0.15s ease, border-color 0.15s ease",
15779
+ "&:hover": {
15780
+ bgcolor: "hsla(var(--muted) / 0.5)",
15781
+ borderColor: "hsl(var(--muted-foreground) / 0.3)"
15782
+ }
15783
+ },
15784
+ ...Array.isArray(sx) ? sx : sx ? [sx] : []
15785
+ ],
15786
+ children: [
15787
+ (() => {
15788
+ const sourceInfo = classifyRunSource(run2);
15789
+ const dsLink = sourceInfo.kind === "datastore" && sourceInfo.datastoreKey && sourceInfo.datastoreCategory ? `https://shuffler.io/admin?tab=datastore&category=${encodeURIComponent(sourceInfo.datastoreCategory)}&key=${encodeURIComponent(sourceInfo.datastoreKey)}` : null;
15790
+ return /* @__PURE__ */ jsx21(
15791
+ Tooltip7,
15792
+ {
15793
+ title: /* @__PURE__ */ jsxs15(Box14, { sx: { lineHeight: 1.4 }, children: [
15794
+ /* @__PURE__ */ jsx21(Box14, { sx: { fontWeight: 600 }, children: sourceInfo.label }),
15795
+ /* @__PURE__ */ jsx21(Box14, { sx: { opacity: 0.85, whiteSpace: "pre-line" }, children: sourceInfo.reason })
15796
+ ] }),
15797
+ placement: "top",
15798
+ arrow: true,
15799
+ children: /* @__PURE__ */ jsx21(
15800
+ Box14,
15801
+ {
15802
+ onClick: dsLink ? async (e) => {
15803
+ e.stopPropagation();
15804
+ await navigateToShuffleCore(dsLink, { newTab: true });
15805
+ } : void 0,
15806
+ sx: {
15807
+ width: 40,
15808
+ height: 40,
15809
+ borderRadius: "50%",
15810
+ display: "flex",
15811
+ alignItems: "center",
15812
+ justifyContent: "center",
15813
+ bgcolor: `${iconColor}15`,
15814
+ color: iconColor,
15815
+ flexShrink: 0,
15816
+ cursor: dsLink ? "pointer" : "inherit",
15817
+ transition: "background 0.15s ease",
15818
+ "&:hover": dsLink ? { bgcolor: `${iconColor}30` } : void 0
15819
+ },
15820
+ children: sourceInfo.icon
15821
+ }
15822
+ )
15823
+ }
15824
+ );
15825
+ })(),
15826
+ /* @__PURE__ */ jsxs15(Box14, { sx: { flex: 1, minWidth: 0 }, children: [
15827
+ /* @__PURE__ */ jsx21(Box14, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 0.25 }, children: /* @__PURE__ */ jsx21(
15828
+ Typography12,
15829
+ {
15830
+ sx: {
15831
+ fontSize: "0.9rem",
15832
+ fontWeight: 500,
15833
+ color: "hsl(var(--foreground))",
15834
+ overflow: "hidden",
15835
+ textOverflow: "ellipsis",
15836
+ whiteSpace: "nowrap"
15837
+ },
15838
+ children: getRunTitle(run2)
15839
+ }
15840
+ ) }),
15841
+ /* @__PURE__ */ jsx21(
15842
+ Typography12,
15843
+ {
15844
+ sx: {
15845
+ fontSize: "0.78rem",
15846
+ color: "hsl(var(--muted-foreground))",
15847
+ overflow: "hidden",
15848
+ textOverflow: "ellipsis",
15849
+ whiteSpace: "nowrap"
15850
+ },
15851
+ children: getRunSubtitle(run2)
15852
+ }
15853
+ ),
15854
+ /* @__PURE__ */ jsxs15(Box14, { sx: { display: "flex", alignItems: "center", gap: 1, mt: 0.5 }, children: [
15855
+ /* @__PURE__ */ jsx21(Typography12, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.7 }, children: run2.started_at ? getTimeAgo(run2.started_at) : "\u2014" }),
15856
+ duration && /* @__PURE__ */ jsxs15(Fragment5, { children: [
15857
+ /* @__PURE__ */ jsx21(Typography12, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.4 }, children: "\xB7" }),
15858
+ /* @__PURE__ */ jsx21(Typography12, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.7 }, children: duration })
15859
+ ] }),
15860
+ decisionCount > 0 && /* @__PURE__ */ jsxs15(Fragment5, { children: [
15861
+ /* @__PURE__ */ jsx21(Typography12, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.4 }, children: "\xB7" }),
15862
+ /* @__PURE__ */ jsxs15(Box14, { sx: { display: "inline-flex", alignItems: "center", gap: 0.5, color: "hsl(var(--muted-foreground))", opacity: 0.7 }, children: [
15863
+ /* @__PURE__ */ jsx21(GitBranch, { size: 11 }),
15864
+ /* @__PURE__ */ jsxs15(Typography12, { sx: { fontSize: "0.72rem", color: "inherit" }, children: [
15865
+ decisionCount,
15866
+ " ",
15867
+ decisionCount === 1 ? "decision" : "decisions"
15868
+ ] })
15869
+ ] })
15870
+ ] }),
15871
+ attachmentCount > 0 && /* @__PURE__ */ jsxs15(Fragment5, { children: [
15872
+ /* @__PURE__ */ jsx21(Typography12, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.4 }, children: "\xB7" }),
15873
+ /* @__PURE__ */ jsxs15(Box14, { sx: { display: "inline-flex", alignItems: "center", gap: 0.5, color: "hsl(var(--muted-foreground))", opacity: 0.7 }, children: [
15874
+ /* @__PURE__ */ jsx21(ImageIcon, { size: 11 }),
15875
+ /* @__PURE__ */ jsxs15(Typography12, { sx: { fontSize: "0.72rem", color: "inherit" }, children: [
15876
+ attachmentCount,
15877
+ " ",
15878
+ attachmentCount === 1 ? "attachment" : "attachments"
15879
+ ] })
15880
+ ] })
15881
+ ] })
15882
+ ] })
15883
+ ] }),
15884
+ /* @__PURE__ */ jsx21(
15885
+ Box14,
15886
+ {
15887
+ onClick: (e) => e.stopPropagation(),
15888
+ sx: {
15889
+ display: { xs: "none", sm: "flex" },
15890
+ alignItems: "center",
15891
+ justifyContent: "flex-end",
15892
+ flexShrink: 0,
15893
+ ml: tools.length > 0 ? 1 : 0,
15894
+ width: tools.length > 0 ? 140 : 0,
15895
+ minHeight: tools.length > 0 ? 28 : 0
15896
+ },
15897
+ children: tools.length > 0 ? /* @__PURE__ */ jsx21(
15898
+ AvatarGroup,
15899
+ {
15900
+ max: 5,
15901
+ sx: {
15902
+ "& .MuiAvatar-root": {
15903
+ width: 28,
15904
+ height: 28,
15905
+ fontSize: "0.7rem",
15906
+ borderColor: "hsl(var(--border))",
15907
+ bgcolor: "hsl(var(--muted))",
15908
+ color: "hsl(var(--muted-foreground))"
15909
+ }
15910
+ },
15911
+ children: tools.map((t) => {
15912
+ const icon = appIcons?.[normToolKey(t.name)];
15913
+ const label = t.name.replace(/_/g, " ");
15914
+ const slug = t.name.toLowerCase().replace(/\s+/g, "_");
15915
+ const ring = TOOL_STATUS_RING[t.status];
15916
+ return /* @__PURE__ */ jsx21(Tooltip7, { title: `${label} \u2014 ${TOOL_STATUS_LABEL[t.status]}`, arrow: true, children: /* @__PURE__ */ jsx21(
15917
+ Avatar4,
15918
+ {
15919
+ src: icon || void 0,
15920
+ alt: label,
15921
+ variant: "rounded",
15922
+ onClick: (e) => {
15923
+ e.stopPropagation();
15924
+ if (onAppClick) {
15925
+ onAppClick({ id: t.id, name: t.name });
15926
+ } else {
15927
+ navigate(`/apps/${encodeURIComponent(t.id || slug)}`);
15928
+ }
15929
+ },
15930
+ sx: {
15931
+ cursor: "pointer",
15932
+ borderColor: `${ring} !important`,
15933
+ borderWidth: "1px",
15934
+ borderStyle: "solid",
15935
+ transition: "transform 0.15s ease, border-color 0.15s ease",
15936
+ "&:hover": {
15937
+ transform: "scale(1.08)",
15938
+ borderColor: "hsl(var(--primary)) !important"
15939
+ }
15940
+ },
15941
+ children: label.charAt(0).toUpperCase()
15942
+ }
15943
+ ) }, t.name);
15944
+ })
15945
+ }
15946
+ ) : null
15947
+ }
15948
+ ),
15949
+ canAbort && /* @__PURE__ */ jsx21(Tooltip7, { title: isAborting ? "Aborting\u2026" : "Abort this execution", arrow: true, placement: "top", children: /* @__PURE__ */ jsx21("span", { children: /* @__PURE__ */ jsx21(
15950
+ IconButton4,
15951
+ {
15952
+ size: "small",
15953
+ disabled: isAborting,
15954
+ onClick: (e) => {
15955
+ e.stopPropagation();
15956
+ onAbort?.(run2);
15957
+ },
15958
+ sx: {
15959
+ flexShrink: 0,
15960
+ ml: 1.5,
15961
+ color: "hsl(var(--muted-foreground))",
15962
+ "&:hover": { color: "hsl(var(--destructive))", bgcolor: "hsl(var(--muted))" }
15963
+ },
15964
+ children: isAborting ? /* @__PURE__ */ jsx21(CircularProgress5, { size: 16, sx: { color: "hsl(var(--destructive))" } }) : /* @__PURE__ */ jsx21(StopCircle, { size: 18 })
15965
+ }
15966
+ ) }) })
15967
+ ]
16444
15968
  }
16445
- lines.push("");
16446
- lines.push(USECASE_ARCHITECTURE_OVERVIEW);
16447
- } else if (flowId) {
16448
- lines.push(`Active View: Specific Usecase (/usecases/${flowId})`);
16449
- if (catalogMatch) {
16450
- lines.push(`Usecase: ${catalogMatch.label} (id: ${catalogMatch.id}, slug: ${catalogMatch.slug})`);
16451
- lines.push(`Phase: ${catalogMatch.phase}`);
16452
- lines.push(`Data flow: ${catalogMatch.source} -> ${catalogMatch.target}`);
16453
- if (catalogMatch.automationArea) lines.push(`Automation area: ${catalogMatch.automationArea}`);
16454
- if (catalogMatch.customRoute) lines.push(`Dedicated configuration route: ${catalogMatch.customRoute}`);
16455
- lines.push(`Automation label: ${catalogMatch.automationLabel}${catalogMatch.automationCategory ? ` (category: ${catalogMatch.automationCategory})` : ""}`);
16456
- lines.push(`Description: ${catalogMatch.summary}`);
16457
- } else {
16458
- lines.push(`Usecase id/slug: ${flowId} (${humanizeUsecaseId(flowId)})`);
15969
+ );
15970
+ };
15971
+ var STATUS_FILTERS = [
15972
+ { label: "All", value: "" },
15973
+ { label: "Completed", value: "FINISHED" },
15974
+ { label: "Running", value: "EXECUTING" },
15975
+ { label: "Failed", value: "ABORTED" }
15976
+ ];
15977
+ var AgentActivityList = ({
15978
+ apiKey,
15979
+ apiBaseUrl,
15980
+ orgId,
15981
+ onRunClick,
15982
+ renderAppDetailDrawer,
15983
+ onEditWorkflow,
15984
+ showSearchBar = true,
15985
+ showStatusChips = true,
15986
+ limit = 50,
15987
+ top,
15988
+ emptyTitle = "No agent activity found",
15989
+ emptySubtitle = "The agent has not performed any actions yet",
15990
+ className,
15991
+ sx,
15992
+ toolbarSx,
15993
+ rowSx,
15994
+ usecaseFilters = [],
15995
+ globalUrl,
15996
+ theme,
15997
+ colorMode,
15998
+ initialRuns,
15999
+ disableFetch = false
16000
+ }) => {
16001
+ const [appDrawer, setAppDrawer] = useState13(null);
16002
+ const [runs, setRuns] = useState13(
16003
+ () => initialRuns ? initialRuns.slice(0, limit) : []
16004
+ );
16005
+ const [cursor, setCursor] = useState13("");
16006
+ const [hasMore, setHasMore] = useState13(false);
16007
+ const [isLoading, setIsLoading] = useState13(false);
16008
+ const [error, setError] = useState13(null);
16009
+ const [statusFilter, setStatusFilter] = useState13("");
16010
+ const [searchQuery, setSearchQuery] = useState13("");
16011
+ const [debouncedQuery, setDebouncedQuery] = useState13("");
16012
+ const [agentWorkflows, setAgentWorkflows] = useState13([]);
16013
+ const [workflowFilter, setWorkflowFilter] = useState13("");
16014
+ const [usecaseFilter, setUsecaseFilter] = useState13("");
16015
+ const [stopOpen, setStopOpen] = useState13(false);
16016
+ const [stopLoading, setStopLoading] = useState13(false);
16017
+ const [appIcons, setAppIcons] = useState13({});
16018
+ const [enrichedRuns, setEnrichedRuns] = useState13({});
16019
+ const enrichedRunsRef = useRef11({});
16020
+ const processedRunIdsRef = useRef11(/* @__PURE__ */ new Set());
16021
+ useEffect12(() => {
16022
+ enrichedRunsRef.current = enrichedRuns;
16023
+ }, [enrichedRuns]);
16024
+ const [abortingIds, setAbortingIds] = useState13(/* @__PURE__ */ new Set());
16025
+ const [abortedIds, setAbortedIds] = useState13(/* @__PURE__ */ new Set());
16026
+ const [lastOpenedRunId, setLastOpenedRunId] = useState13(() => getLastOpenedAgentRun());
16027
+ useEffect12(() => {
16028
+ const unsubAbort = subscribeAgentAborted((eid) => {
16029
+ setAbortedIds((prev) => prev.has(eid) ? prev : /* @__PURE__ */ new Set([...Array.from(prev), eid]));
16030
+ setRuns((prev) => prev.map((r) => r.execution_id === eid ? { ...r, status: "ABORTED" } : r));
16031
+ });
16032
+ const unsubOpened = subscribeLastOpenedAgentRun((eid) => setLastOpenedRunId(eid));
16033
+ return () => {
16034
+ unsubAbort();
16035
+ unsubOpened();
16036
+ };
16037
+ }, []);
16038
+ const debounceRef = useRef11(void 0);
16039
+ const selectedAgentWorkflow = agentWorkflows.find((w) => w.id === workflowFilter) || null;
16040
+ const openEditPrompt = useCallback8(async () => {
16041
+ if (!workflowFilter) return;
16042
+ try {
16043
+ const { prompt, apps } = await getAgentScheduleConfig(workflowFilter, { apiKey, apiBaseUrl, orgId });
16044
+ onEditWorkflow?.({
16045
+ workflowId: workflowFilter,
16046
+ name: selectedAgentWorkflow?.name || "Schedule",
16047
+ prompt,
16048
+ apps
16049
+ });
16050
+ } catch (e) {
16051
+ setError(e instanceof Error ? e.message : "Failed to load workflow");
16459
16052
  }
16460
- lines.push("");
16461
- lines.push(USECASE_ARCHITECTURE_OVERVIEW);
16462
- } else {
16463
- lines.push("Page: Usecases overview (/usecases \u2014 interactive 3-phase pipeline of all security automation usecases in Shuffle Security).");
16464
- if (overviewCtx) {
16465
- lines.push("");
16466
- lines.push("Current Tenant Live State on /usecases:");
16467
- if (overviewCtx.enabledUsecases?.length) {
16468
- lines.push(` - Enabled usecases (${overviewCtx.enabledUsecases.length}): ${overviewCtx.enabledUsecases.map((u) => `${u.label} (${u.id}: ${u.source} -> ${u.target})`).join(", ")}`);
16469
- } else {
16470
- lines.push(" - Enabled usecases: None enabled yet.");
16053
+ }, [workflowFilter, selectedAgentWorkflow, apiKey, apiBaseUrl, orgId, onEditWorkflow]);
16054
+ const confirmStop = useCallback8(async () => {
16055
+ if (!workflowFilter) return;
16056
+ setStopLoading(true);
16057
+ try {
16058
+ await stopAgentSchedule(workflowFilter, { apiKey, apiBaseUrl, orgId });
16059
+ setStopOpen(false);
16060
+ const items = await listAgentScheduleWorkflows({ apiKey, apiBaseUrl, orgId });
16061
+ setAgentWorkflows(items);
16062
+ setWorkflowFilter("");
16063
+ } catch (e) {
16064
+ setError(e instanceof Error ? e.message : "Failed to stop schedule");
16065
+ } finally {
16066
+ setStopLoading(false);
16067
+ }
16068
+ }, [workflowFilter, apiKey, apiBaseUrl, orgId]);
16069
+ const handleAbort = useCallback8(
16070
+ async (run2) => {
16071
+ const eid = run2.execution_id;
16072
+ const wfId = run2.workflow_id || run2?.workflow?.id;
16073
+ if (!eid || !wfId) return;
16074
+ const prevStatus = run2.status;
16075
+ setAbortingIds((prev) => /* @__PURE__ */ new Set([...Array.from(prev), eid]));
16076
+ setAbortedIds((prev) => /* @__PURE__ */ new Set([...Array.from(prev), eid]));
16077
+ broadcastAgentAborted(eid);
16078
+ try {
16079
+ await abortAgentExecution({
16080
+ workflowId: wfId,
16081
+ executionId: eid,
16082
+ authorization: run2.authorization,
16083
+ apiKey,
16084
+ apiBaseUrl,
16085
+ orgId
16086
+ });
16087
+ setRuns(
16088
+ (prev) => prev.map((r) => r.execution_id === eid ? { ...r, status: "ABORTED" } : r)
16089
+ );
16090
+ } catch (e) {
16091
+ setAbortedIds((prev) => {
16092
+ const next = new Set(Array.from(prev));
16093
+ next.delete(eid);
16094
+ return next;
16095
+ });
16096
+ setRuns(
16097
+ (prev) => prev.map((r) => r.execution_id === eid ? { ...r, status: prevStatus } : r)
16098
+ );
16099
+ toast2({
16100
+ title: "Abort failed",
16101
+ description: e instanceof Error ? e.message : "Failed to abort execution",
16102
+ variant: "destructive"
16103
+ });
16104
+ } finally {
16105
+ setAbortingIds((prev) => {
16106
+ const next = new Set(Array.from(prev));
16107
+ next.delete(eid);
16108
+ return next;
16109
+ });
16471
16110
  }
16472
- if (overviewCtx.disabledUsecases?.length) {
16473
- lines.push(` - Disabled / available usecases (${overviewCtx.disabledUsecases.length}): ${overviewCtx.disabledUsecases.map((u) => `${u.label} (${u.id})`).join(", ")}`);
16111
+ },
16112
+ [apiKey, apiBaseUrl, orgId]
16113
+ );
16114
+ const updateSearchQuery = useCallback8((q) => {
16115
+ setSearchQuery(q);
16116
+ if (debounceRef.current) clearTimeout(debounceRef.current);
16117
+ debounceRef.current = setTimeout(() => setDebouncedQuery(q), 350);
16118
+ }, []);
16119
+ useEffect12(() => () => {
16120
+ if (debounceRef.current) clearTimeout(debounceRef.current);
16121
+ }, []);
16122
+ const fetchRuns = useCallback8(
16123
+ async (append = false, cursorParam = "") => {
16124
+ if (disableFetch) {
16125
+ if (initialRuns && !append) {
16126
+ setRuns(initialRuns.slice(0, limit));
16127
+ }
16128
+ return;
16474
16129
  }
16475
- if (overviewCtx.validatedApps?.length) {
16476
- lines.push(` - Connected & authenticated apps: ${overviewCtx.validatedApps.slice(0, 30).join(", ")}`);
16477
- } else {
16478
- lines.push(" - Connected & authenticated apps: None authenticated yet.");
16130
+ setIsLoading(true);
16131
+ setError(null);
16132
+ try {
16133
+ const result = await searchAgentActivity({
16134
+ limit,
16135
+ top,
16136
+ status: statusFilter,
16137
+ cursor: cursorParam,
16138
+ apiKey,
16139
+ apiBaseUrl,
16140
+ orgId,
16141
+ workflowId: workflowFilter || "AGENT"
16142
+ });
16143
+ if (result.success && result.runs && result.runs.length > 0) {
16144
+ const pageRuns = result.runs.slice(0, limit);
16145
+ setRuns((prev) => append ? [...prev, ...pageRuns] : pageRuns);
16146
+ setCursor(result.cursor);
16147
+ setHasMore(!!result.cursor && result.runs.length > 0);
16148
+ } else if (result.success) {
16149
+ setRuns((prev) => append ? prev : initialRuns && initialRuns.length > 0 ? initialRuns.slice(0, limit) : []);
16150
+ setCursor(result.cursor);
16151
+ setHasMore(false);
16152
+ } else {
16153
+ if (initialRuns && initialRuns.length > 0 && !append) {
16154
+ setRuns(initialRuns.slice(0, limit));
16155
+ } else {
16156
+ setError("Failed to fetch agent activity");
16157
+ }
16158
+ }
16159
+ } catch (err) {
16160
+ if (initialRuns && initialRuns.length > 0 && !append) {
16161
+ setRuns(initialRuns.slice(0, limit));
16162
+ } else {
16163
+ setError(err instanceof Error ? err.message : "Failed to fetch agent activity");
16164
+ }
16165
+ } finally {
16166
+ setIsLoading(false);
16167
+ }
16168
+ },
16169
+ [disableFetch, initialRuns, statusFilter, limit, top, apiKey, apiBaseUrl, orgId, workflowFilter]
16170
+ );
16171
+ useEffect12(() => {
16172
+ fetchRuns(false, "");
16173
+ }, [statusFilter, apiKey, apiBaseUrl, orgId, workflowFilter]);
16174
+ useEffect12(() => {
16175
+ let cancelled = false;
16176
+ listAgentScheduleWorkflows({ apiKey, apiBaseUrl, orgId }).then((items) => {
16177
+ if (!cancelled) setAgentWorkflows(items);
16178
+ }).catch(() => {
16179
+ });
16180
+ return () => {
16181
+ cancelled = true;
16182
+ };
16183
+ }, [apiKey, apiBaseUrl, orgId]);
16184
+ useEffect12(() => {
16185
+ let cancelled = false;
16186
+ fetchAppsViaApiConfig().then((apps) => {
16187
+ if (cancelled || !Array.isArray(apps)) return;
16188
+ const map = {};
16189
+ for (const a of apps) {
16190
+ const name = a?.name;
16191
+ const img = a?.large_image || a?.image_url || a?.image;
16192
+ if (!name || !img) continue;
16193
+ map[normToolKey(name)] = img;
16479
16194
  }
16480
- if (overviewCtx.validatedCategories?.length) {
16481
- lines.push(` - Authenticated tool categories: ${overviewCtx.validatedCategories.join(", ")}`);
16195
+ setAppIcons(map);
16196
+ }).catch(() => {
16197
+ });
16198
+ return () => {
16199
+ cancelled = true;
16200
+ };
16201
+ }, []);
16202
+ const iconAttemptsRef = useRef11(/* @__PURE__ */ new Set());
16203
+ const iconMountedRef = useRef11(true);
16204
+ useEffect12(() => {
16205
+ iconMountedRef.current = true;
16206
+ return () => {
16207
+ iconMountedRef.current = false;
16208
+ };
16209
+ }, []);
16210
+ useEffect12(() => {
16211
+ if (!runs.length) return;
16212
+ const missing = /* @__PURE__ */ new Set();
16213
+ for (const run2 of runs) {
16214
+ for (const t of getRunTools(run2)) {
16215
+ const key = normToolKey(t.name);
16216
+ if (!appIcons[key] && !iconAttemptsRef.current.has(key)) missing.add(t.name);
16482
16217
  }
16483
- if (overviewCtx.activeWorkflows?.length) {
16484
- lines.push(` - Active tenant workflows (${overviewCtx.activeWorkflows.length}): ${overviewCtx.activeWorkflows.slice(0, 20).join(", ")}${overviewCtx.activeWorkflows.length > 20 ? ` (+${overviewCtx.activeWorkflows.length - 20} more)` : ""}`);
16218
+ }
16219
+ if (missing.size === 0) return;
16220
+ (async () => {
16221
+ const names = [...missing];
16222
+ const resolved = {};
16223
+ for (let pass = 0; pass < 2; pass++) {
16224
+ for (const name of names) {
16225
+ const key = normToolKey(name);
16226
+ iconAttemptsRef.current.add(key);
16227
+ if (!resolved[key]) {
16228
+ const fb = publicAppImage(name);
16229
+ if (fb) {
16230
+ resolved[key] = fb;
16231
+ } else {
16232
+ try {
16233
+ const hit = await Promise.race([
16234
+ resolveApp(name),
16235
+ new Promise((r) => setTimeout(() => r(null), 4e3))
16236
+ ]);
16237
+ if (hit?.image) resolved[key] = hit.image;
16238
+ } catch {
16239
+ }
16240
+ }
16241
+ }
16242
+ await new Promise((r) => setTimeout(r, 120));
16243
+ }
16244
+ if (pass === 0) await new Promise((r) => setTimeout(r, 2e3));
16485
16245
  }
16486
- if (overviewCtx.activeFilters) {
16487
- const f = overviewCtx.activeFilters;
16488
- const activeFilterParts = [
16489
- f.phase && f.phase !== "all" ? `phase=${f.phase}` : "",
16490
- f.category && f.category !== "all" ? `category=${f.category}` : "",
16491
- f.area && f.area !== "all" ? `area=${f.area}` : "",
16492
- f.tag && f.tag !== "all" ? `tag=${f.tag}` : "",
16493
- f.search ? `search="${f.search}"` : ""
16494
- ].filter(Boolean);
16495
- if (activeFilterParts.length > 0) {
16496
- lines.push(` - User's active UI filters: ${activeFilterParts.join(", ")}`);
16246
+ if (!iconMountedRef.current || Object.keys(resolved).length === 0) return;
16247
+ setAppIcons((prev) => ({ ...prev, ...resolved }));
16248
+ })();
16249
+ }, [runs, appIcons]);
16250
+ useEffect12(() => {
16251
+ if (!runs.length) return;
16252
+ let cancelled = false;
16253
+ const buildPatchFromRun = (r) => {
16254
+ const results = Array.isArray(r.results) ? r.results : null;
16255
+ if (!results || results.length === 0) return null;
16256
+ const agentResult = results.find((x) => x?.action?.app_name === "AI Agent");
16257
+ let decisions = Array.isArray(r.decisions) ? r.decisions : void 0;
16258
+ let originalInput;
16259
+ let allowedActions;
16260
+ if (agentResult?.result) {
16261
+ try {
16262
+ const parsed = JSON.parse(agentResult.result);
16263
+ if (!decisions && Array.isArray(parsed?.decisions)) decisions = parsed.decisions;
16264
+ if (typeof parsed?.original_input === "string") originalInput = parsed.original_input;
16265
+ if (Array.isArray(parsed?.allowed_actions)) allowedActions = parsed.allowed_actions;
16266
+ } catch {
16497
16267
  }
16498
16268
  }
16269
+ const hasPrompt = !!r.execution_argument || !!originalInput || !!agentResult?.result;
16270
+ if (!decisions || !hasPrompt) return null;
16271
+ const patch2 = {
16272
+ results,
16273
+ execution_argument: r.execution_argument,
16274
+ result: agentResult?.result ?? r.result,
16275
+ decisions,
16276
+ allowed_actions: allowedActions
16277
+ };
16278
+ if (originalInput && !patch2.execution_argument) {
16279
+ patch2.execution_argument = JSON.stringify({ original_input: originalInput });
16280
+ }
16281
+ return patch2;
16282
+ };
16283
+ let pending = {};
16284
+ let flushTimer = null;
16285
+ const flush = () => {
16286
+ flushTimer = null;
16287
+ if (cancelled) return;
16288
+ const batch = pending;
16289
+ pending = {};
16290
+ if (Object.keys(batch).length === 0) return;
16291
+ setEnrichedRuns((prev) => ({ ...prev, ...batch }));
16292
+ };
16293
+ const queuePatch = (id, patch2) => {
16294
+ pending[id] = patch2;
16295
+ if (!flushTimer) flushTimer = setTimeout(flush, 250);
16296
+ };
16297
+ const needsFetch = [];
16298
+ for (const r of runs) {
16299
+ const id = r.execution_id;
16300
+ if (!id || enrichedRunsRef.current[id] || processedRunIdsRef.current.has(id)) continue;
16301
+ if (id.startsWith("demo-") || id.startsWith("dummy-")) {
16302
+ processedRunIdsRef.current.add(id);
16303
+ continue;
16304
+ }
16305
+ const native = buildPatchFromRun(r);
16306
+ if (native) {
16307
+ processedRunIdsRef.current.add(id);
16308
+ queuePatch(id, native);
16309
+ } else {
16310
+ needsFetch.push(id);
16311
+ }
16499
16312
  }
16500
- lines.push("");
16501
- lines.push(USECASE_ARCHITECTURE_OVERVIEW);
16502
- lines.push("");
16503
- lines.push(`Supported Tool Categories:
16504
- ${USECASE_TOOL_CATEGORIES.map((c) => ` - ${c}`).join("\n")}`);
16505
- lines.push("");
16506
- lines.push("Complete Shuffle Security Usecases Catalog (All 3 Phases):");
16507
- for (const item of DEFAULT_USECASE_CATALOG) {
16508
- lines.push(
16509
- ` - [${item.phase}] ${item.label} (id: ${item.id}, slug: /usecases/${item.slug}, flow: ${item.source} -> ${item.target}, automation: ${item.automationLabel}${item.automationCategory ? `, category: ${item.automationCategory}` : ""}${item.customRoute ? `, route: ${item.customRoute}` : ""}): ${item.summary}`
16510
- );
16511
- }
16512
- }
16513
- lines.push("");
16514
- lines.push(`Required Shuffle APIs:
16515
- ${USECASE_REQUIRED_APIS.map((a) => ` - ${a}`).join("\n")}`);
16516
- lines.push(USECASE_PROMPT_END);
16517
- return `${trimmed}
16518
-
16519
- ${lines.join("\n")}`;
16520
- };
16521
-
16522
- // ../lib/docsPromptContext.ts
16523
- var DOC_SELECTION_EVENT = "shuffle:doc-selection-change";
16524
- var GLOBAL_DOC_SELECTION_KEY = "__shuffleActiveDocSelection";
16525
- var truncateDocSelectionPreview = (text2, maxChars = 36) => {
16526
- const clean = (text2 || "").replace(/\s+/g, " ").trim();
16527
- if (!clean) return "";
16528
- if (clean.length <= maxChars) return clean;
16529
- return `${clean.slice(0, maxChars).trimEnd()}\u2026`;
16530
- };
16531
- var GLOBAL_DOC_KEY = "__shuffleActiveDocInfo";
16532
- var getGlobalScope2 = () => {
16533
- if (typeof globalThis !== "undefined") return globalThis;
16534
- if (typeof window !== "undefined") return window;
16535
- if (typeof global !== "undefined") return global;
16536
- return null;
16537
- };
16538
- var setActiveDocSelectedText = (selectedText) => {
16539
- const scope2 = getGlobalScope2();
16540
- if (!scope2) return;
16541
- const cleaned = (selectedText || "").trim();
16542
- if (cleaned) {
16543
- scope2[GLOBAL_DOC_SELECTION_KEY] = cleaned;
16544
- if (scope2[GLOBAL_DOC_KEY] && typeof scope2[GLOBAL_DOC_KEY] === "object") {
16545
- scope2[GLOBAL_DOC_KEY].selectedText = cleaned;
16546
- }
16547
- } else {
16548
- delete scope2[GLOBAL_DOC_SELECTION_KEY];
16549
- if (scope2[GLOBAL_DOC_KEY] && typeof scope2[GLOBAL_DOC_KEY] === "object") {
16550
- delete scope2[GLOBAL_DOC_KEY].selectedText;
16551
- }
16552
- }
16553
- if (typeof window !== "undefined") {
16554
- try {
16555
- window.dispatchEvent(
16556
- new CustomEvent(DOC_SELECTION_EVENT, {
16557
- detail: { selectedText: cleaned || null }
16558
- })
16559
- );
16560
- } catch {
16561
- }
16562
- }
16563
- };
16564
- var getActiveDocSelectedText = () => {
16565
- const scope2 = getGlobalScope2();
16566
- if (!scope2) return null;
16567
- const sel = scope2[GLOBAL_DOC_SELECTION_KEY] || scope2[GLOBAL_DOC_KEY]?.selectedText;
16568
- return typeof sel === "string" && sel.trim().length > 0 ? sel.trim() : null;
16569
- };
16570
- var getActiveDocPromptContext = () => {
16571
- const scope2 = getGlobalScope2();
16572
- if (!scope2) return null;
16573
- const info = scope2[GLOBAL_DOC_KEY];
16574
- if (info && typeof info === "object") {
16575
- return info;
16576
- }
16577
- return null;
16578
- };
16579
- var isDocsRoute = (pathname) => {
16580
- if (!pathname) return false;
16581
- return pathname === "/docs" || pathname.startsWith("/docs/") || pathname === "/legal" || pathname.startsWith("/legal/") || pathname === "/articles" || pathname.startsWith("/articles/");
16582
- };
16583
- var DOC_PROMPT_DELIMITER_START = "--- BEGIN DOCUMENTATION ---";
16584
- var DOC_PROMPT_DELIMITER_END = "--- END DOCUMENTATION ---";
16585
- var DOC_PROMPT_PREFIX_REGEX = /^Answer the users? question about (?:["']?)(.*?)(?:["']?) based on(?: on)? the following (?:documentation )?content:/i;
16586
- var sanitizeDocMarkdown = (markdown, maxChars = 35e3) => {
16587
- if (!markdown || typeof markdown !== "string") return "";
16588
- let cleaned = markdown;
16589
- cleaned = cleaned.replace(/^---\s*\r?\n[\s\S]*?\r?\n---\s*(?:\r?\n|$)/, "");
16590
- cleaned = stripInContentToc(cleaned);
16591
- cleaned = cleaned.replace(/<!--[\s\S]*?-->/g, "");
16592
- cleaned = cleaned.replace(/<(script|style|iframe)[^>]*>[\s\S]*?<\/\1>/gi, "");
16593
- cleaned = cleaned.replace(/data:image\/[a-zA-Z0-9+.-]+;base64,[A-Za-z0-9+/=]+/g, "[embedded image]");
16594
- cleaned = cleaned.replace(/\n{3,}/g, "\n\n");
16595
- cleaned = cleaned.trim();
16596
- if (cleaned.length > maxChars) {
16597
- const cut = cleaned.lastIndexOf("\n\n", maxChars);
16598
- const splitIndex = cut > maxChars * 0.7 ? cut : maxChars;
16599
- cleaned = `${cleaned.slice(0, splitIndex).trim()}
16600
-
16601
- [Documentation content truncated for length...]`;
16602
- }
16603
- return cleaned;
16604
- };
16605
- var isDocInjectedPrompt = (prompt) => {
16606
- if (!prompt || typeof prompt !== "string") return false;
16607
- return prompt.includes(DOC_PROMPT_DELIMITER_START) || DOC_PROMPT_PREFIX_REGEX.test(prompt);
16608
- };
16609
- var extractCleanDisplayPrompt = (prompt) => {
16610
- if (!prompt || typeof prompt !== "string") return "";
16611
- let trimmed = prompt.trim();
16612
- if (isUsecaseInjectedPrompt(trimmed)) {
16613
- trimmed = extractCleanUsecasePrompt(trimmed);
16614
- }
16615
- if (isIncidentInjectedPrompt(trimmed)) {
16616
- return extractCleanIncidentPrompt(trimmed);
16617
- }
16618
- if (!isDocInjectedPrompt(trimmed)) return trimmed;
16619
- if (trimmed.includes(DOC_PROMPT_DELIMITER_END)) {
16620
- const parts = trimmed.split(DOC_PROMPT_DELIMITER_END);
16621
- const after = parts[parts.length - 1]?.trim() || "";
16622
- const cleaned = after.replace(/^(?:User\s+Question:\s*|Question:\s*)/i, "").trim();
16623
- if (cleaned) return cleaned;
16624
- }
16625
- if (DOC_PROMPT_PREFIX_REGEX.test(trimmed)) {
16626
- const paragraphs = trimmed.split(/\n\s*\n/).filter(Boolean);
16627
- if (paragraphs.length > 1) {
16628
- const last = paragraphs[paragraphs.length - 1].trim();
16629
- const cleaned = last.replace(/^(?:User\s+Question:\s*|Question:\s*)/i, "").trim();
16630
- if (cleaned) return cleaned;
16631
- }
16632
- }
16633
- return trimmed;
16634
- };
16635
- var normalizeDocPromptTitle = (title) => {
16636
- const t = (title || "").trim();
16637
- if (!t || t.toLowerCase() === "index" || t.toLowerCase() === "documentation" || t.toLowerCase() === "docs") {
16638
- return "Shuffle documentation";
16639
- }
16640
- const lower = t.toLowerCase();
16641
- if (lower === "about" || lower === "about shuffle" || lower === "about us" || lower === "about-shuffle" || lower === "about-us") {
16642
- return "Shuffle";
16643
- }
16644
- if (/^about\s+/i.test(t)) {
16645
- const stripped = t.replace(/^about\s+/i, "").trim();
16646
- return stripped || "Shuffle";
16647
- }
16648
- return t;
16649
- };
16650
- var composeDocPromptInput = (userQuestion, pathname, fallbackTitle, overrideDocInfo) => {
16651
- const trimmedQuestion = (userQuestion || "").trim();
16652
- if (!trimmedQuestion) return "";
16653
- if (isDocInjectedPrompt(trimmedQuestion)) {
16654
- return trimmedQuestion;
16655
- }
16656
- const activeDoc = overrideDocInfo ?? getActiveDocPromptContext();
16657
- const rawTitle = (activeDoc?.title || fallbackTitle || getGlobalScope2()?.__shuffleActiveEntityTitle || "Shuffle documentation").trim();
16658
- const title = normalizeDocPromptTitle(rawTitle);
16659
- const groupLabel = activeDoc?.groupLabel;
16660
- const groupId = activeDoc?.groupId;
16661
- const currentSlug = (activeDoc?.slug || "").toLowerCase().replace(/_+/g, "-");
16662
- let categoryDocs = activeDoc?.groupDocs || [];
16663
- if (categoryDocs.length === 0 && groupId) {
16664
- categoryDocs = getCachedGroupDocs(groupId);
16665
- }
16666
- if (categoryDocs.length === 0 && currentSlug) {
16667
- const groupDef2 = getDocGroup(currentSlug);
16668
- if (groupDef2) {
16669
- categoryDocs = getCachedGroupDocs(groupDef2.id);
16670
- }
16671
- }
16672
- const groupDef = currentSlug ? getDocGroup(currentSlug) : null;
16673
- const categoryName = groupLabel || groupDef?.label || title;
16674
- const rawMarkdown = activeDoc?.content || "";
16675
- const renderedDocs = [];
16676
- if (categoryDocs.length > 0) {
16677
- for (const doc of categoryDocs) {
16678
- const docSlugClean = (doc.slug || "").toLowerCase().replace(/_+/g, "-");
16679
- const isCurrent = docSlugClean === currentSlug;
16680
- const contentToUse = isCurrent && rawMarkdown ? rawMarkdown : doc.content;
16681
- renderedDocs.push({
16682
- title: doc.title || title,
16683
- slug: doc.slug,
16684
- content: sanitizeDocMarkdown(contentToUse, 16e3),
16685
- isCurrent
16313
+ const CONCURRENCY = 2;
16314
+ let i = 0;
16315
+ const fetchOne = async (executionId) => {
16316
+ try {
16317
+ const resp = await fetch(
16318
+ getApiUrl(
16319
+ `/api/v1/executions/${encodeURIComponent(executionId)}?authorization=${encodeURIComponent(executionId)}`
16320
+ ),
16321
+ {
16322
+ method: "GET",
16323
+ credentials: "include",
16324
+ headers: {
16325
+ ...apiKey ? { Authorization: `Bearer ${apiKey}` } : getAuthHeader(),
16326
+ ...orgId ? { "Org-Id": orgId } : {}
16327
+ }
16328
+ }
16329
+ );
16330
+ if (!resp.ok || cancelled) return;
16331
+ const json = await resp.json().catch(() => null);
16332
+ if (!json || cancelled) return;
16333
+ let decisions;
16334
+ let originalInput;
16335
+ let allowedActions;
16336
+ const agentResult = Array.isArray(json.results) ? json.results.find((r) => r?.action?.app_name === "AI Agent") : null;
16337
+ if (agentResult?.result) {
16338
+ try {
16339
+ const parsed = JSON.parse(agentResult.result);
16340
+ if (Array.isArray(parsed?.decisions)) decisions = parsed.decisions;
16341
+ if (typeof parsed?.original_input === "string") originalInput = parsed.original_input;
16342
+ if (Array.isArray(parsed?.allowed_actions)) allowedActions = parsed.allowed_actions;
16343
+ } catch {
16344
+ }
16345
+ }
16346
+ const patch2 = {
16347
+ results: json.results,
16348
+ execution_argument: json.execution_argument,
16349
+ result: agentResult?.result,
16350
+ decisions,
16351
+ allowed_actions: allowedActions
16352
+ };
16353
+ if (originalInput && !patch2.execution_argument) {
16354
+ patch2.execution_argument = JSON.stringify({ original_input: originalInput });
16355
+ }
16356
+ queuePatch(executionId, patch2);
16357
+ } catch {
16358
+ }
16359
+ };
16360
+ const idle = (fn) => {
16361
+ const ric = window.requestIdleCallback;
16362
+ if (ric) ric(fn, { timeout: 1e3 });
16363
+ else setTimeout(fn, 32);
16364
+ };
16365
+ const nextIdle = () => new Promise((resolve) => idle(resolve));
16366
+ if (needsFetch.length > 0) {
16367
+ for (const id of needsFetch) processedRunIdsRef.current.add(id);
16368
+ const workers = Array.from({ length: Math.min(CONCURRENCY, needsFetch.length) }, async () => {
16369
+ while (!cancelled && i < needsFetch.length) {
16370
+ const id = needsFetch[i++];
16371
+ await nextIdle();
16372
+ if (cancelled) return;
16373
+ await fetchOne(id);
16374
+ }
16686
16375
  });
16376
+ Promise.all(workers).catch(() => {
16377
+ });
16378
+ }
16379
+ return () => {
16380
+ cancelled = true;
16381
+ if (flushTimer) clearTimeout(flushTimer);
16382
+ };
16383
+ }, [runs, apiKey, orgId]);
16384
+ const mergedRuns = runs.map((r) => {
16385
+ const patch2 = r.execution_id ? enrichedRuns[r.execution_id] : void 0;
16386
+ const merged = patch2 ? { ...r, ...patch2 } : r;
16387
+ return r.execution_id && abortedIds.has(r.execution_id) ? { ...merged, status: "ABORTED" } : merged;
16388
+ });
16389
+ const loadMore = useCallback8(() => {
16390
+ if (cursor && !isLoading) fetchRuns(true, cursor);
16391
+ }, [cursor, isLoading, fetchRuns]);
16392
+ const activeUsecase = usecaseFilters.find((u) => u.id === usecaseFilter) || null;
16393
+ const usecaseMatchedRuns = activeUsecase ? mergedRuns.filter((r) => {
16394
+ const hay = [
16395
+ r.execution_source,
16396
+ r.workflow?.name,
16397
+ r.workflow?.description,
16398
+ r.execution_argument,
16399
+ ...(r.workflow?.actions || []).flatMap((a) => [a.app_name, a.label])
16400
+ ].filter(Boolean).join(" ").toLowerCase();
16401
+ return activeUsecase.matchTokens.some((t) => hay.includes(t.toLowerCase()));
16402
+ }) : mergedRuns;
16403
+ const filteredRuns = debouncedQuery ? usecaseMatchedRuns.filter((r) => {
16404
+ const hay = [
16405
+ getRunTitle(r),
16406
+ getRunSubtitle(r),
16407
+ r.execution_id,
16408
+ r.execution_source,
16409
+ r.status
16410
+ ].filter(Boolean).join(" ").toLowerCase();
16411
+ return hay.includes(debouncedQuery.toLowerCase());
16412
+ }) : usecaseMatchedRuns;
16413
+ return /* @__PURE__ */ jsxs15(
16414
+ Box14,
16415
+ {
16416
+ className,
16417
+ sx: [{ display: "flex", flexDirection: "column", gap: 2 }, ...Array.isArray(sx) ? sx : sx ? [sx] : []],
16418
+ children: [
16419
+ (showSearchBar || showStatusChips) && /* @__PURE__ */ jsxs15(Box14, { sx: [{ display: "flex", alignItems: "center", justifyContent: "center", gap: 1.5, flexWrap: "wrap" }, ...Array.isArray(toolbarSx) ? toolbarSx : toolbarSx ? [toolbarSx] : []], children: [
16420
+ /* @__PURE__ */ jsxs15(
16421
+ Select2,
16422
+ {
16423
+ size: "small",
16424
+ value: usecaseFilter ? `uc:${usecaseFilter}` : workflowFilter,
16425
+ onChange: (e) => {
16426
+ const val = String(e.target.value);
16427
+ if (val.startsWith("uc:")) {
16428
+ setUsecaseFilter(val.slice(3));
16429
+ setWorkflowFilter("");
16430
+ } else {
16431
+ setUsecaseFilter("");
16432
+ setWorkflowFilter(val);
16433
+ }
16434
+ },
16435
+ displayEmpty: true,
16436
+ renderValue: (val) => {
16437
+ if (!val) return "All Agent runs";
16438
+ if (String(val).startsWith("uc:")) {
16439
+ const uc = usecaseFilters.find((u) => u.id === String(val).slice(3));
16440
+ return uc?.label || "Usecase agent";
16441
+ }
16442
+ const wf = agentWorkflows.find((w) => w.id === val);
16443
+ return wf?.name || "Selected workflow";
16444
+ },
16445
+ sx: {
16446
+ height: 36,
16447
+ minWidth: 200,
16448
+ maxWidth: 260,
16449
+ fontSize: "0.85rem",
16450
+ bgcolor: "hsl(var(--card))",
16451
+ color: "hsl(var(--foreground))",
16452
+ borderRadius: 1.5,
16453
+ "& .MuiOutlinedInput-notchedOutline": { borderColor: "hsl(var(--border))" },
16454
+ "&:hover .MuiOutlinedInput-notchedOutline": { borderColor: "hsl(var(--muted-foreground) / 0.3)" },
16455
+ "&.Mui-focused .MuiOutlinedInput-notchedOutline": { borderColor: "hsl(var(--primary))" }
16456
+ },
16457
+ MenuProps: {
16458
+ slotProps: {
16459
+ paper: {
16460
+ sx: {
16461
+ bgcolor: "hsl(var(--card))",
16462
+ border: "1px solid hsl(var(--border))",
16463
+ color: "hsl(var(--foreground))",
16464
+ maxHeight: 320
16465
+ }
16466
+ }
16467
+ }
16468
+ },
16469
+ children: [
16470
+ /* @__PURE__ */ jsx21(MenuItem2, { value: "", sx: { fontSize: "0.85rem" }, children: "All Agent runs" }),
16471
+ usecaseFilters.length > 0 && /* @__PURE__ */ jsx21(Divider2, { sx: { borderColor: "hsl(var(--border))", my: 0.5 } }),
16472
+ usecaseFilters.length > 0 && /* @__PURE__ */ jsx21(
16473
+ Box14,
16474
+ {
16475
+ sx: {
16476
+ px: 1.5,
16477
+ py: 0.5,
16478
+ fontSize: "0.7rem",
16479
+ textTransform: "uppercase",
16480
+ letterSpacing: "0.04em",
16481
+ color: "hsl(var(--muted-foreground))",
16482
+ pointerEvents: "none"
16483
+ },
16484
+ children: "Usecase agents"
16485
+ }
16486
+ ),
16487
+ usecaseFilters.map((u) => /* @__PURE__ */ jsx21(MenuItem2, { value: `uc:${u.id}`, sx: { fontSize: "0.85rem" }, children: u.label }, u.id)),
16488
+ agentWorkflows.length > 0 && usecaseFilters.length > 0 && /* @__PURE__ */ jsx21(Divider2, { sx: { borderColor: "hsl(var(--border))", my: 0.5 } }),
16489
+ agentWorkflows.map((w) => /* @__PURE__ */ jsx21(MenuItem2, { value: w.id, sx: { fontSize: "0.85rem" }, children: w.name }, w.id)),
16490
+ /* @__PURE__ */ jsx21(Divider2, { sx: { borderColor: "hsl(var(--border))", my: 0.5 } }),
16491
+ /* @__PURE__ */ jsx21(
16492
+ Box14,
16493
+ {
16494
+ sx: {
16495
+ px: 1.5,
16496
+ py: 1,
16497
+ fontSize: "0.72rem",
16498
+ color: "hsl(var(--muted-foreground))",
16499
+ lineHeight: 1.45,
16500
+ whiteSpace: "normal",
16501
+ maxWidth: 280,
16502
+ pointerEvents: "none"
16503
+ },
16504
+ children: "Scheduled agent runs will appear in this list once you set them up."
16505
+ }
16506
+ )
16507
+ ]
16508
+ }
16509
+ ),
16510
+ showSearchBar && /* @__PURE__ */ jsx21(
16511
+ TextField5,
16512
+ {
16513
+ placeholder: "Search results...",
16514
+ size: "small",
16515
+ value: searchQuery,
16516
+ onChange: (e) => updateSearchQuery(e.target.value),
16517
+ slotProps: {
16518
+ input: {
16519
+ startAdornment: /* @__PURE__ */ jsx21(InputAdornment, { position: "start", children: /* @__PURE__ */ jsx21(SearchIcon, { size: 18, color: "hsl(var(--muted-foreground))" }) })
16520
+ }
16521
+ },
16522
+ sx: {
16523
+ flex: 1,
16524
+ maxWidth: 280,
16525
+ minWidth: 160,
16526
+ "& .MuiOutlinedInput-root": {
16527
+ bgcolor: "hsl(var(--card))",
16528
+ borderRadius: 1.5,
16529
+ fontSize: "0.85rem",
16530
+ height: 36,
16531
+ "& fieldset": { borderColor: "hsl(var(--border))" },
16532
+ "&:hover fieldset": { borderColor: "hsl(var(--muted-foreground) / 0.3)" },
16533
+ "&.Mui-focused fieldset": { borderColor: "hsl(var(--primary))" }
16534
+ },
16535
+ "& .MuiInputBase-input": { color: "hsl(var(--foreground))" }
16536
+ }
16537
+ }
16538
+ ),
16539
+ showStatusChips && /* @__PURE__ */ jsx21(
16540
+ SegmentedControl,
16541
+ {
16542
+ size: "sm",
16543
+ ariaLabel: "Filter by status",
16544
+ value: statusFilter,
16545
+ onChange: setStatusFilter,
16546
+ options: STATUS_FILTERS.map((f) => ({ value: f.value, label: f.label }))
16547
+ }
16548
+ ),
16549
+ isLoading && runs.length > 0 && /* @__PURE__ */ jsx21(CircularProgress5, { size: 16, sx: { color: "hsl(var(--primary))", ml: 0.5 } })
16550
+ ] }),
16551
+ selectedAgentWorkflow && /* @__PURE__ */ jsxs15(
16552
+ Box14,
16553
+ {
16554
+ sx: {
16555
+ display: "flex",
16556
+ alignItems: "center",
16557
+ gap: 1.5,
16558
+ p: 1.5,
16559
+ borderRadius: 2,
16560
+ border: "1px solid hsl(var(--border))",
16561
+ bgcolor: "hsl(var(--card))"
16562
+ },
16563
+ children: [
16564
+ /* @__PURE__ */ jsxs15(Box14, { sx: { flex: 1, minWidth: 0 }, children: [
16565
+ /* @__PURE__ */ jsx21(Typography12, { sx: { fontSize: "0.85rem", fontWeight: 600, color: "hsl(var(--foreground))", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: selectedAgentWorkflow.name }),
16566
+ selectedAgentWorkflow.description && /* @__PURE__ */ jsx21(Typography12, { sx: { fontSize: "0.75rem", color: "hsl(var(--muted-foreground))", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: selectedAgentWorkflow.description })
16567
+ ] }),
16568
+ /* @__PURE__ */ jsx21(
16569
+ Button8,
16570
+ {
16571
+ size: "small",
16572
+ startIcon: /* @__PURE__ */ jsx21(Pencil, { size: 14 }),
16573
+ onClick: openEditPrompt,
16574
+ sx: {
16575
+ height: 36,
16576
+ border: "1px solid hsl(var(--border))",
16577
+ borderRadius: 1.5,
16578
+ color: "hsl(var(--foreground))",
16579
+ textTransform: "none",
16580
+ fontSize: "0.8rem",
16581
+ px: 1.5,
16582
+ "&:hover": { bgcolor: "hsl(var(--muted))" }
16583
+ },
16584
+ children: "Edit"
16585
+ }
16586
+ ),
16587
+ /* @__PURE__ */ jsx21(
16588
+ Button8,
16589
+ {
16590
+ size: "small",
16591
+ startIcon: /* @__PURE__ */ jsx21(StopCircle, { size: 14 }),
16592
+ onClick: () => setStopOpen(true),
16593
+ sx: {
16594
+ height: 36,
16595
+ border: "1px solid hsl(var(--severity-critical, 0 72% 55%) / 0.4)",
16596
+ borderRadius: 1.5,
16597
+ color: "hsl(var(--severity-critical, 0 72% 55%))",
16598
+ textTransform: "none",
16599
+ fontSize: "0.8rem",
16600
+ px: 1.5,
16601
+ "&:hover": { bgcolor: "hsla(var(--severity-critical, 0 72% 55%) / 0.08)" }
16602
+ },
16603
+ children: "Stop schedule"
16604
+ }
16605
+ )
16606
+ ]
16607
+ }
16608
+ ),
16609
+ isLoading && runs.length === 0 ? /* @__PURE__ */ jsx21(Box14, { sx: { display: "flex", justifyContent: "center", py: 8 }, children: /* @__PURE__ */ jsx21(CircularProgress5, { size: 28, sx: { color: "hsl(var(--primary))" } }) }) : error ? /* @__PURE__ */ jsxs15(Box14, { sx: { display: "flex", flexDirection: "column", alignItems: "center", textAlign: "center", py: 6 }, children: [
16610
+ /* @__PURE__ */ jsx21(AlertCircle, { size: 28, style: { color: "hsl(var(--severity-critical, 0 72% 55%))", marginBottom: 8 } }),
16611
+ /* @__PURE__ */ jsx21(
16612
+ Typography12,
16613
+ {
16614
+ sx: { color: "hsl(var(--severity-critical, 0 72% 55%))", fontSize: "0.9rem", mb: 1 },
16615
+ children: error
16616
+ }
16617
+ ),
16618
+ /* @__PURE__ */ jsx21(
16619
+ Button8,
16620
+ {
16621
+ size: "small",
16622
+ onClick: () => fetchRuns(false, ""),
16623
+ sx: { color: "hsl(var(--primary))", textTransform: "none" },
16624
+ children: "Try again"
16625
+ }
16626
+ )
16627
+ ] }) : filteredRuns.length === 0 ? /* @__PURE__ */ jsxs15(Box14, { sx: { display: "flex", flexDirection: "column", alignItems: "center", textAlign: "center", py: 8 }, children: [
16628
+ /* @__PURE__ */ jsx21(
16629
+ Activity,
16630
+ {
16631
+ size: 40,
16632
+ style: { color: "hsl(var(--muted-foreground))", marginBottom: 12 }
16633
+ }
16634
+ ),
16635
+ /* @__PURE__ */ jsx21(Typography12, { sx: { color: "hsl(var(--muted-foreground))", fontSize: "0.9rem" }, children: emptyTitle }),
16636
+ /* @__PURE__ */ jsx21(
16637
+ Typography12,
16638
+ {
16639
+ sx: { color: "hsl(var(--muted-foreground))", fontSize: "0.8rem", mt: 0.5, opacity: 0.7 },
16640
+ children: emptySubtitle
16641
+ }
16642
+ )
16643
+ ] }) : /* @__PURE__ */ jsxs15(Box14, { sx: { display: "flex", flexDirection: "column", gap: 1.5 }, children: [
16644
+ filteredRuns.map((run2, idx) => /* @__PURE__ */ jsx21(
16645
+ AgentRunRow,
16646
+ {
16647
+ run: run2,
16648
+ onClick: () => {
16649
+ setLastOpenedAgentRun(run2.execution_id);
16650
+ setLastOpenedRunId(run2.execution_id || null);
16651
+ onRunClick?.(run2);
16652
+ },
16653
+ sx: [
16654
+ ...Array.isArray(rowSx) ? rowSx : rowSx ? [rowSx] : [],
16655
+ ...run2.execution_id && run2.execution_id === lastOpenedRunId ? [{
16656
+ borderColor: "hsl(var(--primary) / 0.55)",
16657
+ bgcolor: "hsl(var(--primary) / 0.06)"
16658
+ }] : []
16659
+ ],
16660
+ appIcons,
16661
+ onAppClick: (app2) => setAppDrawer(app2),
16662
+ apiKey,
16663
+ apiBaseUrl,
16664
+ orgId,
16665
+ abortingIds,
16666
+ onAbort: handleAbort
16667
+ },
16668
+ run2.execution_id || idx
16669
+ )),
16670
+ hasMore && /* @__PURE__ */ jsx21(Box14, { sx: { display: "flex", justifyContent: "center", mt: 1 }, children: /* @__PURE__ */ jsxs15(
16671
+ Button8,
16672
+ {
16673
+ size: "small",
16674
+ onClick: loadMore,
16675
+ disabled: isLoading,
16676
+ sx: { color: "hsl(var(--primary))", textTransform: "none", fontSize: "0.8rem" },
16677
+ children: [
16678
+ isLoading ? /* @__PURE__ */ jsx21(CircularProgress5, { size: 14, sx: { mr: 1 } }) : null,
16679
+ "Load more"
16680
+ ]
16681
+ }
16682
+ ) })
16683
+ ] }),
16684
+ /* @__PURE__ */ jsxs15(
16685
+ Dialog2,
16686
+ {
16687
+ open: stopOpen,
16688
+ onClose: () => stopLoading ? null : setStopOpen(false),
16689
+ slotProps: { paper: { sx: { bgcolor: "hsl(var(--card))", color: "hsl(var(--foreground))", border: "1px solid hsl(var(--border))" } } },
16690
+ children: [
16691
+ /* @__PURE__ */ jsx21(DialogTitle2, { sx: { fontSize: "1rem", fontWeight: 600 }, children: "Stop schedule?" }),
16692
+ /* @__PURE__ */ jsx21(DialogContent2, { children: /* @__PURE__ */ jsxs15(Typography12, { sx: { fontSize: "0.85rem", color: "hsl(var(--muted-foreground))" }, children: [
16693
+ 'This will stop "',
16694
+ selectedAgentWorkflow?.name,
16695
+ '" and delete the scheduled workflow. Past executions remain visible. This cannot be undone.'
16696
+ ] }) }),
16697
+ /* @__PURE__ */ jsxs15(DialogActions, { sx: { px: 3, pb: 2 }, children: [
16698
+ /* @__PURE__ */ jsx21(Button8, { onClick: () => setStopOpen(false), disabled: stopLoading, sx: { textTransform: "none", color: "hsl(var(--muted-foreground))" }, children: "Cancel" }),
16699
+ /* @__PURE__ */ jsxs15(
16700
+ Button8,
16701
+ {
16702
+ onClick: confirmStop,
16703
+ disabled: stopLoading,
16704
+ sx: { textTransform: "none", bgcolor: "hsl(var(--severity-critical, 0 72% 55%))", color: "hsl(var(--primary-foreground))", "&:hover": { bgcolor: "hsla(var(--severity-critical, 0 72% 55%) / 0.9)" } },
16705
+ children: [
16706
+ stopLoading ? /* @__PURE__ */ jsx21(CircularProgress5, { size: 16, sx: { color: "hsl(var(--primary-foreground))", mr: 1 } }) : null,
16707
+ "Stop schedule"
16708
+ ]
16709
+ }
16710
+ )
16711
+ ] })
16712
+ ]
16713
+ }
16714
+ ),
16715
+ renderAppDetailDrawer ? renderAppDetailDrawer({
16716
+ open: !!appDrawer,
16717
+ onClose: () => setAppDrawer(null),
16718
+ appName: appDrawer?.name || null,
16719
+ appId: appDrawer?.id || null,
16720
+ activeOrgId: orgId || null,
16721
+ globalUrl: globalUrl || apiBaseUrl,
16722
+ theme,
16723
+ colorMode
16724
+ }) : null
16725
+ ]
16687
16726
  }
16688
- } else if (rawMarkdown) {
16689
- renderedDocs.push({
16690
- title,
16691
- slug: activeDoc?.slug,
16692
- content: sanitizeDocMarkdown(rawMarkdown, 3e4),
16693
- isCurrent: true
16694
- });
16695
- }
16696
- let docSection = "";
16697
- if (renderedDocs.length > 0) {
16698
- const docBlocks = renderedDocs.filter((d) => d.content && d.content.trim().length > 0).map((d) => {
16699
- const marker = d.isCurrent ? " (Currently Viewed)" : "";
16700
- return `## Document: ${d.title}${marker}
16701
-
16702
- ${d.content}`;
16703
- }).join("\n\n---\n\n");
16704
- docSection = `# Documentation Category: ${categoryName}
16705
-
16706
- ${docBlocks}`;
16707
- }
16708
- const markedExcerpt = (activeDoc?.selectedText || getActiveDocSelectedText() || "").trim();
16709
- const excerptBlock = markedExcerpt ? `
16710
-
16711
- ## User-Marked Excerpt (Focus of Question)
16712
- The user highlighted the following text in "${title}":
16713
- """
16714
- ${markedExcerpt.slice(0, 4e3)}
16715
- """` : "";
16716
- if (docSection) {
16717
- return `Answer the users question about Shuffle ${categoryName} based on the following complete documentation for the ${categoryName} category:
16718
- ${DOC_PROMPT_DELIMITER_START}
16719
- ${docSection}${excerptBlock}
16720
- ${DOC_PROMPT_DELIMITER_END}
16721
-
16722
- User Question:
16723
- ${trimmedQuestion}`;
16724
- }
16725
- return `Answer the users question about Shuffle ${categoryName}:${excerptBlock}
16726
-
16727
- ${trimmedQuestion}`;
16727
+ );
16728
16728
  };
16729
+ var AgentActivityList_default = AgentActivityList;
16729
16730
 
16730
16731
  // agentChat.ts
16731
16732
  var AGENT_WORKFLOW_CHANGED_EVENT = "shuffle:agent_workflow_changed";