@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.
- package/dist/index.js +2198 -2197
- package/dist/index.mjs +2198 -2197
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -15296,1867 +15296,276 @@ var collectLlmImageAttachments = (root2, maxDepth = 10) => {
|
|
|
15296
15296
|
return deduped;
|
|
15297
15297
|
};
|
|
15298
15298
|
|
|
15299
|
-
// components/
|
|
15300
|
-
var
|
|
15299
|
+
// ../components/docs/tocUtils.ts
|
|
15300
|
+
var isTocHeading = (text2) => {
|
|
15301
|
+
const norm4 = text2.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
15302
|
+
return norm4 === "tableofcontents" || norm4 === "tableofcontent" || norm4 === "toc";
|
|
15303
|
+
};
|
|
15304
|
+
var stripMarkdownInline = (value) => value.replace(/!\[[^\]]*\]\([^)]*\)/g, "").replace(/\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/<[^>]+>/g, "").replace(/[`*_~#>]/g, "").replace(/\{#[^}]+\}/g, "").replace(/\s+/g, " ").trim();
|
|
15305
|
+
var stripInContentToc = (markdown) => {
|
|
15306
|
+
if (!markdown) return "";
|
|
15307
|
+
const lines = markdown.split("\n");
|
|
15308
|
+
const result = [];
|
|
15309
|
+
let inToc = false;
|
|
15310
|
+
for (let i = 0; i < lines.length; i++) {
|
|
15311
|
+
const line = lines[i];
|
|
15312
|
+
const trimmed = line.trim();
|
|
15313
|
+
const headingMatch = trimmed.match(/^(#{1,4})\s+(.+)$/);
|
|
15314
|
+
if (headingMatch) {
|
|
15315
|
+
const headingText = stripMarkdownInline(headingMatch[2]);
|
|
15316
|
+
if (isTocHeading(headingText)) {
|
|
15317
|
+
inToc = true;
|
|
15318
|
+
continue;
|
|
15319
|
+
} else if (inToc) {
|
|
15320
|
+
inToc = false;
|
|
15321
|
+
}
|
|
15322
|
+
}
|
|
15323
|
+
if (inToc) {
|
|
15324
|
+
if (!trimmed || /^[-*+]\s+/.test(trimmed) || /^\d+\.\s+/.test(trimmed) || /^[-*_]{3,}$/.test(trimmed) || /^>/.test(trimmed) || /^<ul[\s\S]*?<\/ul>$/i.test(trimmed)) {
|
|
15325
|
+
continue;
|
|
15326
|
+
} else {
|
|
15327
|
+
inToc = false;
|
|
15328
|
+
}
|
|
15329
|
+
}
|
|
15330
|
+
result.push(line);
|
|
15331
|
+
}
|
|
15332
|
+
return result.join("\n");
|
|
15333
|
+
};
|
|
15301
15334
|
|
|
15302
|
-
// ../
|
|
15303
|
-
var
|
|
15304
|
-
|
|
15305
|
-
|
|
15306
|
-
|
|
15307
|
-
|
|
15308
|
-
|
|
15309
|
-
fontSize: "11px",
|
|
15310
|
-
itemPaddingY: "4px",
|
|
15311
|
-
itemPaddingX: "10px",
|
|
15312
|
-
itemGap: "6px",
|
|
15313
|
-
countMin: "16px",
|
|
15314
|
-
countH: "16px",
|
|
15315
|
-
countPx: "4px",
|
|
15316
|
-
countFs: "10px",
|
|
15317
|
-
dividerH: "16px",
|
|
15318
|
-
dividerMx: "2px"
|
|
15335
|
+
// ../lib/docGroups.ts
|
|
15336
|
+
var DOC_GROUPS = [
|
|
15337
|
+
{
|
|
15338
|
+
id: "usability",
|
|
15339
|
+
label: "Usability",
|
|
15340
|
+
description: "Platform overview, getting started, tenants, features, and API reference.",
|
|
15341
|
+
slugs: ["getting_started", "getting-started", "about", "tenants", "features", "api"]
|
|
15319
15342
|
},
|
|
15320
|
-
|
|
15321
|
-
|
|
15322
|
-
|
|
15323
|
-
|
|
15324
|
-
|
|
15325
|
-
|
|
15326
|
-
|
|
15327
|
-
|
|
15328
|
-
|
|
15329
|
-
|
|
15330
|
-
|
|
15331
|
-
|
|
15343
|
+
{
|
|
15344
|
+
id: "automation",
|
|
15345
|
+
label: "Automation",
|
|
15346
|
+
description: "Workflows, apps, AI agents, and playbook automations.",
|
|
15347
|
+
slugs: ["workflows", "apps", "ai"]
|
|
15348
|
+
},
|
|
15349
|
+
{
|
|
15350
|
+
id: "security",
|
|
15351
|
+
label: "Security",
|
|
15352
|
+
description: "Incident triage, vulnerability management, host posture, and response.",
|
|
15353
|
+
slugs: ["incidents", "vulnerabilities", "monitors", "host-monitors", "detection", "sigma"]
|
|
15354
|
+
},
|
|
15355
|
+
{
|
|
15356
|
+
id: "infrastructure",
|
|
15357
|
+
label: "Infrastructure",
|
|
15358
|
+
description: "Architecture, configuration, extensions, and troubleshooting.",
|
|
15359
|
+
slugs: ["architecture", "configuration", "extensions", "troubleshooting"]
|
|
15332
15360
|
}
|
|
15361
|
+
];
|
|
15362
|
+
var DOC_LABEL_OVERRIDES = {
|
|
15363
|
+
ai: "Agents & AI",
|
|
15364
|
+
api: "API Reference",
|
|
15365
|
+
getting_started: "Getting Started",
|
|
15366
|
+
incidents: "Incidents & Cases",
|
|
15367
|
+
vulnerabilities: "Vulnerabilities",
|
|
15368
|
+
monitors: "Host Monitors",
|
|
15369
|
+
"host-monitors": "Host Monitors",
|
|
15370
|
+
about: "About Shuffle",
|
|
15371
|
+
tenants: "Tenants & Multi-Tenancy",
|
|
15372
|
+
features: "Features Overview",
|
|
15373
|
+
workflows: "Workflows",
|
|
15374
|
+
apps: "Apps & Integrations",
|
|
15375
|
+
extensions: "Extensions",
|
|
15376
|
+
architecture: "Architecture",
|
|
15377
|
+
configuration: "Configuration",
|
|
15378
|
+
troubleshooting: "Troubleshooting"
|
|
15333
15379
|
};
|
|
15334
|
-
var
|
|
15335
|
-
|
|
15336
|
-
|
|
15337
|
-
|
|
15338
|
-
|
|
15339
|
-
|
|
15380
|
+
var getDocGroup = (slug) => {
|
|
15381
|
+
const usabilityGroup = DOC_GROUPS.find((g) => g.id === "usability") || DOC_GROUPS[0];
|
|
15382
|
+
if (!slug || slug === "index") return usabilityGroup;
|
|
15383
|
+
const clean = slug.toLowerCase().replace(/_+/g, "-");
|
|
15384
|
+
for (const group of DOC_GROUPS) {
|
|
15385
|
+
if (group.slugs.includes(clean)) {
|
|
15386
|
+
return group;
|
|
15387
|
+
}
|
|
15340
15388
|
}
|
|
15341
|
-
return
|
|
15342
|
-
}
|
|
15343
|
-
function SegmentedControl({
|
|
15344
|
-
options,
|
|
15345
|
-
value,
|
|
15346
|
-
onChange,
|
|
15347
|
-
size = "md",
|
|
15348
|
-
variant = "outline",
|
|
15349
|
-
className,
|
|
15350
|
-
ariaLabel,
|
|
15351
|
-
layoutId,
|
|
15352
|
-
trailing
|
|
15353
|
-
}) {
|
|
15354
|
-
const autoId = useUniqueId("segmented");
|
|
15355
|
-
const groupId = layoutId ?? autoId;
|
|
15356
|
-
const s = SIZE[size];
|
|
15357
|
-
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_framer_motion6.LayoutGroup, { id: groupId, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
15358
|
-
"div",
|
|
15359
|
-
{
|
|
15360
|
-
role: "tablist",
|
|
15361
|
-
"aria-label": ariaLabel,
|
|
15362
|
-
className,
|
|
15363
|
-
style: {
|
|
15364
|
-
display: "inline-flex",
|
|
15365
|
-
alignItems: "center",
|
|
15366
|
-
gap: "2px",
|
|
15367
|
-
borderRadius: 9999,
|
|
15368
|
-
padding: s.containerPadding,
|
|
15369
|
-
fontSize: s.fontSize,
|
|
15370
|
-
border: variant === "outline" ? "1px solid hsl(var(--border))" : "1px solid transparent",
|
|
15371
|
-
background: variant === "outline" ? "transparent" : "hsl(var(--muted) / 0.4)"
|
|
15372
|
-
},
|
|
15373
|
-
children: [
|
|
15374
|
-
options.map((opt, i) => {
|
|
15375
|
-
if (opt.type === "divider") {
|
|
15376
|
-
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
15377
|
-
"span",
|
|
15378
|
-
{
|
|
15379
|
-
"aria-hidden": true,
|
|
15380
|
-
style: {
|
|
15381
|
-
width: 1,
|
|
15382
|
-
height: s.dividerH,
|
|
15383
|
-
margin: `0 ${s.dividerMx}`,
|
|
15384
|
-
background: "hsl(var(--border))",
|
|
15385
|
-
alignSelf: "center"
|
|
15386
|
-
}
|
|
15387
|
-
},
|
|
15388
|
-
opt.key ?? `divider-${i}`
|
|
15389
|
-
);
|
|
15390
|
-
}
|
|
15391
|
-
const isAction2 = opt.type === "action";
|
|
15392
|
-
const active2 = !isAction2 && opt.value === value;
|
|
15393
|
-
const key = isAction2 ? opt.key : opt.value;
|
|
15394
|
-
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
15395
|
-
"button",
|
|
15396
|
-
{
|
|
15397
|
-
type: "button",
|
|
15398
|
-
"data-tour": opt.dataTour,
|
|
15399
|
-
"data-active": active2 ? "true" : "false",
|
|
15400
|
-
role: isAction2 ? "button" : "tab",
|
|
15401
|
-
"aria-selected": isAction2 ? void 0 : active2,
|
|
15402
|
-
title: opt.title,
|
|
15403
|
-
disabled: opt.disabled,
|
|
15404
|
-
onClick: () => {
|
|
15405
|
-
if (opt.disabled) return;
|
|
15406
|
-
if (isAction2) opt.onClick();
|
|
15407
|
-
else onChange(opt.value);
|
|
15408
|
-
},
|
|
15409
|
-
style: {
|
|
15410
|
-
position: "relative",
|
|
15411
|
-
display: "inline-flex",
|
|
15412
|
-
alignItems: "center",
|
|
15413
|
-
gap: s.itemGap,
|
|
15414
|
-
padding: `${s.itemPaddingY} ${s.itemPaddingX}`,
|
|
15415
|
-
borderRadius: 9999,
|
|
15416
|
-
fontWeight: 500,
|
|
15417
|
-
fontSize: s.fontSize,
|
|
15418
|
-
background: "transparent",
|
|
15419
|
-
border: "none",
|
|
15420
|
-
cursor: opt.disabled ? "not-allowed" : "pointer",
|
|
15421
|
-
color: active2 ? "hsl(var(--foreground))" : "hsl(var(--muted-foreground))",
|
|
15422
|
-
opacity: opt.disabled ? 0.5 : 1,
|
|
15423
|
-
transition: "color 200ms ease",
|
|
15424
|
-
outline: "none",
|
|
15425
|
-
fontFamily: "inherit"
|
|
15426
|
-
},
|
|
15427
|
-
onMouseEnter: (e) => {
|
|
15428
|
-
if (!active2 && !opt.disabled) {
|
|
15429
|
-
e.currentTarget.style.color = "hsl(var(--foreground))";
|
|
15430
|
-
}
|
|
15431
|
-
},
|
|
15432
|
-
onMouseLeave: (e) => {
|
|
15433
|
-
if (!active2 && !opt.disabled) {
|
|
15434
|
-
e.currentTarget.style.color = "hsl(var(--muted-foreground))";
|
|
15435
|
-
}
|
|
15436
|
-
},
|
|
15437
|
-
children: [
|
|
15438
|
-
active2 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
15439
|
-
import_framer_motion6.motion.span,
|
|
15440
|
-
{
|
|
15441
|
-
layoutId: `${groupId}-pill`,
|
|
15442
|
-
style: {
|
|
15443
|
-
position: "absolute",
|
|
15444
|
-
inset: 0,
|
|
15445
|
-
borderRadius: 9999,
|
|
15446
|
-
background: "hsl(var(--muted))",
|
|
15447
|
-
border: "1px solid hsl(var(--border))"
|
|
15448
|
-
},
|
|
15449
|
-
initial: false,
|
|
15450
|
-
transition: {
|
|
15451
|
-
type: "spring",
|
|
15452
|
-
stiffness: 260,
|
|
15453
|
-
damping: 30
|
|
15454
|
-
}
|
|
15455
|
-
}
|
|
15456
|
-
),
|
|
15457
|
-
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
15458
|
-
"span",
|
|
15459
|
-
{
|
|
15460
|
-
style: {
|
|
15461
|
-
position: "relative",
|
|
15462
|
-
zIndex: 1,
|
|
15463
|
-
display: "inline-flex",
|
|
15464
|
-
alignItems: "center",
|
|
15465
|
-
gap: s.itemGap
|
|
15466
|
-
},
|
|
15467
|
-
children: opt.label
|
|
15468
|
-
}
|
|
15469
|
-
),
|
|
15470
|
-
!isAction2 && (typeof opt.count === "number" || typeof opt.count === "string") && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
15471
|
-
"span",
|
|
15472
|
-
{
|
|
15473
|
-
style: {
|
|
15474
|
-
position: "relative",
|
|
15475
|
-
zIndex: 1,
|
|
15476
|
-
display: "inline-flex",
|
|
15477
|
-
alignItems: "center",
|
|
15478
|
-
justifyContent: "center",
|
|
15479
|
-
minWidth: s.countMin,
|
|
15480
|
-
height: s.countH,
|
|
15481
|
-
padding: `0 ${s.countPx}`,
|
|
15482
|
-
fontSize: s.countFs,
|
|
15483
|
-
fontWeight: 600,
|
|
15484
|
-
fontVariantNumeric: "tabular-nums",
|
|
15485
|
-
borderRadius: 6,
|
|
15486
|
-
border: "1px solid hsl(var(--border) / 0.6)",
|
|
15487
|
-
color: active2 ? "hsl(var(--foreground))" : "hsl(var(--muted-foreground))",
|
|
15488
|
-
background: "transparent"
|
|
15489
|
-
},
|
|
15490
|
-
children: opt.count
|
|
15491
|
-
}
|
|
15492
|
-
)
|
|
15493
|
-
]
|
|
15494
|
-
},
|
|
15495
|
-
key
|
|
15496
|
-
);
|
|
15497
|
-
}),
|
|
15498
|
-
trailing != null && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
15499
|
-
"span",
|
|
15500
|
-
{
|
|
15501
|
-
style: {
|
|
15502
|
-
display: "inline-flex",
|
|
15503
|
-
alignItems: "center",
|
|
15504
|
-
alignSelf: "center"
|
|
15505
|
-
},
|
|
15506
|
-
children: trailing
|
|
15507
|
-
}
|
|
15508
|
-
)
|
|
15509
|
-
]
|
|
15510
|
-
}
|
|
15511
|
-
) });
|
|
15512
|
-
}
|
|
15513
|
-
|
|
15514
|
-
// components/AgentActivityList.tsx
|
|
15515
|
-
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
15516
|
-
var STATUS_CONFIG = {
|
|
15517
|
-
FINISHED: { icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.CheckCircle, { size: 16 }), color: "hsl(var(--severity-low, 142 71% 45%))", label: "Completed" },
|
|
15518
|
-
SUCCESS: { icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.CheckCircle, { size: 16 }), color: "hsl(var(--severity-low, 142 71% 45%))", label: "Completed" },
|
|
15519
|
-
FAILED: { icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.XCircle, { size: 16 }), color: "hsl(var(--severity-critical, 0 72% 55%))", label: "Failed" },
|
|
15520
|
-
ABORTED: { icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.XCircle, { size: 16 }), color: "hsl(var(--severity-critical, 0 72% 55%))", label: "Aborted" },
|
|
15521
|
-
EXECUTING: { icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Loader2, { size: 16 }), color: "hsl(var(--severity-medium, 38 92% 50%))", label: "Running" },
|
|
15522
|
-
RUNNING: { icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Loader2, { size: 16 }), color: "hsl(var(--severity-medium, 38 92% 50%))", label: "Running" },
|
|
15523
|
-
WAITING: { icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Clock, { size: 16 }), color: "hsl(var(--severity-info, 217 91% 60%))", label: "Waiting" },
|
|
15524
|
-
LIMIT_REACHED: { icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react8.AlertTriangle, { size: 16 }), color: "hsl(var(--severity-critical, 0 72% 55%))", label: "Limit reached" }
|
|
15389
|
+
return usabilityGroup;
|
|
15525
15390
|
};
|
|
15526
|
-
var
|
|
15527
|
-
const
|
|
15528
|
-
if (
|
|
15529
|
-
|
|
15530
|
-
const d = diagnoseOutputWarning(run2);
|
|
15531
|
-
if (d?.kind === "token_limit") return "LIMIT_REACHED";
|
|
15532
|
-
} catch {
|
|
15533
|
-
}
|
|
15391
|
+
var getDocDisplayLabel = (slug, fallbackName) => {
|
|
15392
|
+
const clean = (slug || "").toLowerCase().replace(/_+/g, "-");
|
|
15393
|
+
if (DOC_LABEL_OVERRIDES[clean]) {
|
|
15394
|
+
return DOC_LABEL_OVERRIDES[clean];
|
|
15534
15395
|
}
|
|
15535
|
-
|
|
15396
|
+
const raw = fallbackName || slug || "";
|
|
15397
|
+
return raw.replace(/[_-]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
15536
15398
|
};
|
|
15537
|
-
|
|
15538
|
-
|
|
15539
|
-
var
|
|
15540
|
-
var
|
|
15541
|
-
|
|
15542
|
-
|
|
15543
|
-
|
|
15544
|
-
|
|
15545
|
-
|
|
15546
|
-
|
|
15547
|
-
|
|
15548
|
-
|
|
15549
|
-
return { key, category };
|
|
15399
|
+
|
|
15400
|
+
// ../components/docs/docGroups.ts
|
|
15401
|
+
var groupDocsCache = /* @__PURE__ */ new Map();
|
|
15402
|
+
var getCachedGroupDocs = (groupId, folder) => {
|
|
15403
|
+
const cacheKey3 = `${folder || "docs"}:${groupId}`;
|
|
15404
|
+
const cached2 = groupDocsCache.get(cacheKey3);
|
|
15405
|
+
if (!cached2) return [];
|
|
15406
|
+
return Object.entries(cached2).map(([slug, content]) => ({
|
|
15407
|
+
slug,
|
|
15408
|
+
title: getDocDisplayLabel(slug),
|
|
15409
|
+
content
|
|
15410
|
+
}));
|
|
15550
15411
|
};
|
|
15551
|
-
|
|
15552
|
-
|
|
15553
|
-
|
|
15554
|
-
|
|
15555
|
-
if (
|
|
15556
|
-
|
|
15412
|
+
|
|
15413
|
+
// ../lib/incidentPromptContext.ts
|
|
15414
|
+
var GLOBAL_INCIDENT_KEY = "__shuffleActiveIncidentContext";
|
|
15415
|
+
var getGlobalScope = () => {
|
|
15416
|
+
if (typeof globalThis !== "undefined") return globalThis;
|
|
15417
|
+
if (typeof window !== "undefined") return window;
|
|
15418
|
+
if (typeof global !== "undefined") return global;
|
|
15419
|
+
return null;
|
|
15557
15420
|
};
|
|
15558
|
-
var
|
|
15559
|
-
|
|
15560
|
-
|
|
15561
|
-
|
|
15562
|
-
|
|
15563
|
-
|
|
15564
|
-
if (typeof obj.input === "string" && obj.input.trim()) {
|
|
15565
|
-
return obj.input.trim();
|
|
15421
|
+
var getActiveIncidentPromptContext = () => {
|
|
15422
|
+
const scope2 = getGlobalScope();
|
|
15423
|
+
if (!scope2) return null;
|
|
15424
|
+
const info = scope2[GLOBAL_INCIDENT_KEY];
|
|
15425
|
+
if (info && typeof info === "object") {
|
|
15426
|
+
return info;
|
|
15566
15427
|
}
|
|
15567
15428
|
return null;
|
|
15568
15429
|
};
|
|
15569
|
-
var
|
|
15570
|
-
|
|
15571
|
-
|
|
15572
|
-
if (
|
|
15573
|
-
const
|
|
15574
|
-
|
|
15575
|
-
|
|
15576
|
-
|
|
15577
|
-
|
|
15578
|
-
|
|
15579
|
-
|
|
15580
|
-
|
|
15581
|
-
|
|
15582
|
-
|
|
15583
|
-
|
|
15584
|
-
|
|
15585
|
-
|
|
15586
|
-
|
|
15587
|
-
|
|
15430
|
+
var INCIDENT_PROMPT_DELIMITER_START = "=== INCIDENT CONTEXT ===";
|
|
15431
|
+
var INCIDENT_PROMPT_DELIMITER_END = "=== END INCIDENT CONTEXT ===";
|
|
15432
|
+
var formatObservableType = (type) => {
|
|
15433
|
+
if (!type) return "Indicator";
|
|
15434
|
+
const clean = type.toLowerCase().trim();
|
|
15435
|
+
switch (clean) {
|
|
15436
|
+
case "ip":
|
|
15437
|
+
case "ipv4":
|
|
15438
|
+
case "ip_address":
|
|
15439
|
+
return "IPv4";
|
|
15440
|
+
case "ipv6":
|
|
15441
|
+
return "IPv6";
|
|
15442
|
+
case "hash":
|
|
15443
|
+
case "sha256":
|
|
15444
|
+
case "file_hash":
|
|
15445
|
+
return "SHA256";
|
|
15446
|
+
case "md5":
|
|
15447
|
+
return "MD5";
|
|
15448
|
+
case "sha1":
|
|
15449
|
+
return "SHA1";
|
|
15450
|
+
case "hostname":
|
|
15451
|
+
case "host":
|
|
15452
|
+
case "endpoint":
|
|
15453
|
+
return "Hostname";
|
|
15454
|
+
case "domain":
|
|
15455
|
+
return "Domain";
|
|
15456
|
+
case "url":
|
|
15457
|
+
return "URL";
|
|
15458
|
+
case "user":
|
|
15459
|
+
case "username":
|
|
15460
|
+
case "account":
|
|
15461
|
+
return "User";
|
|
15462
|
+
case "email":
|
|
15463
|
+
return "Email";
|
|
15464
|
+
case "process":
|
|
15465
|
+
case "process_name":
|
|
15466
|
+
return "Process";
|
|
15467
|
+
case "file":
|
|
15468
|
+
case "filename":
|
|
15469
|
+
return "File";
|
|
15470
|
+
default:
|
|
15471
|
+
return type.charAt(0).toUpperCase() + type.slice(1);
|
|
15588
15472
|
}
|
|
15589
|
-
return null;
|
|
15590
15473
|
};
|
|
15591
|
-
var
|
|
15592
|
-
|
|
15593
|
-
|
|
15594
|
-
|
|
15595
|
-
|
|
15596
|
-
|
|
15597
|
-
|
|
15598
|
-
|
|
15599
|
-
|
|
15600
|
-
|
|
15601
|
-
reason: ds ? `Category: ${ds.category}
|
|
15602
|
-
Key: ${ds.key}
|
|
15603
|
-
Click to open it in the Datastore.` : "Started by a datastore automation (e.g. enrichment trigger).",
|
|
15604
|
-
icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Database, { size: SOURCE_ICON_SIZE }),
|
|
15605
|
-
datastoreKey: ds?.key,
|
|
15606
|
-
datastoreCategory: ds?.category
|
|
15607
|
-
};
|
|
15474
|
+
var isIncidentInjectedPrompt = (prompt) => {
|
|
15475
|
+
if (!prompt || typeof prompt !== "string") return false;
|
|
15476
|
+
return prompt.includes(INCIDENT_PROMPT_DELIMITER_START) || prompt.includes("OPERATIONAL DIRECTIVES:") || prompt.includes("INCIDENT OBSERVABLES / IOCs:");
|
|
15477
|
+
};
|
|
15478
|
+
var extractCleanIncidentPrompt = (prompt) => {
|
|
15479
|
+
if (!prompt || typeof prompt !== "string") return "";
|
|
15480
|
+
const trimmed = prompt.trim();
|
|
15481
|
+
const taskMatch = trimmed.match(/TASK TO EXECUTE:\s*\n\s*-\s*Task:\s*([^\n]+)/i);
|
|
15482
|
+
if (taskMatch && taskMatch[1]?.trim()) {
|
|
15483
|
+
return `Task: ${taskMatch[1].trim()}`;
|
|
15608
15484
|
}
|
|
15609
|
-
|
|
15610
|
-
|
|
15611
|
-
|
|
15612
|
-
label: "Schedule",
|
|
15613
|
-
reason: "Started by a scheduled trigger (cron).",
|
|
15614
|
-
icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Clock, { size: SOURCE_ICON_SIZE })
|
|
15615
|
-
};
|
|
15485
|
+
const legacyMatch = trimmed.match(/\n\s*Task:\s*([^\n]+)/i);
|
|
15486
|
+
if (legacyMatch && legacyMatch[1]?.trim()) {
|
|
15487
|
+
return `Task: ${legacyMatch[1].trim()}`;
|
|
15616
15488
|
}
|
|
15617
|
-
if (
|
|
15618
|
-
|
|
15619
|
-
|
|
15620
|
-
label: "Webhook",
|
|
15621
|
-
reason: "Started by an incoming webhook / HTTP trigger.",
|
|
15622
|
-
icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Globe, { size: SOURCE_ICON_SIZE })
|
|
15623
|
-
};
|
|
15489
|
+
if (trimmed.includes(INCIDENT_PROMPT_DELIMITER_START)) {
|
|
15490
|
+
const before = trimmed.split(INCIDENT_PROMPT_DELIMITER_START)[0]?.trim();
|
|
15491
|
+
if (before) return before;
|
|
15624
15492
|
}
|
|
15625
|
-
|
|
15626
|
-
|
|
15627
|
-
|
|
15628
|
-
|
|
15629
|
-
|
|
15630
|
-
|
|
15631
|
-
|
|
15493
|
+
return trimmed;
|
|
15494
|
+
};
|
|
15495
|
+
var composeIncidentPromptInput = (rawInput, context) => {
|
|
15496
|
+
if (!rawInput || typeof rawInput !== "string") return "";
|
|
15497
|
+
const trimmed = rawInput.trim();
|
|
15498
|
+
if (isIncidentInjectedPrompt(trimmed)) {
|
|
15499
|
+
return trimmed;
|
|
15632
15500
|
}
|
|
15633
|
-
|
|
15634
|
-
|
|
15635
|
-
|
|
15636
|
-
label: "Workflow",
|
|
15637
|
-
reason: "Started by another workflow as a subflow.",
|
|
15638
|
-
icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Workflow, { size: SOURCE_ICON_SIZE })
|
|
15639
|
-
};
|
|
15501
|
+
const ctx = context || getActiveIncidentPromptContext();
|
|
15502
|
+
if (!ctx || !ctx.id) {
|
|
15503
|
+
return trimmed;
|
|
15640
15504
|
}
|
|
15641
|
-
|
|
15642
|
-
|
|
15643
|
-
|
|
15644
|
-
|
|
15645
|
-
|
|
15646
|
-
|
|
15647
|
-
|
|
15648
|
-
|
|
15649
|
-
|
|
15650
|
-
|
|
15651
|
-
|
|
15652
|
-
|
|
15653
|
-
|
|
15654
|
-
|
|
15655
|
-
|
|
15656
|
-
|
|
15657
|
-
const status = getEffectiveStatus(run2);
|
|
15658
|
-
if (status === "FINISHED" || status === "SUCCESS") return "hsl(var(--severity-low, 142 71% 45%))";
|
|
15659
|
-
if (status === "FAILED" || status === "ABORTED") return "hsl(var(--severity-critical, 0 72% 55%))";
|
|
15660
|
-
if (status === "LIMIT_REACHED") return "hsl(var(--severity-critical, 0 72% 55%))";
|
|
15661
|
-
if (status === "EXECUTING" || status === "RUNNING") return "hsl(var(--severity-medium, 38 92% 50%))";
|
|
15662
|
-
return "hsl(var(--primary, 24 100% 50%))";
|
|
15663
|
-
};
|
|
15664
|
-
var formatDuration = (run2) => {
|
|
15665
|
-
if (run2.started_at && run2.completed_at) {
|
|
15666
|
-
const start = Number(run2.started_at);
|
|
15667
|
-
const end = Number(run2.completed_at);
|
|
15668
|
-
if (!isNaN(start) && !isNaN(end)) {
|
|
15669
|
-
const toMs = (n) => n > 1e12 ? n : n * 1e3;
|
|
15670
|
-
const ms = Math.max(0, toMs(end) - toMs(start));
|
|
15671
|
-
if (ms < 1e3) return `${(ms / 1e3).toFixed(2)}s`;
|
|
15672
|
-
if (ms < 6e4) return `${(ms / 1e3).toFixed(2)}s`;
|
|
15673
|
-
return `${Math.floor(ms / 6e4)}m ${Math.floor(ms % 6e4 / 1e3)}s`;
|
|
15674
|
-
}
|
|
15675
|
-
}
|
|
15676
|
-
if (run2.duration) return `${run2.duration.toFixed(2)}s`;
|
|
15677
|
-
return "";
|
|
15678
|
-
};
|
|
15679
|
-
var getTimeAgo = (dateStr) => {
|
|
15680
|
-
try {
|
|
15681
|
-
const ts = isNaN(Number(dateStr)) ? new Date(dateStr).getTime() : Number(dateStr) * 1e3;
|
|
15682
|
-
if (isNaN(ts)) return dateStr;
|
|
15683
|
-
const diffSec = Math.max(0, Math.floor((Date.now() - ts) / 1e3));
|
|
15684
|
-
if (diffSec < 60) return `${diffSec}s ago`;
|
|
15685
|
-
if (diffSec < 3600) return `${Math.floor(diffSec / 60)}m ago`;
|
|
15686
|
-
if (diffSec < 86400) return `${Math.floor(diffSec / 3600)}h ago`;
|
|
15687
|
-
return `${Math.floor(diffSec / 86400)}d ago`;
|
|
15688
|
-
} catch {
|
|
15689
|
-
return dateStr;
|
|
15690
|
-
}
|
|
15691
|
-
};
|
|
15692
|
-
var PROMPT_KEYS = [
|
|
15693
|
-
"original_input",
|
|
15694
|
-
"originalinput",
|
|
15695
|
-
"input",
|
|
15696
|
-
"prompt",
|
|
15697
|
-
"user_input",
|
|
15698
|
-
"userinput",
|
|
15699
|
-
"user_prompt",
|
|
15700
|
-
"userprompt",
|
|
15701
|
-
"question",
|
|
15702
|
-
"query",
|
|
15703
|
-
"message",
|
|
15704
|
-
"text",
|
|
15705
|
-
"content",
|
|
15706
|
-
"task",
|
|
15707
|
-
"instruction",
|
|
15708
|
-
"instructions"
|
|
15709
|
-
];
|
|
15710
|
-
var deepFindPrompt = (value, depth = 0) => {
|
|
15711
|
-
if (value == null || depth > 6) return null;
|
|
15712
|
-
if (typeof value === "string") {
|
|
15713
|
-
const s = value.trim();
|
|
15714
|
-
if (s.startsWith("{") && s.endsWith("}") || s.startsWith("[") && s.endsWith("]")) {
|
|
15715
|
-
try {
|
|
15716
|
-
return deepFindPrompt(JSON.parse(s), depth + 1);
|
|
15717
|
-
} catch {
|
|
15718
|
-
}
|
|
15719
|
-
}
|
|
15720
|
-
return null;
|
|
15721
|
-
}
|
|
15722
|
-
if (Array.isArray(value)) {
|
|
15723
|
-
for (const item of value) {
|
|
15724
|
-
const hit = deepFindPrompt(item, depth + 1);
|
|
15725
|
-
if (hit) return hit;
|
|
15726
|
-
}
|
|
15727
|
-
return null;
|
|
15728
|
-
}
|
|
15729
|
-
if (typeof value === "object") {
|
|
15730
|
-
const obj = value;
|
|
15731
|
-
for (const key of Object.keys(obj)) {
|
|
15732
|
-
if (PROMPT_KEYS.includes(key.toLowerCase())) {
|
|
15733
|
-
const v = obj[key];
|
|
15734
|
-
if (typeof v === "string" && v.trim()) return v.trim();
|
|
15735
|
-
const nested = deepFindPrompt(v, depth + 1);
|
|
15736
|
-
if (nested) return nested;
|
|
15737
|
-
}
|
|
15738
|
-
}
|
|
15739
|
-
for (const key of Object.keys(obj)) {
|
|
15740
|
-
if (PROMPT_KEYS.includes(key.toLowerCase())) continue;
|
|
15741
|
-
const hit = deepFindPrompt(obj[key], depth + 1);
|
|
15742
|
-
if (hit) return hit;
|
|
15743
|
-
}
|
|
15744
|
-
}
|
|
15745
|
-
return null;
|
|
15746
|
-
};
|
|
15747
|
-
var tryParseJson = (s) => {
|
|
15748
|
-
if (typeof s !== "string") return s;
|
|
15749
|
-
const trimmed = s.trim();
|
|
15750
|
-
if (!trimmed) return null;
|
|
15751
|
-
try {
|
|
15752
|
-
return JSON.parse(trimmed);
|
|
15753
|
-
} catch {
|
|
15754
|
-
return trimmed;
|
|
15755
|
-
}
|
|
15756
|
-
};
|
|
15757
|
-
var getRunPrompt = (run2) => {
|
|
15758
|
-
const anyRun = run2;
|
|
15759
|
-
const agentInput = getDirectAgentInput(getAIAgentResultPayload(run2));
|
|
15760
|
-
if (agentInput) return agentInput;
|
|
15761
|
-
const results = Array.isArray(anyRun.results) ? anyRun.results : null;
|
|
15762
|
-
if (results) {
|
|
15763
|
-
const agentResult = results.find(isAIAgentResult);
|
|
15764
|
-
if (agentResult?.result) {
|
|
15765
|
-
const hit = deepFindPrompt(tryParseJson(agentResult.result));
|
|
15766
|
-
if (hit) return hit;
|
|
15767
|
-
}
|
|
15768
|
-
for (const r of results) {
|
|
15769
|
-
if (r === agentResult) continue;
|
|
15770
|
-
const hit = deepFindPrompt(tryParseJson(r?.result));
|
|
15771
|
-
if (hit) return hit;
|
|
15505
|
+
const incidentRef = ctx.id ? `#${ctx.id}` : "";
|
|
15506
|
+
const incidentTitle = ctx.title?.trim() || "Incident";
|
|
15507
|
+
const severity = ctx.severity || "Medium";
|
|
15508
|
+
const status = ctx.status || "In Progress";
|
|
15509
|
+
const description = ctx.description?.trim() || "No description provided.";
|
|
15510
|
+
const seenObservables = /* @__PURE__ */ new Set();
|
|
15511
|
+
const formattedObservables = [];
|
|
15512
|
+
if (Array.isArray(ctx.observables)) {
|
|
15513
|
+
for (const obs of ctx.observables) {
|
|
15514
|
+
if (!obs || obs.archived || !obs.value?.trim()) continue;
|
|
15515
|
+
const val = obs.value.trim();
|
|
15516
|
+
const typeLabel = formatObservableType(obs.type);
|
|
15517
|
+
const key = `${typeLabel.toLowerCase()}:${val.toLowerCase()}`;
|
|
15518
|
+
if (seenObservables.has(key)) continue;
|
|
15519
|
+
seenObservables.add(key);
|
|
15520
|
+
formattedObservables.push(`- ${typeLabel}: ${val}`);
|
|
15772
15521
|
}
|
|
15773
15522
|
}
|
|
15774
|
-
|
|
15775
|
-
|
|
15776
|
-
|
|
15777
|
-
|
|
15778
|
-
|
|
15779
|
-
|
|
15780
|
-
|
|
15781
|
-
|
|
15782
|
-
|
|
15783
|
-
|
|
15784
|
-
|
|
15785
|
-
|
|
15786
|
-
|
|
15787
|
-
|
|
15788
|
-
|
|
15789
|
-
}
|
|
15790
|
-
return null;
|
|
15523
|
+
const observablesBlock = formattedObservables.length > 0 ? formattedObservables.slice(0, 50).join("\n") : "- None recorded yet";
|
|
15524
|
+
return `${trimmed}
|
|
15525
|
+
|
|
15526
|
+
${INCIDENT_PROMPT_DELIMITER_START}
|
|
15527
|
+
INCIDENT CONTEXT:
|
|
15528
|
+
- Incident: ${incidentRef} ("${incidentTitle}")
|
|
15529
|
+
- Severity: ${severity} | Status: ${status}
|
|
15530
|
+
- Summary: ${description}
|
|
15531
|
+
|
|
15532
|
+
INCIDENT OBSERVABLES / IOCs:
|
|
15533
|
+
${observablesBlock}
|
|
15534
|
+
${INCIDENT_PROMPT_DELIMITER_END}
|
|
15535
|
+
|
|
15536
|
+
DIRECTIVES:
|
|
15537
|
+
- Relevant incident details and observables are provided above. Do not call "get_incident" unless raw external logs are required.`;
|
|
15791
15538
|
};
|
|
15792
|
-
|
|
15793
|
-
|
|
15794
|
-
|
|
15795
|
-
|
|
15796
|
-
|
|
15797
|
-
|
|
15798
|
-
|
|
15799
|
-
|
|
15800
|
-
|
|
15801
|
-
s = s.replace(/^\s{0,3}#{1,6}\s+/gm, "");
|
|
15802
|
-
s = s.replace(/^\s{0,3}>\s?/gm, "");
|
|
15803
|
-
s = s.replace(/^\s*[-*+]\s+/gm, "");
|
|
15804
|
-
s = s.replace(/^\s*\d+\.\s+/gm, "");
|
|
15805
|
-
s = s.replace(/\s+/g, " ").trim();
|
|
15806
|
-
return s;
|
|
15539
|
+
|
|
15540
|
+
// ../lib/usecasePromptContext.ts
|
|
15541
|
+
var GLOBAL_KEY = "__shuffleActiveUsecaseContext";
|
|
15542
|
+
var GLOBAL_OVERVIEW_KEY = "__shuffleActiveUsecasesOverviewContext";
|
|
15543
|
+
var USECASE_CONTEXT_EVENT = "shuffle-usecase-context-changed";
|
|
15544
|
+
var scope = () => typeof globalThis !== "undefined" ? globalThis : null;
|
|
15545
|
+
var getActiveUsecasePromptContext = () => {
|
|
15546
|
+
const v = scope()?.[GLOBAL_KEY];
|
|
15547
|
+
return v && typeof v === "object" ? v : null;
|
|
15807
15548
|
};
|
|
15808
|
-
var
|
|
15809
|
-
const
|
|
15810
|
-
|
|
15811
|
-
const oneLine = stripMarkdown(prompt);
|
|
15812
|
-
return oneLine.length > 80 ? oneLine.slice(0, 80) + "\u2026" : oneLine;
|
|
15813
|
-
}
|
|
15814
|
-
if (run2.workflow?.name) return stripMarkdown(run2.workflow.name);
|
|
15815
|
-
return `Execution ${run2.execution_id?.slice(0, 8) || "\u2014"}`;
|
|
15549
|
+
var getActiveUsecasesOverviewContext = () => {
|
|
15550
|
+
const v = scope()?.[GLOBAL_OVERVIEW_KEY];
|
|
15551
|
+
return v && typeof v === "object" ? v : null;
|
|
15816
15552
|
};
|
|
15817
|
-
var
|
|
15818
|
-
|
|
15819
|
-
|
|
15553
|
+
var humanizeUsecaseId = (id) => id.replace(/_\d+$/, "").replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()).trim();
|
|
15554
|
+
var getUsecaseDisplayName = (flowId) => {
|
|
15555
|
+
const ctx = getActiveUsecasePromptContext();
|
|
15556
|
+
if (ctx?.label) return ctx.label;
|
|
15557
|
+
return flowId ? humanizeUsecaseId(flowId) : "Usecases";
|
|
15820
15558
|
};
|
|
15821
|
-
var
|
|
15822
|
-
|
|
15823
|
-
|
|
15824
|
-
|
|
15825
|
-
|
|
15826
|
-
const out = stripMarkdown(p.output);
|
|
15827
|
-
return out.length > 120 ? out.slice(0, 120) + "\u2026" : out;
|
|
15828
|
-
}
|
|
15829
|
-
if (p.original_input && typeof p.original_input === "string") {
|
|
15830
|
-
const inp = stripMarkdown(p.original_input);
|
|
15831
|
-
return inp.length > 120 ? inp.slice(0, 120) + "\u2026" : inp;
|
|
15832
|
-
}
|
|
15833
|
-
if (p.message && typeof p.message === "string") return stripMarkdown(p.message);
|
|
15834
|
-
} catch {
|
|
15835
|
-
if (run2.result.length < 120) return stripMarkdown(run2.result);
|
|
15836
|
-
}
|
|
15837
|
-
}
|
|
15838
|
-
const status = getEffectiveStatus(run2);
|
|
15839
|
-
const isRunning = status === "EXECUTING" || status === "RUNNING" || status === "WAITING";
|
|
15840
|
-
return run2.execution_source || (isRunning ? "Agent still running\u2026" : "Agent execution");
|
|
15559
|
+
var USECASE_PROMPT_START = "=== USECASE CONTEXT ===";
|
|
15560
|
+
var USECASE_PROMPT_END = "=== END USECASE CONTEXT ===";
|
|
15561
|
+
var isUsecaseInjectedPrompt = (prompt) => {
|
|
15562
|
+
if (!prompt || typeof prompt !== "string") return false;
|
|
15563
|
+
return prompt.includes(USECASE_PROMPT_START);
|
|
15841
15564
|
};
|
|
15842
|
-
var
|
|
15843
|
-
|
|
15844
|
-
if (
|
|
15845
|
-
|
|
15846
|
-
if (v === "WAITING" || v === "SKIPPED") return "waiting";
|
|
15847
|
-
return "unknown";
|
|
15848
|
-
};
|
|
15849
|
-
var getRunTools = (run2) => {
|
|
15850
|
-
const map = /* @__PURE__ */ new Map();
|
|
15851
|
-
const ids = /* @__PURE__ */ new Map();
|
|
15852
|
-
const skip = (s) => /^(ai\s*agent|shuffle\s*agent|shuffle_agent)$/i.test(s);
|
|
15853
|
-
const rank = { failure: 3, waiting: 2, success: 1, unknown: 0 };
|
|
15854
|
-
const merge2 = (name, status, id) => {
|
|
15855
|
-
if (!name) return;
|
|
15856
|
-
const s = String(name).trim();
|
|
15857
|
-
if (!s || skip(s)) return;
|
|
15858
|
-
const next = status || "unknown";
|
|
15859
|
-
const prev = map.get(s);
|
|
15860
|
-
if (!prev || rank[next] > rank[prev]) map.set(s, next);
|
|
15861
|
-
if (id && !ids.has(s)) ids.set(s, id);
|
|
15862
|
-
};
|
|
15863
|
-
const allowed = run2.allowed_actions;
|
|
15864
|
-
if (Array.isArray(allowed) && allowed.length > 0) {
|
|
15865
|
-
for (const entry of allowed) {
|
|
15866
|
-
if (typeof entry !== "string") continue;
|
|
15867
|
-
const parts = entry.split(":");
|
|
15868
|
-
if (parts.length < 3 || parts[0] !== "app") continue;
|
|
15869
|
-
const id = parts[1];
|
|
15870
|
-
const name = parts.slice(2).join(":");
|
|
15871
|
-
merge2(name, "unknown", id);
|
|
15872
|
-
}
|
|
15873
|
-
const normalize3 = (s) => s.toLowerCase().replace(/[\s_-]+/g, "_");
|
|
15874
|
-
const known = /* @__PURE__ */ new Map();
|
|
15875
|
-
for (const real of map.keys()) known.set(normalize3(real), real);
|
|
15876
|
-
const apply = (name, status) => {
|
|
15877
|
-
if (!name) return;
|
|
15878
|
-
const hit = known.get(normalize3(name));
|
|
15879
|
-
if (hit) merge2(hit, status);
|
|
15880
|
-
};
|
|
15881
|
-
(run2.results || []).forEach((r) => {
|
|
15882
|
-
apply(r?.action?.app_name || r?.action?.label, normalizeResultStatus(r?.status));
|
|
15883
|
-
});
|
|
15884
|
-
(run2.decisions || []).forEach((d) => {
|
|
15885
|
-
if (typeof d?.tool === "string") apply(d.tool, normalizeResultStatus(d?.status));
|
|
15886
|
-
});
|
|
15887
|
-
} else {
|
|
15888
|
-
(run2.results || []).forEach((r) => {
|
|
15889
|
-
merge2(r?.action?.app_name || r?.action?.label, normalizeResultStatus(r?.status));
|
|
15890
|
-
});
|
|
15891
|
-
(run2.decisions || []).forEach((d) => {
|
|
15892
|
-
if (typeof d?.tool === "string") merge2(d.tool, normalizeResultStatus(d?.status));
|
|
15893
|
-
});
|
|
15894
|
-
}
|
|
15895
|
-
return Array.from(map.entries()).slice(0, 6).map(([name, status]) => ({ name, status, id: ids.get(name) }));
|
|
15896
|
-
};
|
|
15897
|
-
var TOOL_STATUS_RING = {
|
|
15898
|
-
success: "hsl(var(--severity-low, 142 71% 45%))",
|
|
15899
|
-
failure: "hsl(var(--severity-critical, 0 72% 55%))",
|
|
15900
|
-
waiting: "hsl(var(--severity-medium, 38 92% 50%))",
|
|
15901
|
-
unknown: "hsl(var(--border))"
|
|
15902
|
-
};
|
|
15903
|
-
var TOOL_STATUS_LABEL = {
|
|
15904
|
-
success: "ran successfully",
|
|
15905
|
-
failure: "failed",
|
|
15906
|
-
waiting: "needs input",
|
|
15907
|
-
unknown: "used"
|
|
15908
|
-
};
|
|
15909
|
-
var normToolKey = (s) => s.toLowerCase().replace(/[\s_\-]+/g, "_");
|
|
15910
|
-
var AgentRunRow = ({ run: run2, onClick, sx, appIcons, onAppClick, apiKey, apiBaseUrl, orgId, abortingIds, onAbort }) => {
|
|
15911
|
-
const navigate = (0, import_router_compat.useNavigate)();
|
|
15912
|
-
const statusKey = getEffectiveStatus(run2);
|
|
15913
|
-
const iconColor = getRunIconColor(run2);
|
|
15914
|
-
const duration = formatDuration(run2);
|
|
15915
|
-
const tools = getRunTools(run2);
|
|
15916
|
-
const decisionCount = getDecisionCount(run2);
|
|
15917
|
-
const attachmentCount = collectLlmImageAttachments(run2).length;
|
|
15918
|
-
const isRunning = statusKey === "EXECUTING" || statusKey === "RUNNING" || statusKey === "WAITING";
|
|
15919
|
-
const isAborting = !!run2.execution_id && !!abortingIds?.has(run2.execution_id);
|
|
15920
|
-
const wfId = run2.workflow_id || run2?.workflow?.id;
|
|
15921
|
-
const canAbort = isRunning && !!run2.execution_id && !!wfId && !isAborting;
|
|
15922
|
-
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
15923
|
-
import_material17.Box,
|
|
15924
|
-
{
|
|
15925
|
-
onClick,
|
|
15926
|
-
sx: [
|
|
15927
|
-
{
|
|
15928
|
-
borderRadius: 2,
|
|
15929
|
-
border: "1px solid hsl(var(--border))",
|
|
15930
|
-
bgcolor: "hsl(var(--card))",
|
|
15931
|
-
display: "flex",
|
|
15932
|
-
alignItems: "center",
|
|
15933
|
-
gap: 2,
|
|
15934
|
-
px: 2.5,
|
|
15935
|
-
py: 2,
|
|
15936
|
-
cursor: "pointer",
|
|
15937
|
-
transition: "background 0.15s ease, border-color 0.15s ease",
|
|
15938
|
-
"&:hover": {
|
|
15939
|
-
bgcolor: "hsla(var(--muted) / 0.5)",
|
|
15940
|
-
borderColor: "hsl(var(--muted-foreground) / 0.3)"
|
|
15941
|
-
}
|
|
15942
|
-
},
|
|
15943
|
-
...Array.isArray(sx) ? sx : sx ? [sx] : []
|
|
15944
|
-
],
|
|
15945
|
-
children: [
|
|
15946
|
-
(() => {
|
|
15947
|
-
const sourceInfo = classifyRunSource(run2);
|
|
15948
|
-
const dsLink = sourceInfo.kind === "datastore" && sourceInfo.datastoreKey && sourceInfo.datastoreCategory ? `https://shuffler.io/admin?tab=datastore&category=${encodeURIComponent(sourceInfo.datastoreCategory)}&key=${encodeURIComponent(sourceInfo.datastoreKey)}` : null;
|
|
15949
|
-
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
15950
|
-
import_material17.Tooltip,
|
|
15951
|
-
{
|
|
15952
|
-
title: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: { lineHeight: 1.4 }, children: [
|
|
15953
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Box, { sx: { fontWeight: 600 }, children: sourceInfo.label }),
|
|
15954
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Box, { sx: { opacity: 0.85, whiteSpace: "pre-line" }, children: sourceInfo.reason })
|
|
15955
|
-
] }),
|
|
15956
|
-
placement: "top",
|
|
15957
|
-
arrow: true,
|
|
15958
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
15959
|
-
import_material17.Box,
|
|
15960
|
-
{
|
|
15961
|
-
onClick: dsLink ? async (e) => {
|
|
15962
|
-
e.stopPropagation();
|
|
15963
|
-
await navigateToShuffleCore(dsLink, { newTab: true });
|
|
15964
|
-
} : void 0,
|
|
15965
|
-
sx: {
|
|
15966
|
-
width: 40,
|
|
15967
|
-
height: 40,
|
|
15968
|
-
borderRadius: "50%",
|
|
15969
|
-
display: "flex",
|
|
15970
|
-
alignItems: "center",
|
|
15971
|
-
justifyContent: "center",
|
|
15972
|
-
bgcolor: `${iconColor}15`,
|
|
15973
|
-
color: iconColor,
|
|
15974
|
-
flexShrink: 0,
|
|
15975
|
-
cursor: dsLink ? "pointer" : "inherit",
|
|
15976
|
-
transition: "background 0.15s ease",
|
|
15977
|
-
"&:hover": dsLink ? { bgcolor: `${iconColor}30` } : void 0
|
|
15978
|
-
},
|
|
15979
|
-
children: sourceInfo.icon
|
|
15980
|
-
}
|
|
15981
|
-
)
|
|
15982
|
-
}
|
|
15983
|
-
);
|
|
15984
|
-
})(),
|
|
15985
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: { flex: 1, minWidth: 0 }, children: [
|
|
15986
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Box, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 0.25 }, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
15987
|
-
import_material17.Typography,
|
|
15988
|
-
{
|
|
15989
|
-
sx: {
|
|
15990
|
-
fontSize: "0.9rem",
|
|
15991
|
-
fontWeight: 500,
|
|
15992
|
-
color: "hsl(var(--foreground))",
|
|
15993
|
-
overflow: "hidden",
|
|
15994
|
-
textOverflow: "ellipsis",
|
|
15995
|
-
whiteSpace: "nowrap"
|
|
15996
|
-
},
|
|
15997
|
-
children: getRunTitle(run2)
|
|
15998
|
-
}
|
|
15999
|
-
) }),
|
|
16000
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
16001
|
-
import_material17.Typography,
|
|
16002
|
-
{
|
|
16003
|
-
sx: {
|
|
16004
|
-
fontSize: "0.78rem",
|
|
16005
|
-
color: "hsl(var(--muted-foreground))",
|
|
16006
|
-
overflow: "hidden",
|
|
16007
|
-
textOverflow: "ellipsis",
|
|
16008
|
-
whiteSpace: "nowrap"
|
|
16009
|
-
},
|
|
16010
|
-
children: getRunSubtitle(run2)
|
|
16011
|
-
}
|
|
16012
|
-
),
|
|
16013
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: { display: "flex", alignItems: "center", gap: 1, mt: 0.5 }, children: [
|
|
16014
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Typography, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.7 }, children: run2.started_at ? getTimeAgo(run2.started_at) : "\u2014" }),
|
|
16015
|
-
duration && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
|
|
16016
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Typography, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.4 }, children: "\xB7" }),
|
|
16017
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Typography, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.7 }, children: duration })
|
|
16018
|
-
] }),
|
|
16019
|
-
decisionCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
|
|
16020
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Typography, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.4 }, children: "\xB7" }),
|
|
16021
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: { display: "inline-flex", alignItems: "center", gap: 0.5, color: "hsl(var(--muted-foreground))", opacity: 0.7 }, children: [
|
|
16022
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.GitBranch, { size: 11 }),
|
|
16023
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Typography, { sx: { fontSize: "0.72rem", color: "inherit" }, children: [
|
|
16024
|
-
decisionCount,
|
|
16025
|
-
" ",
|
|
16026
|
-
decisionCount === 1 ? "decision" : "decisions"
|
|
16027
|
-
] })
|
|
16028
|
-
] })
|
|
16029
|
-
] }),
|
|
16030
|
-
attachmentCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
|
|
16031
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Typography, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.4 }, children: "\xB7" }),
|
|
16032
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: { display: "inline-flex", alignItems: "center", gap: 0.5, color: "hsl(var(--muted-foreground))", opacity: 0.7 }, children: [
|
|
16033
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Image, { size: 11 }),
|
|
16034
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Typography, { sx: { fontSize: "0.72rem", color: "inherit" }, children: [
|
|
16035
|
-
attachmentCount,
|
|
16036
|
-
" ",
|
|
16037
|
-
attachmentCount === 1 ? "attachment" : "attachments"
|
|
16038
|
-
] })
|
|
16039
|
-
] })
|
|
16040
|
-
] })
|
|
16041
|
-
] })
|
|
16042
|
-
] }),
|
|
16043
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
16044
|
-
import_material17.Box,
|
|
16045
|
-
{
|
|
16046
|
-
onClick: (e) => e.stopPropagation(),
|
|
16047
|
-
sx: {
|
|
16048
|
-
display: { xs: "none", sm: "flex" },
|
|
16049
|
-
alignItems: "center",
|
|
16050
|
-
justifyContent: "flex-end",
|
|
16051
|
-
flexShrink: 0,
|
|
16052
|
-
ml: tools.length > 0 ? 1 : 0,
|
|
16053
|
-
width: tools.length > 0 ? 140 : 0,
|
|
16054
|
-
minHeight: tools.length > 0 ? 28 : 0
|
|
16055
|
-
},
|
|
16056
|
-
children: tools.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
16057
|
-
import_material17.AvatarGroup,
|
|
16058
|
-
{
|
|
16059
|
-
max: 5,
|
|
16060
|
-
sx: {
|
|
16061
|
-
"& .MuiAvatar-root": {
|
|
16062
|
-
width: 28,
|
|
16063
|
-
height: 28,
|
|
16064
|
-
fontSize: "0.7rem",
|
|
16065
|
-
borderColor: "hsl(var(--border))",
|
|
16066
|
-
bgcolor: "hsl(var(--muted))",
|
|
16067
|
-
color: "hsl(var(--muted-foreground))"
|
|
16068
|
-
}
|
|
16069
|
-
},
|
|
16070
|
-
children: tools.map((t) => {
|
|
16071
|
-
const icon = appIcons?.[normToolKey(t.name)];
|
|
16072
|
-
const label = t.name.replace(/_/g, " ");
|
|
16073
|
-
const slug = t.name.toLowerCase().replace(/\s+/g, "_");
|
|
16074
|
-
const ring = TOOL_STATUS_RING[t.status];
|
|
16075
|
-
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Tooltip, { title: `${label} \u2014 ${TOOL_STATUS_LABEL[t.status]}`, arrow: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
16076
|
-
import_material17.Avatar,
|
|
16077
|
-
{
|
|
16078
|
-
src: icon || void 0,
|
|
16079
|
-
alt: label,
|
|
16080
|
-
variant: "rounded",
|
|
16081
|
-
onClick: (e) => {
|
|
16082
|
-
e.stopPropagation();
|
|
16083
|
-
if (onAppClick) {
|
|
16084
|
-
onAppClick({ id: t.id, name: t.name });
|
|
16085
|
-
} else {
|
|
16086
|
-
navigate(`/apps/${encodeURIComponent(t.id || slug)}`);
|
|
16087
|
-
}
|
|
16088
|
-
},
|
|
16089
|
-
sx: {
|
|
16090
|
-
cursor: "pointer",
|
|
16091
|
-
borderColor: `${ring} !important`,
|
|
16092
|
-
borderWidth: "1px",
|
|
16093
|
-
borderStyle: "solid",
|
|
16094
|
-
transition: "transform 0.15s ease, border-color 0.15s ease",
|
|
16095
|
-
"&:hover": {
|
|
16096
|
-
transform: "scale(1.08)",
|
|
16097
|
-
borderColor: "hsl(var(--primary)) !important"
|
|
16098
|
-
}
|
|
16099
|
-
},
|
|
16100
|
-
children: label.charAt(0).toUpperCase()
|
|
16101
|
-
}
|
|
16102
|
-
) }, t.name);
|
|
16103
|
-
})
|
|
16104
|
-
}
|
|
16105
|
-
) : null
|
|
16106
|
-
}
|
|
16107
|
-
),
|
|
16108
|
-
canAbort && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Tooltip, { title: isAborting ? "Aborting\u2026" : "Abort this execution", arrow: true, placement: "top", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
16109
|
-
import_material17.IconButton,
|
|
16110
|
-
{
|
|
16111
|
-
size: "small",
|
|
16112
|
-
disabled: isAborting,
|
|
16113
|
-
onClick: (e) => {
|
|
16114
|
-
e.stopPropagation();
|
|
16115
|
-
onAbort?.(run2);
|
|
16116
|
-
},
|
|
16117
|
-
sx: {
|
|
16118
|
-
flexShrink: 0,
|
|
16119
|
-
ml: 1.5,
|
|
16120
|
-
color: "hsl(var(--muted-foreground))",
|
|
16121
|
-
"&:hover": { color: "hsl(var(--destructive))", bgcolor: "hsl(var(--muted))" }
|
|
16122
|
-
},
|
|
16123
|
-
children: isAborting ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.CircularProgress, { size: 16, sx: { color: "hsl(var(--destructive))" } }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react8.StopCircle, { size: 18 })
|
|
16124
|
-
}
|
|
16125
|
-
) }) })
|
|
16126
|
-
]
|
|
16127
|
-
}
|
|
16128
|
-
);
|
|
16129
|
-
};
|
|
16130
|
-
var STATUS_FILTERS = [
|
|
16131
|
-
{ label: "All", value: "" },
|
|
16132
|
-
{ label: "Completed", value: "FINISHED" },
|
|
16133
|
-
{ label: "Running", value: "EXECUTING" },
|
|
16134
|
-
{ label: "Failed", value: "ABORTED" }
|
|
16135
|
-
];
|
|
16136
|
-
var AgentActivityList = ({
|
|
16137
|
-
apiKey,
|
|
16138
|
-
apiBaseUrl,
|
|
16139
|
-
orgId,
|
|
16140
|
-
onRunClick,
|
|
16141
|
-
renderAppDetailDrawer,
|
|
16142
|
-
onEditWorkflow,
|
|
16143
|
-
showSearchBar = true,
|
|
16144
|
-
showStatusChips = true,
|
|
16145
|
-
limit = 50,
|
|
16146
|
-
top,
|
|
16147
|
-
emptyTitle = "No agent activity found",
|
|
16148
|
-
emptySubtitle = "The agent has not performed any actions yet",
|
|
16149
|
-
className,
|
|
16150
|
-
sx,
|
|
16151
|
-
toolbarSx,
|
|
16152
|
-
rowSx,
|
|
16153
|
-
usecaseFilters = [],
|
|
16154
|
-
globalUrl,
|
|
16155
|
-
theme,
|
|
16156
|
-
colorMode,
|
|
16157
|
-
initialRuns,
|
|
16158
|
-
disableFetch = false
|
|
16159
|
-
}) => {
|
|
16160
|
-
const [appDrawer, setAppDrawer] = (0, import_react18.useState)(null);
|
|
16161
|
-
const [runs, setRuns] = (0, import_react18.useState)(
|
|
16162
|
-
() => initialRuns ? initialRuns.slice(0, limit) : []
|
|
16163
|
-
);
|
|
16164
|
-
const [cursor, setCursor] = (0, import_react18.useState)("");
|
|
16165
|
-
const [hasMore, setHasMore] = (0, import_react18.useState)(false);
|
|
16166
|
-
const [isLoading, setIsLoading] = (0, import_react18.useState)(false);
|
|
16167
|
-
const [error, setError] = (0, import_react18.useState)(null);
|
|
16168
|
-
const [statusFilter, setStatusFilter] = (0, import_react18.useState)("");
|
|
16169
|
-
const [searchQuery, setSearchQuery] = (0, import_react18.useState)("");
|
|
16170
|
-
const [debouncedQuery, setDebouncedQuery] = (0, import_react18.useState)("");
|
|
16171
|
-
const [agentWorkflows, setAgentWorkflows] = (0, import_react18.useState)([]);
|
|
16172
|
-
const [workflowFilter, setWorkflowFilter] = (0, import_react18.useState)("");
|
|
16173
|
-
const [usecaseFilter, setUsecaseFilter] = (0, import_react18.useState)("");
|
|
16174
|
-
const [stopOpen, setStopOpen] = (0, import_react18.useState)(false);
|
|
16175
|
-
const [stopLoading, setStopLoading] = (0, import_react18.useState)(false);
|
|
16176
|
-
const [appIcons, setAppIcons] = (0, import_react18.useState)({});
|
|
16177
|
-
const [enrichedRuns, setEnrichedRuns] = (0, import_react18.useState)({});
|
|
16178
|
-
const enrichedRunsRef = (0, import_react18.useRef)({});
|
|
16179
|
-
const processedRunIdsRef = (0, import_react18.useRef)(/* @__PURE__ */ new Set());
|
|
16180
|
-
(0, import_react18.useEffect)(() => {
|
|
16181
|
-
enrichedRunsRef.current = enrichedRuns;
|
|
16182
|
-
}, [enrichedRuns]);
|
|
16183
|
-
const [abortingIds, setAbortingIds] = (0, import_react18.useState)(/* @__PURE__ */ new Set());
|
|
16184
|
-
const [abortedIds, setAbortedIds] = (0, import_react18.useState)(/* @__PURE__ */ new Set());
|
|
16185
|
-
const [lastOpenedRunId, setLastOpenedRunId] = (0, import_react18.useState)(() => getLastOpenedAgentRun());
|
|
16186
|
-
(0, import_react18.useEffect)(() => {
|
|
16187
|
-
const unsubAbort = subscribeAgentAborted((eid) => {
|
|
16188
|
-
setAbortedIds((prev) => prev.has(eid) ? prev : /* @__PURE__ */ new Set([...Array.from(prev), eid]));
|
|
16189
|
-
setRuns((prev) => prev.map((r) => r.execution_id === eid ? { ...r, status: "ABORTED" } : r));
|
|
16190
|
-
});
|
|
16191
|
-
const unsubOpened = subscribeLastOpenedAgentRun((eid) => setLastOpenedRunId(eid));
|
|
16192
|
-
return () => {
|
|
16193
|
-
unsubAbort();
|
|
16194
|
-
unsubOpened();
|
|
16195
|
-
};
|
|
16196
|
-
}, []);
|
|
16197
|
-
const debounceRef = (0, import_react18.useRef)(void 0);
|
|
16198
|
-
const selectedAgentWorkflow = agentWorkflows.find((w) => w.id === workflowFilter) || null;
|
|
16199
|
-
const openEditPrompt = (0, import_react18.useCallback)(async () => {
|
|
16200
|
-
if (!workflowFilter) return;
|
|
16201
|
-
try {
|
|
16202
|
-
const { prompt, apps } = await getAgentScheduleConfig(workflowFilter, { apiKey, apiBaseUrl, orgId });
|
|
16203
|
-
onEditWorkflow?.({
|
|
16204
|
-
workflowId: workflowFilter,
|
|
16205
|
-
name: selectedAgentWorkflow?.name || "Schedule",
|
|
16206
|
-
prompt,
|
|
16207
|
-
apps
|
|
16208
|
-
});
|
|
16209
|
-
} catch (e) {
|
|
16210
|
-
setError(e instanceof Error ? e.message : "Failed to load workflow");
|
|
16211
|
-
}
|
|
16212
|
-
}, [workflowFilter, selectedAgentWorkflow, apiKey, apiBaseUrl, orgId, onEditWorkflow]);
|
|
16213
|
-
const confirmStop = (0, import_react18.useCallback)(async () => {
|
|
16214
|
-
if (!workflowFilter) return;
|
|
16215
|
-
setStopLoading(true);
|
|
16216
|
-
try {
|
|
16217
|
-
await stopAgentSchedule(workflowFilter, { apiKey, apiBaseUrl, orgId });
|
|
16218
|
-
setStopOpen(false);
|
|
16219
|
-
const items = await listAgentScheduleWorkflows({ apiKey, apiBaseUrl, orgId });
|
|
16220
|
-
setAgentWorkflows(items);
|
|
16221
|
-
setWorkflowFilter("");
|
|
16222
|
-
} catch (e) {
|
|
16223
|
-
setError(e instanceof Error ? e.message : "Failed to stop schedule");
|
|
16224
|
-
} finally {
|
|
16225
|
-
setStopLoading(false);
|
|
16226
|
-
}
|
|
16227
|
-
}, [workflowFilter, apiKey, apiBaseUrl, orgId]);
|
|
16228
|
-
const handleAbort = (0, import_react18.useCallback)(
|
|
16229
|
-
async (run2) => {
|
|
16230
|
-
const eid = run2.execution_id;
|
|
16231
|
-
const wfId = run2.workflow_id || run2?.workflow?.id;
|
|
16232
|
-
if (!eid || !wfId) return;
|
|
16233
|
-
const prevStatus = run2.status;
|
|
16234
|
-
setAbortingIds((prev) => /* @__PURE__ */ new Set([...Array.from(prev), eid]));
|
|
16235
|
-
setAbortedIds((prev) => /* @__PURE__ */ new Set([...Array.from(prev), eid]));
|
|
16236
|
-
broadcastAgentAborted(eid);
|
|
16237
|
-
try {
|
|
16238
|
-
await abortAgentExecution({
|
|
16239
|
-
workflowId: wfId,
|
|
16240
|
-
executionId: eid,
|
|
16241
|
-
authorization: run2.authorization,
|
|
16242
|
-
apiKey,
|
|
16243
|
-
apiBaseUrl,
|
|
16244
|
-
orgId
|
|
16245
|
-
});
|
|
16246
|
-
setRuns(
|
|
16247
|
-
(prev) => prev.map((r) => r.execution_id === eid ? { ...r, status: "ABORTED" } : r)
|
|
16248
|
-
);
|
|
16249
|
-
} catch (e) {
|
|
16250
|
-
setAbortedIds((prev) => {
|
|
16251
|
-
const next = new Set(Array.from(prev));
|
|
16252
|
-
next.delete(eid);
|
|
16253
|
-
return next;
|
|
16254
|
-
});
|
|
16255
|
-
setRuns(
|
|
16256
|
-
(prev) => prev.map((r) => r.execution_id === eid ? { ...r, status: prevStatus } : r)
|
|
16257
|
-
);
|
|
16258
|
-
toast2({
|
|
16259
|
-
title: "Abort failed",
|
|
16260
|
-
description: e instanceof Error ? e.message : "Failed to abort execution",
|
|
16261
|
-
variant: "destructive"
|
|
16262
|
-
});
|
|
16263
|
-
} finally {
|
|
16264
|
-
setAbortingIds((prev) => {
|
|
16265
|
-
const next = new Set(Array.from(prev));
|
|
16266
|
-
next.delete(eid);
|
|
16267
|
-
return next;
|
|
16268
|
-
});
|
|
16269
|
-
}
|
|
16270
|
-
},
|
|
16271
|
-
[apiKey, apiBaseUrl, orgId]
|
|
16272
|
-
);
|
|
16273
|
-
const updateSearchQuery = (0, import_react18.useCallback)((q) => {
|
|
16274
|
-
setSearchQuery(q);
|
|
16275
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
16276
|
-
debounceRef.current = setTimeout(() => setDebouncedQuery(q), 350);
|
|
16277
|
-
}, []);
|
|
16278
|
-
(0, import_react18.useEffect)(() => () => {
|
|
16279
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
16280
|
-
}, []);
|
|
16281
|
-
const fetchRuns = (0, import_react18.useCallback)(
|
|
16282
|
-
async (append = false, cursorParam = "") => {
|
|
16283
|
-
if (disableFetch) {
|
|
16284
|
-
if (initialRuns && !append) {
|
|
16285
|
-
setRuns(initialRuns.slice(0, limit));
|
|
16286
|
-
}
|
|
16287
|
-
return;
|
|
16288
|
-
}
|
|
16289
|
-
setIsLoading(true);
|
|
16290
|
-
setError(null);
|
|
16291
|
-
try {
|
|
16292
|
-
const result = await searchAgentActivity({
|
|
16293
|
-
limit,
|
|
16294
|
-
top,
|
|
16295
|
-
status: statusFilter,
|
|
16296
|
-
cursor: cursorParam,
|
|
16297
|
-
apiKey,
|
|
16298
|
-
apiBaseUrl,
|
|
16299
|
-
orgId,
|
|
16300
|
-
workflowId: workflowFilter || "AGENT"
|
|
16301
|
-
});
|
|
16302
|
-
if (result.success && result.runs && result.runs.length > 0) {
|
|
16303
|
-
const pageRuns = result.runs.slice(0, limit);
|
|
16304
|
-
setRuns((prev) => append ? [...prev, ...pageRuns] : pageRuns);
|
|
16305
|
-
setCursor(result.cursor);
|
|
16306
|
-
setHasMore(!!result.cursor && result.runs.length > 0);
|
|
16307
|
-
} else if (result.success) {
|
|
16308
|
-
setRuns((prev) => append ? prev : initialRuns && initialRuns.length > 0 ? initialRuns.slice(0, limit) : []);
|
|
16309
|
-
setCursor(result.cursor);
|
|
16310
|
-
setHasMore(false);
|
|
16311
|
-
} else {
|
|
16312
|
-
if (initialRuns && initialRuns.length > 0 && !append) {
|
|
16313
|
-
setRuns(initialRuns.slice(0, limit));
|
|
16314
|
-
} else {
|
|
16315
|
-
setError("Failed to fetch agent activity");
|
|
16316
|
-
}
|
|
16317
|
-
}
|
|
16318
|
-
} catch (err) {
|
|
16319
|
-
if (initialRuns && initialRuns.length > 0 && !append) {
|
|
16320
|
-
setRuns(initialRuns.slice(0, limit));
|
|
16321
|
-
} else {
|
|
16322
|
-
setError(err instanceof Error ? err.message : "Failed to fetch agent activity");
|
|
16323
|
-
}
|
|
16324
|
-
} finally {
|
|
16325
|
-
setIsLoading(false);
|
|
16326
|
-
}
|
|
16327
|
-
},
|
|
16328
|
-
[disableFetch, initialRuns, statusFilter, limit, top, apiKey, apiBaseUrl, orgId, workflowFilter]
|
|
16329
|
-
);
|
|
16330
|
-
(0, import_react18.useEffect)(() => {
|
|
16331
|
-
fetchRuns(false, "");
|
|
16332
|
-
}, [statusFilter, apiKey, apiBaseUrl, orgId, workflowFilter]);
|
|
16333
|
-
(0, import_react18.useEffect)(() => {
|
|
16334
|
-
let cancelled = false;
|
|
16335
|
-
listAgentScheduleWorkflows({ apiKey, apiBaseUrl, orgId }).then((items) => {
|
|
16336
|
-
if (!cancelled) setAgentWorkflows(items);
|
|
16337
|
-
}).catch(() => {
|
|
16338
|
-
});
|
|
16339
|
-
return () => {
|
|
16340
|
-
cancelled = true;
|
|
16341
|
-
};
|
|
16342
|
-
}, [apiKey, apiBaseUrl, orgId]);
|
|
16343
|
-
(0, import_react18.useEffect)(() => {
|
|
16344
|
-
let cancelled = false;
|
|
16345
|
-
fetchAppsViaApiConfig().then((apps) => {
|
|
16346
|
-
if (cancelled || !Array.isArray(apps)) return;
|
|
16347
|
-
const map = {};
|
|
16348
|
-
for (const a of apps) {
|
|
16349
|
-
const name = a?.name;
|
|
16350
|
-
const img = a?.large_image || a?.image_url || a?.image;
|
|
16351
|
-
if (!name || !img) continue;
|
|
16352
|
-
map[normToolKey(name)] = img;
|
|
16353
|
-
}
|
|
16354
|
-
setAppIcons(map);
|
|
16355
|
-
}).catch(() => {
|
|
16356
|
-
});
|
|
16357
|
-
return () => {
|
|
16358
|
-
cancelled = true;
|
|
16359
|
-
};
|
|
16360
|
-
}, []);
|
|
16361
|
-
const iconAttemptsRef = (0, import_react18.useRef)(/* @__PURE__ */ new Set());
|
|
16362
|
-
const iconMountedRef = (0, import_react18.useRef)(true);
|
|
16363
|
-
(0, import_react18.useEffect)(() => {
|
|
16364
|
-
iconMountedRef.current = true;
|
|
16365
|
-
return () => {
|
|
16366
|
-
iconMountedRef.current = false;
|
|
16367
|
-
};
|
|
16368
|
-
}, []);
|
|
16369
|
-
(0, import_react18.useEffect)(() => {
|
|
16370
|
-
if (!runs.length) return;
|
|
16371
|
-
const missing = /* @__PURE__ */ new Set();
|
|
16372
|
-
for (const run2 of runs) {
|
|
16373
|
-
for (const t of getRunTools(run2)) {
|
|
16374
|
-
const key = normToolKey(t.name);
|
|
16375
|
-
if (!appIcons[key] && !iconAttemptsRef.current.has(key)) missing.add(t.name);
|
|
16376
|
-
}
|
|
16377
|
-
}
|
|
16378
|
-
if (missing.size === 0) return;
|
|
16379
|
-
(async () => {
|
|
16380
|
-
const names = [...missing];
|
|
16381
|
-
const resolved = {};
|
|
16382
|
-
for (let pass = 0; pass < 2; pass++) {
|
|
16383
|
-
for (const name of names) {
|
|
16384
|
-
const key = normToolKey(name);
|
|
16385
|
-
iconAttemptsRef.current.add(key);
|
|
16386
|
-
if (!resolved[key]) {
|
|
16387
|
-
const fb = publicAppImage(name);
|
|
16388
|
-
if (fb) {
|
|
16389
|
-
resolved[key] = fb;
|
|
16390
|
-
} else {
|
|
16391
|
-
try {
|
|
16392
|
-
const hit = await Promise.race([
|
|
16393
|
-
resolveApp(name),
|
|
16394
|
-
new Promise((r) => setTimeout(() => r(null), 4e3))
|
|
16395
|
-
]);
|
|
16396
|
-
if (hit?.image) resolved[key] = hit.image;
|
|
16397
|
-
} catch {
|
|
16398
|
-
}
|
|
16399
|
-
}
|
|
16400
|
-
}
|
|
16401
|
-
await new Promise((r) => setTimeout(r, 120));
|
|
16402
|
-
}
|
|
16403
|
-
if (pass === 0) await new Promise((r) => setTimeout(r, 2e3));
|
|
16404
|
-
}
|
|
16405
|
-
if (!iconMountedRef.current || Object.keys(resolved).length === 0) return;
|
|
16406
|
-
setAppIcons((prev) => ({ ...prev, ...resolved }));
|
|
16407
|
-
})();
|
|
16408
|
-
}, [runs, appIcons]);
|
|
16409
|
-
(0, import_react18.useEffect)(() => {
|
|
16410
|
-
if (!runs.length) return;
|
|
16411
|
-
let cancelled = false;
|
|
16412
|
-
const buildPatchFromRun = (r) => {
|
|
16413
|
-
const results = Array.isArray(r.results) ? r.results : null;
|
|
16414
|
-
if (!results || results.length === 0) return null;
|
|
16415
|
-
const agentResult = results.find((x) => x?.action?.app_name === "AI Agent");
|
|
16416
|
-
let decisions = Array.isArray(r.decisions) ? r.decisions : void 0;
|
|
16417
|
-
let originalInput;
|
|
16418
|
-
let allowedActions;
|
|
16419
|
-
if (agentResult?.result) {
|
|
16420
|
-
try {
|
|
16421
|
-
const parsed = JSON.parse(agentResult.result);
|
|
16422
|
-
if (!decisions && Array.isArray(parsed?.decisions)) decisions = parsed.decisions;
|
|
16423
|
-
if (typeof parsed?.original_input === "string") originalInput = parsed.original_input;
|
|
16424
|
-
if (Array.isArray(parsed?.allowed_actions)) allowedActions = parsed.allowed_actions;
|
|
16425
|
-
} catch {
|
|
16426
|
-
}
|
|
16427
|
-
}
|
|
16428
|
-
const hasPrompt = !!r.execution_argument || !!originalInput || !!agentResult?.result;
|
|
16429
|
-
if (!decisions || !hasPrompt) return null;
|
|
16430
|
-
const patch2 = {
|
|
16431
|
-
results,
|
|
16432
|
-
execution_argument: r.execution_argument,
|
|
16433
|
-
result: agentResult?.result ?? r.result,
|
|
16434
|
-
decisions,
|
|
16435
|
-
allowed_actions: allowedActions
|
|
16436
|
-
};
|
|
16437
|
-
if (originalInput && !patch2.execution_argument) {
|
|
16438
|
-
patch2.execution_argument = JSON.stringify({ original_input: originalInput });
|
|
16439
|
-
}
|
|
16440
|
-
return patch2;
|
|
16441
|
-
};
|
|
16442
|
-
let pending = {};
|
|
16443
|
-
let flushTimer = null;
|
|
16444
|
-
const flush = () => {
|
|
16445
|
-
flushTimer = null;
|
|
16446
|
-
if (cancelled) return;
|
|
16447
|
-
const batch = pending;
|
|
16448
|
-
pending = {};
|
|
16449
|
-
if (Object.keys(batch).length === 0) return;
|
|
16450
|
-
setEnrichedRuns((prev) => ({ ...prev, ...batch }));
|
|
16451
|
-
};
|
|
16452
|
-
const queuePatch = (id, patch2) => {
|
|
16453
|
-
pending[id] = patch2;
|
|
16454
|
-
if (!flushTimer) flushTimer = setTimeout(flush, 250);
|
|
16455
|
-
};
|
|
16456
|
-
const needsFetch = [];
|
|
16457
|
-
for (const r of runs) {
|
|
16458
|
-
const id = r.execution_id;
|
|
16459
|
-
if (!id || enrichedRunsRef.current[id] || processedRunIdsRef.current.has(id)) continue;
|
|
16460
|
-
if (id.startsWith("demo-") || id.startsWith("dummy-")) {
|
|
16461
|
-
processedRunIdsRef.current.add(id);
|
|
16462
|
-
continue;
|
|
16463
|
-
}
|
|
16464
|
-
const native = buildPatchFromRun(r);
|
|
16465
|
-
if (native) {
|
|
16466
|
-
processedRunIdsRef.current.add(id);
|
|
16467
|
-
queuePatch(id, native);
|
|
16468
|
-
} else {
|
|
16469
|
-
needsFetch.push(id);
|
|
16470
|
-
}
|
|
16471
|
-
}
|
|
16472
|
-
const CONCURRENCY = 2;
|
|
16473
|
-
let i = 0;
|
|
16474
|
-
const fetchOne = async (executionId) => {
|
|
16475
|
-
try {
|
|
16476
|
-
const resp = await fetch(
|
|
16477
|
-
getApiUrl(
|
|
16478
|
-
`/api/v1/executions/${encodeURIComponent(executionId)}?authorization=${encodeURIComponent(executionId)}`
|
|
16479
|
-
),
|
|
16480
|
-
{
|
|
16481
|
-
method: "GET",
|
|
16482
|
-
credentials: "include",
|
|
16483
|
-
headers: {
|
|
16484
|
-
...apiKey ? { Authorization: `Bearer ${apiKey}` } : getAuthHeader(),
|
|
16485
|
-
...orgId ? { "Org-Id": orgId } : {}
|
|
16486
|
-
}
|
|
16487
|
-
}
|
|
16488
|
-
);
|
|
16489
|
-
if (!resp.ok || cancelled) return;
|
|
16490
|
-
const json = await resp.json().catch(() => null);
|
|
16491
|
-
if (!json || cancelled) return;
|
|
16492
|
-
let decisions;
|
|
16493
|
-
let originalInput;
|
|
16494
|
-
let allowedActions;
|
|
16495
|
-
const agentResult = Array.isArray(json.results) ? json.results.find((r) => r?.action?.app_name === "AI Agent") : null;
|
|
16496
|
-
if (agentResult?.result) {
|
|
16497
|
-
try {
|
|
16498
|
-
const parsed = JSON.parse(agentResult.result);
|
|
16499
|
-
if (Array.isArray(parsed?.decisions)) decisions = parsed.decisions;
|
|
16500
|
-
if (typeof parsed?.original_input === "string") originalInput = parsed.original_input;
|
|
16501
|
-
if (Array.isArray(parsed?.allowed_actions)) allowedActions = parsed.allowed_actions;
|
|
16502
|
-
} catch {
|
|
16503
|
-
}
|
|
16504
|
-
}
|
|
16505
|
-
const patch2 = {
|
|
16506
|
-
results: json.results,
|
|
16507
|
-
execution_argument: json.execution_argument,
|
|
16508
|
-
result: agentResult?.result,
|
|
16509
|
-
decisions,
|
|
16510
|
-
allowed_actions: allowedActions
|
|
16511
|
-
};
|
|
16512
|
-
if (originalInput && !patch2.execution_argument) {
|
|
16513
|
-
patch2.execution_argument = JSON.stringify({ original_input: originalInput });
|
|
16514
|
-
}
|
|
16515
|
-
queuePatch(executionId, patch2);
|
|
16516
|
-
} catch {
|
|
16517
|
-
}
|
|
16518
|
-
};
|
|
16519
|
-
const idle = (fn) => {
|
|
16520
|
-
const ric = window.requestIdleCallback;
|
|
16521
|
-
if (ric) ric(fn, { timeout: 1e3 });
|
|
16522
|
-
else setTimeout(fn, 32);
|
|
16523
|
-
};
|
|
16524
|
-
const nextIdle = () => new Promise((resolve) => idle(resolve));
|
|
16525
|
-
if (needsFetch.length > 0) {
|
|
16526
|
-
for (const id of needsFetch) processedRunIdsRef.current.add(id);
|
|
16527
|
-
const workers = Array.from({ length: Math.min(CONCURRENCY, needsFetch.length) }, async () => {
|
|
16528
|
-
while (!cancelled && i < needsFetch.length) {
|
|
16529
|
-
const id = needsFetch[i++];
|
|
16530
|
-
await nextIdle();
|
|
16531
|
-
if (cancelled) return;
|
|
16532
|
-
await fetchOne(id);
|
|
16533
|
-
}
|
|
16534
|
-
});
|
|
16535
|
-
Promise.all(workers).catch(() => {
|
|
16536
|
-
});
|
|
16537
|
-
}
|
|
16538
|
-
return () => {
|
|
16539
|
-
cancelled = true;
|
|
16540
|
-
if (flushTimer) clearTimeout(flushTimer);
|
|
16541
|
-
};
|
|
16542
|
-
}, [runs, apiKey, orgId]);
|
|
16543
|
-
const mergedRuns = runs.map((r) => {
|
|
16544
|
-
const patch2 = r.execution_id ? enrichedRuns[r.execution_id] : void 0;
|
|
16545
|
-
const merged = patch2 ? { ...r, ...patch2 } : r;
|
|
16546
|
-
return r.execution_id && abortedIds.has(r.execution_id) ? { ...merged, status: "ABORTED" } : merged;
|
|
16547
|
-
});
|
|
16548
|
-
const loadMore = (0, import_react18.useCallback)(() => {
|
|
16549
|
-
if (cursor && !isLoading) fetchRuns(true, cursor);
|
|
16550
|
-
}, [cursor, isLoading, fetchRuns]);
|
|
16551
|
-
const activeUsecase = usecaseFilters.find((u) => u.id === usecaseFilter) || null;
|
|
16552
|
-
const usecaseMatchedRuns = activeUsecase ? mergedRuns.filter((r) => {
|
|
16553
|
-
const hay = [
|
|
16554
|
-
r.execution_source,
|
|
16555
|
-
r.workflow?.name,
|
|
16556
|
-
r.workflow?.description,
|
|
16557
|
-
r.execution_argument,
|
|
16558
|
-
...(r.workflow?.actions || []).flatMap((a) => [a.app_name, a.label])
|
|
16559
|
-
].filter(Boolean).join(" ").toLowerCase();
|
|
16560
|
-
return activeUsecase.matchTokens.some((t) => hay.includes(t.toLowerCase()));
|
|
16561
|
-
}) : mergedRuns;
|
|
16562
|
-
const filteredRuns = debouncedQuery ? usecaseMatchedRuns.filter((r) => {
|
|
16563
|
-
const hay = [
|
|
16564
|
-
getRunTitle(r),
|
|
16565
|
-
getRunSubtitle(r),
|
|
16566
|
-
r.execution_id,
|
|
16567
|
-
r.execution_source,
|
|
16568
|
-
r.status
|
|
16569
|
-
].filter(Boolean).join(" ").toLowerCase();
|
|
16570
|
-
return hay.includes(debouncedQuery.toLowerCase());
|
|
16571
|
-
}) : usecaseMatchedRuns;
|
|
16572
|
-
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
16573
|
-
import_material17.Box,
|
|
16574
|
-
{
|
|
16575
|
-
className,
|
|
16576
|
-
sx: [{ display: "flex", flexDirection: "column", gap: 2 }, ...Array.isArray(sx) ? sx : sx ? [sx] : []],
|
|
16577
|
-
children: [
|
|
16578
|
-
(showSearchBar || showStatusChips) && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: [{ display: "flex", alignItems: "center", justifyContent: "center", gap: 1.5, flexWrap: "wrap" }, ...Array.isArray(toolbarSx) ? toolbarSx : toolbarSx ? [toolbarSx] : []], children: [
|
|
16579
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
16580
|
-
import_material17.Select,
|
|
16581
|
-
{
|
|
16582
|
-
size: "small",
|
|
16583
|
-
value: usecaseFilter ? `uc:${usecaseFilter}` : workflowFilter,
|
|
16584
|
-
onChange: (e) => {
|
|
16585
|
-
const val = String(e.target.value);
|
|
16586
|
-
if (val.startsWith("uc:")) {
|
|
16587
|
-
setUsecaseFilter(val.slice(3));
|
|
16588
|
-
setWorkflowFilter("");
|
|
16589
|
-
} else {
|
|
16590
|
-
setUsecaseFilter("");
|
|
16591
|
-
setWorkflowFilter(val);
|
|
16592
|
-
}
|
|
16593
|
-
},
|
|
16594
|
-
displayEmpty: true,
|
|
16595
|
-
renderValue: (val) => {
|
|
16596
|
-
if (!val) return "All Agent runs";
|
|
16597
|
-
if (String(val).startsWith("uc:")) {
|
|
16598
|
-
const uc = usecaseFilters.find((u) => u.id === String(val).slice(3));
|
|
16599
|
-
return uc?.label || "Usecase agent";
|
|
16600
|
-
}
|
|
16601
|
-
const wf = agentWorkflows.find((w) => w.id === val);
|
|
16602
|
-
return wf?.name || "Selected workflow";
|
|
16603
|
-
},
|
|
16604
|
-
sx: {
|
|
16605
|
-
height: 36,
|
|
16606
|
-
minWidth: 200,
|
|
16607
|
-
maxWidth: 260,
|
|
16608
|
-
fontSize: "0.85rem",
|
|
16609
|
-
bgcolor: "hsl(var(--card))",
|
|
16610
|
-
color: "hsl(var(--foreground))",
|
|
16611
|
-
borderRadius: 1.5,
|
|
16612
|
-
"& .MuiOutlinedInput-notchedOutline": { borderColor: "hsl(var(--border))" },
|
|
16613
|
-
"&:hover .MuiOutlinedInput-notchedOutline": { borderColor: "hsl(var(--muted-foreground) / 0.3)" },
|
|
16614
|
-
"&.Mui-focused .MuiOutlinedInput-notchedOutline": { borderColor: "hsl(var(--primary))" }
|
|
16615
|
-
},
|
|
16616
|
-
MenuProps: {
|
|
16617
|
-
slotProps: {
|
|
16618
|
-
paper: {
|
|
16619
|
-
sx: {
|
|
16620
|
-
bgcolor: "hsl(var(--card))",
|
|
16621
|
-
border: "1px solid hsl(var(--border))",
|
|
16622
|
-
color: "hsl(var(--foreground))",
|
|
16623
|
-
maxHeight: 320
|
|
16624
|
-
}
|
|
16625
|
-
}
|
|
16626
|
-
}
|
|
16627
|
-
},
|
|
16628
|
-
children: [
|
|
16629
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.MenuItem, { value: "", sx: { fontSize: "0.85rem" }, children: "All Agent runs" }),
|
|
16630
|
-
usecaseFilters.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Divider, { sx: { borderColor: "hsl(var(--border))", my: 0.5 } }),
|
|
16631
|
-
usecaseFilters.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
16632
|
-
import_material17.Box,
|
|
16633
|
-
{
|
|
16634
|
-
sx: {
|
|
16635
|
-
px: 1.5,
|
|
16636
|
-
py: 0.5,
|
|
16637
|
-
fontSize: "0.7rem",
|
|
16638
|
-
textTransform: "uppercase",
|
|
16639
|
-
letterSpacing: "0.04em",
|
|
16640
|
-
color: "hsl(var(--muted-foreground))",
|
|
16641
|
-
pointerEvents: "none"
|
|
16642
|
-
},
|
|
16643
|
-
children: "Usecase agents"
|
|
16644
|
-
}
|
|
16645
|
-
),
|
|
16646
|
-
usecaseFilters.map((u) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.MenuItem, { value: `uc:${u.id}`, sx: { fontSize: "0.85rem" }, children: u.label }, u.id)),
|
|
16647
|
-
agentWorkflows.length > 0 && usecaseFilters.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Divider, { sx: { borderColor: "hsl(var(--border))", my: 0.5 } }),
|
|
16648
|
-
agentWorkflows.map((w) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.MenuItem, { value: w.id, sx: { fontSize: "0.85rem" }, children: w.name }, w.id)),
|
|
16649
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Divider, { sx: { borderColor: "hsl(var(--border))", my: 0.5 } }),
|
|
16650
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
16651
|
-
import_material17.Box,
|
|
16652
|
-
{
|
|
16653
|
-
sx: {
|
|
16654
|
-
px: 1.5,
|
|
16655
|
-
py: 1,
|
|
16656
|
-
fontSize: "0.72rem",
|
|
16657
|
-
color: "hsl(var(--muted-foreground))",
|
|
16658
|
-
lineHeight: 1.45,
|
|
16659
|
-
whiteSpace: "normal",
|
|
16660
|
-
maxWidth: 280,
|
|
16661
|
-
pointerEvents: "none"
|
|
16662
|
-
},
|
|
16663
|
-
children: "Scheduled agent runs will appear in this list once you set them up."
|
|
16664
|
-
}
|
|
16665
|
-
)
|
|
16666
|
-
]
|
|
16667
|
-
}
|
|
16668
|
-
),
|
|
16669
|
-
showSearchBar && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
16670
|
-
import_material17.TextField,
|
|
16671
|
-
{
|
|
16672
|
-
placeholder: "Search results...",
|
|
16673
|
-
size: "small",
|
|
16674
|
-
value: searchQuery,
|
|
16675
|
-
onChange: (e) => updateSearchQuery(e.target.value),
|
|
16676
|
-
slotProps: {
|
|
16677
|
-
input: {
|
|
16678
|
-
startAdornment: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.InputAdornment, { position: "start", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Search, { size: 18, color: "hsl(var(--muted-foreground))" }) })
|
|
16679
|
-
}
|
|
16680
|
-
},
|
|
16681
|
-
sx: {
|
|
16682
|
-
flex: 1,
|
|
16683
|
-
maxWidth: 280,
|
|
16684
|
-
minWidth: 160,
|
|
16685
|
-
"& .MuiOutlinedInput-root": {
|
|
16686
|
-
bgcolor: "hsl(var(--card))",
|
|
16687
|
-
borderRadius: 1.5,
|
|
16688
|
-
fontSize: "0.85rem",
|
|
16689
|
-
height: 36,
|
|
16690
|
-
"& fieldset": { borderColor: "hsl(var(--border))" },
|
|
16691
|
-
"&:hover fieldset": { borderColor: "hsl(var(--muted-foreground) / 0.3)" },
|
|
16692
|
-
"&.Mui-focused fieldset": { borderColor: "hsl(var(--primary))" }
|
|
16693
|
-
},
|
|
16694
|
-
"& .MuiInputBase-input": { color: "hsl(var(--foreground))" }
|
|
16695
|
-
}
|
|
16696
|
-
}
|
|
16697
|
-
),
|
|
16698
|
-
showStatusChips && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
16699
|
-
SegmentedControl,
|
|
16700
|
-
{
|
|
16701
|
-
size: "sm",
|
|
16702
|
-
ariaLabel: "Filter by status",
|
|
16703
|
-
value: statusFilter,
|
|
16704
|
-
onChange: setStatusFilter,
|
|
16705
|
-
options: STATUS_FILTERS.map((f) => ({ value: f.value, label: f.label }))
|
|
16706
|
-
}
|
|
16707
|
-
),
|
|
16708
|
-
isLoading && runs.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.CircularProgress, { size: 16, sx: { color: "hsl(var(--primary))", ml: 0.5 } })
|
|
16709
|
-
] }),
|
|
16710
|
-
selectedAgentWorkflow && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
16711
|
-
import_material17.Box,
|
|
16712
|
-
{
|
|
16713
|
-
sx: {
|
|
16714
|
-
display: "flex",
|
|
16715
|
-
alignItems: "center",
|
|
16716
|
-
gap: 1.5,
|
|
16717
|
-
p: 1.5,
|
|
16718
|
-
borderRadius: 2,
|
|
16719
|
-
border: "1px solid hsl(var(--border))",
|
|
16720
|
-
bgcolor: "hsl(var(--card))"
|
|
16721
|
-
},
|
|
16722
|
-
children: [
|
|
16723
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: { flex: 1, minWidth: 0 }, children: [
|
|
16724
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Typography, { sx: { fontSize: "0.85rem", fontWeight: 600, color: "hsl(var(--foreground))", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: selectedAgentWorkflow.name }),
|
|
16725
|
-
selectedAgentWorkflow.description && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Typography, { sx: { fontSize: "0.75rem", color: "hsl(var(--muted-foreground))", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: selectedAgentWorkflow.description })
|
|
16726
|
-
] }),
|
|
16727
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
16728
|
-
import_material17.Button,
|
|
16729
|
-
{
|
|
16730
|
-
size: "small",
|
|
16731
|
-
startIcon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react8.Pencil, { size: 14 }),
|
|
16732
|
-
onClick: openEditPrompt,
|
|
16733
|
-
sx: {
|
|
16734
|
-
height: 36,
|
|
16735
|
-
border: "1px solid hsl(var(--border))",
|
|
16736
|
-
borderRadius: 1.5,
|
|
16737
|
-
color: "hsl(var(--foreground))",
|
|
16738
|
-
textTransform: "none",
|
|
16739
|
-
fontSize: "0.8rem",
|
|
16740
|
-
px: 1.5,
|
|
16741
|
-
"&:hover": { bgcolor: "hsl(var(--muted))" }
|
|
16742
|
-
},
|
|
16743
|
-
children: "Edit"
|
|
16744
|
-
}
|
|
16745
|
-
),
|
|
16746
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
16747
|
-
import_material17.Button,
|
|
16748
|
-
{
|
|
16749
|
-
size: "small",
|
|
16750
|
-
startIcon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react8.StopCircle, { size: 14 }),
|
|
16751
|
-
onClick: () => setStopOpen(true),
|
|
16752
|
-
sx: {
|
|
16753
|
-
height: 36,
|
|
16754
|
-
border: "1px solid hsl(var(--severity-critical, 0 72% 55%) / 0.4)",
|
|
16755
|
-
borderRadius: 1.5,
|
|
16756
|
-
color: "hsl(var(--severity-critical, 0 72% 55%))",
|
|
16757
|
-
textTransform: "none",
|
|
16758
|
-
fontSize: "0.8rem",
|
|
16759
|
-
px: 1.5,
|
|
16760
|
-
"&:hover": { bgcolor: "hsla(var(--severity-critical, 0 72% 55%) / 0.08)" }
|
|
16761
|
-
},
|
|
16762
|
-
children: "Stop schedule"
|
|
16763
|
-
}
|
|
16764
|
-
)
|
|
16765
|
-
]
|
|
16766
|
-
}
|
|
16767
|
-
),
|
|
16768
|
-
isLoading && runs.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Box, { sx: { display: "flex", justifyContent: "center", py: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.CircularProgress, { size: 28, sx: { color: "hsl(var(--primary))" } }) }) : error ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: { display: "flex", flexDirection: "column", alignItems: "center", textAlign: "center", py: 6 }, children: [
|
|
16769
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.AlertCircle, { size: 28, style: { color: "hsl(var(--severity-critical, 0 72% 55%))", marginBottom: 8 } }),
|
|
16770
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
16771
|
-
import_material17.Typography,
|
|
16772
|
-
{
|
|
16773
|
-
sx: { color: "hsl(var(--severity-critical, 0 72% 55%))", fontSize: "0.9rem", mb: 1 },
|
|
16774
|
-
children: error
|
|
16775
|
-
}
|
|
16776
|
-
),
|
|
16777
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
16778
|
-
import_material17.Button,
|
|
16779
|
-
{
|
|
16780
|
-
size: "small",
|
|
16781
|
-
onClick: () => fetchRuns(false, ""),
|
|
16782
|
-
sx: { color: "hsl(var(--primary))", textTransform: "none" },
|
|
16783
|
-
children: "Try again"
|
|
16784
|
-
}
|
|
16785
|
-
)
|
|
16786
|
-
] }) : filteredRuns.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: { display: "flex", flexDirection: "column", alignItems: "center", textAlign: "center", py: 8 }, children: [
|
|
16787
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
16788
|
-
import_lucide_react7.Activity,
|
|
16789
|
-
{
|
|
16790
|
-
size: 40,
|
|
16791
|
-
style: { color: "hsl(var(--muted-foreground))", marginBottom: 12 }
|
|
16792
|
-
}
|
|
16793
|
-
),
|
|
16794
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Typography, { sx: { color: "hsl(var(--muted-foreground))", fontSize: "0.9rem" }, children: emptyTitle }),
|
|
16795
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
16796
|
-
import_material17.Typography,
|
|
16797
|
-
{
|
|
16798
|
-
sx: { color: "hsl(var(--muted-foreground))", fontSize: "0.8rem", mt: 0.5, opacity: 0.7 },
|
|
16799
|
-
children: emptySubtitle
|
|
16800
|
-
}
|
|
16801
|
-
)
|
|
16802
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: { display: "flex", flexDirection: "column", gap: 1.5 }, children: [
|
|
16803
|
-
filteredRuns.map((run2, idx) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
16804
|
-
AgentRunRow,
|
|
16805
|
-
{
|
|
16806
|
-
run: run2,
|
|
16807
|
-
onClick: () => {
|
|
16808
|
-
setLastOpenedAgentRun(run2.execution_id);
|
|
16809
|
-
setLastOpenedRunId(run2.execution_id || null);
|
|
16810
|
-
onRunClick?.(run2);
|
|
16811
|
-
},
|
|
16812
|
-
sx: [
|
|
16813
|
-
...Array.isArray(rowSx) ? rowSx : rowSx ? [rowSx] : [],
|
|
16814
|
-
...run2.execution_id && run2.execution_id === lastOpenedRunId ? [{
|
|
16815
|
-
borderColor: "hsl(var(--primary) / 0.55)",
|
|
16816
|
-
bgcolor: "hsl(var(--primary) / 0.06)"
|
|
16817
|
-
}] : []
|
|
16818
|
-
],
|
|
16819
|
-
appIcons,
|
|
16820
|
-
onAppClick: (app2) => setAppDrawer(app2),
|
|
16821
|
-
apiKey,
|
|
16822
|
-
apiBaseUrl,
|
|
16823
|
-
orgId,
|
|
16824
|
-
abortingIds,
|
|
16825
|
-
onAbort: handleAbort
|
|
16826
|
-
},
|
|
16827
|
-
run2.execution_id || idx
|
|
16828
|
-
)),
|
|
16829
|
-
hasMore && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Box, { sx: { display: "flex", justifyContent: "center", mt: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
16830
|
-
import_material17.Button,
|
|
16831
|
-
{
|
|
16832
|
-
size: "small",
|
|
16833
|
-
onClick: loadMore,
|
|
16834
|
-
disabled: isLoading,
|
|
16835
|
-
sx: { color: "hsl(var(--primary))", textTransform: "none", fontSize: "0.8rem" },
|
|
16836
|
-
children: [
|
|
16837
|
-
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.CircularProgress, { size: 14, sx: { mr: 1 } }) : null,
|
|
16838
|
-
"Load more"
|
|
16839
|
-
]
|
|
16840
|
-
}
|
|
16841
|
-
) })
|
|
16842
|
-
] }),
|
|
16843
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
16844
|
-
import_material17.Dialog,
|
|
16845
|
-
{
|
|
16846
|
-
open: stopOpen,
|
|
16847
|
-
onClose: () => stopLoading ? null : setStopOpen(false),
|
|
16848
|
-
slotProps: { paper: { sx: { bgcolor: "hsl(var(--card))", color: "hsl(var(--foreground))", border: "1px solid hsl(var(--border))" } } },
|
|
16849
|
-
children: [
|
|
16850
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.DialogTitle, { sx: { fontSize: "1rem", fontWeight: 600 }, children: "Stop schedule?" }),
|
|
16851
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.DialogContent, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Typography, { sx: { fontSize: "0.85rem", color: "hsl(var(--muted-foreground))" }, children: [
|
|
16852
|
-
'This will stop "',
|
|
16853
|
-
selectedAgentWorkflow?.name,
|
|
16854
|
-
'" and delete the scheduled workflow. Past executions remain visible. This cannot be undone.'
|
|
16855
|
-
] }) }),
|
|
16856
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.DialogActions, { sx: { px: 3, pb: 2 }, children: [
|
|
16857
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Button, { onClick: () => setStopOpen(false), disabled: stopLoading, sx: { textTransform: "none", color: "hsl(var(--muted-foreground))" }, children: "Cancel" }),
|
|
16858
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
16859
|
-
import_material17.Button,
|
|
16860
|
-
{
|
|
16861
|
-
onClick: confirmStop,
|
|
16862
|
-
disabled: stopLoading,
|
|
16863
|
-
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)" } },
|
|
16864
|
-
children: [
|
|
16865
|
-
stopLoading ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.CircularProgress, { size: 16, sx: { color: "hsl(var(--primary-foreground))", mr: 1 } }) : null,
|
|
16866
|
-
"Stop schedule"
|
|
16867
|
-
]
|
|
16868
|
-
}
|
|
16869
|
-
)
|
|
16870
|
-
] })
|
|
16871
|
-
]
|
|
16872
|
-
}
|
|
16873
|
-
),
|
|
16874
|
-
renderAppDetailDrawer ? renderAppDetailDrawer({
|
|
16875
|
-
open: !!appDrawer,
|
|
16876
|
-
onClose: () => setAppDrawer(null),
|
|
16877
|
-
appName: appDrawer?.name || null,
|
|
16878
|
-
appId: appDrawer?.id || null,
|
|
16879
|
-
activeOrgId: orgId || null,
|
|
16880
|
-
globalUrl: globalUrl || apiBaseUrl,
|
|
16881
|
-
theme,
|
|
16882
|
-
colorMode
|
|
16883
|
-
}) : null
|
|
16884
|
-
]
|
|
16885
|
-
}
|
|
16886
|
-
);
|
|
16887
|
-
};
|
|
16888
|
-
var AgentActivityList_default = AgentActivityList;
|
|
16889
|
-
|
|
16890
|
-
// ../components/docs/tocUtils.ts
|
|
16891
|
-
var isTocHeading = (text2) => {
|
|
16892
|
-
const norm4 = text2.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
16893
|
-
return norm4 === "tableofcontents" || norm4 === "tableofcontent" || norm4 === "toc";
|
|
16894
|
-
};
|
|
16895
|
-
var stripMarkdownInline = (value) => value.replace(/!\[[^\]]*\]\([^)]*\)/g, "").replace(/\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/<[^>]+>/g, "").replace(/[`*_~#>]/g, "").replace(/\{#[^}]+\}/g, "").replace(/\s+/g, " ").trim();
|
|
16896
|
-
var stripInContentToc = (markdown) => {
|
|
16897
|
-
if (!markdown) return "";
|
|
16898
|
-
const lines = markdown.split("\n");
|
|
16899
|
-
const result = [];
|
|
16900
|
-
let inToc = false;
|
|
16901
|
-
for (let i = 0; i < lines.length; i++) {
|
|
16902
|
-
const line = lines[i];
|
|
16903
|
-
const trimmed = line.trim();
|
|
16904
|
-
const headingMatch = trimmed.match(/^(#{1,4})\s+(.+)$/);
|
|
16905
|
-
if (headingMatch) {
|
|
16906
|
-
const headingText = stripMarkdownInline(headingMatch[2]);
|
|
16907
|
-
if (isTocHeading(headingText)) {
|
|
16908
|
-
inToc = true;
|
|
16909
|
-
continue;
|
|
16910
|
-
} else if (inToc) {
|
|
16911
|
-
inToc = false;
|
|
16912
|
-
}
|
|
16913
|
-
}
|
|
16914
|
-
if (inToc) {
|
|
16915
|
-
if (!trimmed || /^[-*+]\s+/.test(trimmed) || /^\d+\.\s+/.test(trimmed) || /^[-*_]{3,}$/.test(trimmed) || /^>/.test(trimmed) || /^<ul[\s\S]*?<\/ul>$/i.test(trimmed)) {
|
|
16916
|
-
continue;
|
|
16917
|
-
} else {
|
|
16918
|
-
inToc = false;
|
|
16919
|
-
}
|
|
16920
|
-
}
|
|
16921
|
-
result.push(line);
|
|
16922
|
-
}
|
|
16923
|
-
return result.join("\n");
|
|
16924
|
-
};
|
|
16925
|
-
|
|
16926
|
-
// ../lib/docGroups.ts
|
|
16927
|
-
var DOC_GROUPS = [
|
|
16928
|
-
{
|
|
16929
|
-
id: "usability",
|
|
16930
|
-
label: "Usability",
|
|
16931
|
-
description: "Platform overview, getting started, tenants, features, and API reference.",
|
|
16932
|
-
slugs: ["getting_started", "getting-started", "about", "tenants", "features", "api"]
|
|
16933
|
-
},
|
|
16934
|
-
{
|
|
16935
|
-
id: "automation",
|
|
16936
|
-
label: "Automation",
|
|
16937
|
-
description: "Workflows, apps, AI agents, and playbook automations.",
|
|
16938
|
-
slugs: ["workflows", "apps", "ai"]
|
|
16939
|
-
},
|
|
16940
|
-
{
|
|
16941
|
-
id: "security",
|
|
16942
|
-
label: "Security",
|
|
16943
|
-
description: "Incident triage, vulnerability management, host posture, and response.",
|
|
16944
|
-
slugs: ["incidents", "vulnerabilities", "monitors", "host-monitors", "detection", "sigma"]
|
|
16945
|
-
},
|
|
16946
|
-
{
|
|
16947
|
-
id: "infrastructure",
|
|
16948
|
-
label: "Infrastructure",
|
|
16949
|
-
description: "Architecture, configuration, extensions, and troubleshooting.",
|
|
16950
|
-
slugs: ["architecture", "configuration", "extensions", "troubleshooting"]
|
|
16951
|
-
}
|
|
16952
|
-
];
|
|
16953
|
-
var DOC_LABEL_OVERRIDES = {
|
|
16954
|
-
ai: "Agents & AI",
|
|
16955
|
-
api: "API Reference",
|
|
16956
|
-
getting_started: "Getting Started",
|
|
16957
|
-
incidents: "Incidents & Cases",
|
|
16958
|
-
vulnerabilities: "Vulnerabilities",
|
|
16959
|
-
monitors: "Host Monitors",
|
|
16960
|
-
"host-monitors": "Host Monitors",
|
|
16961
|
-
about: "About Shuffle",
|
|
16962
|
-
tenants: "Tenants & Multi-Tenancy",
|
|
16963
|
-
features: "Features Overview",
|
|
16964
|
-
workflows: "Workflows",
|
|
16965
|
-
apps: "Apps & Integrations",
|
|
16966
|
-
extensions: "Extensions",
|
|
16967
|
-
architecture: "Architecture",
|
|
16968
|
-
configuration: "Configuration",
|
|
16969
|
-
troubleshooting: "Troubleshooting"
|
|
16970
|
-
};
|
|
16971
|
-
var getDocGroup = (slug) => {
|
|
16972
|
-
const usabilityGroup = DOC_GROUPS.find((g) => g.id === "usability") || DOC_GROUPS[0];
|
|
16973
|
-
if (!slug || slug === "index") return usabilityGroup;
|
|
16974
|
-
const clean = slug.toLowerCase().replace(/_+/g, "-");
|
|
16975
|
-
for (const group of DOC_GROUPS) {
|
|
16976
|
-
if (group.slugs.includes(clean)) {
|
|
16977
|
-
return group;
|
|
16978
|
-
}
|
|
16979
|
-
}
|
|
16980
|
-
return usabilityGroup;
|
|
16981
|
-
};
|
|
16982
|
-
var getDocDisplayLabel = (slug, fallbackName) => {
|
|
16983
|
-
const clean = (slug || "").toLowerCase().replace(/_+/g, "-");
|
|
16984
|
-
if (DOC_LABEL_OVERRIDES[clean]) {
|
|
16985
|
-
return DOC_LABEL_OVERRIDES[clean];
|
|
16986
|
-
}
|
|
16987
|
-
const raw = fallbackName || slug || "";
|
|
16988
|
-
return raw.replace(/[_-]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
16989
|
-
};
|
|
16990
|
-
|
|
16991
|
-
// ../components/docs/docGroups.ts
|
|
16992
|
-
var groupDocsCache = /* @__PURE__ */ new Map();
|
|
16993
|
-
var getCachedGroupDocs = (groupId, folder) => {
|
|
16994
|
-
const cacheKey3 = `${folder || "docs"}:${groupId}`;
|
|
16995
|
-
const cached2 = groupDocsCache.get(cacheKey3);
|
|
16996
|
-
if (!cached2) return [];
|
|
16997
|
-
return Object.entries(cached2).map(([slug, content]) => ({
|
|
16998
|
-
slug,
|
|
16999
|
-
title: getDocDisplayLabel(slug),
|
|
17000
|
-
content
|
|
17001
|
-
}));
|
|
17002
|
-
};
|
|
17003
|
-
|
|
17004
|
-
// ../lib/incidentPromptContext.ts
|
|
17005
|
-
var GLOBAL_INCIDENT_KEY = "__shuffleActiveIncidentContext";
|
|
17006
|
-
var getGlobalScope = () => {
|
|
17007
|
-
if (typeof globalThis !== "undefined") return globalThis;
|
|
17008
|
-
if (typeof window !== "undefined") return window;
|
|
17009
|
-
if (typeof global !== "undefined") return global;
|
|
17010
|
-
return null;
|
|
17011
|
-
};
|
|
17012
|
-
var getActiveIncidentPromptContext = () => {
|
|
17013
|
-
const scope2 = getGlobalScope();
|
|
17014
|
-
if (!scope2) return null;
|
|
17015
|
-
const info = scope2[GLOBAL_INCIDENT_KEY];
|
|
17016
|
-
if (info && typeof info === "object") {
|
|
17017
|
-
return info;
|
|
17018
|
-
}
|
|
17019
|
-
return null;
|
|
17020
|
-
};
|
|
17021
|
-
var INCIDENT_PROMPT_DELIMITER_START = "=== INCIDENT CONTEXT ===";
|
|
17022
|
-
var INCIDENT_PROMPT_DELIMITER_END = "=== END INCIDENT CONTEXT ===";
|
|
17023
|
-
var formatObservableType = (type) => {
|
|
17024
|
-
if (!type) return "Indicator";
|
|
17025
|
-
const clean = type.toLowerCase().trim();
|
|
17026
|
-
switch (clean) {
|
|
17027
|
-
case "ip":
|
|
17028
|
-
case "ipv4":
|
|
17029
|
-
case "ip_address":
|
|
17030
|
-
return "IPv4";
|
|
17031
|
-
case "ipv6":
|
|
17032
|
-
return "IPv6";
|
|
17033
|
-
case "hash":
|
|
17034
|
-
case "sha256":
|
|
17035
|
-
case "file_hash":
|
|
17036
|
-
return "SHA256";
|
|
17037
|
-
case "md5":
|
|
17038
|
-
return "MD5";
|
|
17039
|
-
case "sha1":
|
|
17040
|
-
return "SHA1";
|
|
17041
|
-
case "hostname":
|
|
17042
|
-
case "host":
|
|
17043
|
-
case "endpoint":
|
|
17044
|
-
return "Hostname";
|
|
17045
|
-
case "domain":
|
|
17046
|
-
return "Domain";
|
|
17047
|
-
case "url":
|
|
17048
|
-
return "URL";
|
|
17049
|
-
case "user":
|
|
17050
|
-
case "username":
|
|
17051
|
-
case "account":
|
|
17052
|
-
return "User";
|
|
17053
|
-
case "email":
|
|
17054
|
-
return "Email";
|
|
17055
|
-
case "process":
|
|
17056
|
-
case "process_name":
|
|
17057
|
-
return "Process";
|
|
17058
|
-
case "file":
|
|
17059
|
-
case "filename":
|
|
17060
|
-
return "File";
|
|
17061
|
-
default:
|
|
17062
|
-
return type.charAt(0).toUpperCase() + type.slice(1);
|
|
17063
|
-
}
|
|
17064
|
-
};
|
|
17065
|
-
var isIncidentInjectedPrompt = (prompt) => {
|
|
17066
|
-
if (!prompt || typeof prompt !== "string") return false;
|
|
17067
|
-
return prompt.includes(INCIDENT_PROMPT_DELIMITER_START) || prompt.includes("OPERATIONAL DIRECTIVES:") || prompt.includes("INCIDENT OBSERVABLES / IOCs:");
|
|
17068
|
-
};
|
|
17069
|
-
var extractCleanIncidentPrompt = (prompt) => {
|
|
17070
|
-
if (!prompt || typeof prompt !== "string") return "";
|
|
17071
|
-
const trimmed = prompt.trim();
|
|
17072
|
-
const taskMatch = trimmed.match(/TASK TO EXECUTE:\s*\n\s*-\s*Task:\s*([^\n]+)/i);
|
|
17073
|
-
if (taskMatch && taskMatch[1]?.trim()) {
|
|
17074
|
-
return `Task: ${taskMatch[1].trim()}`;
|
|
17075
|
-
}
|
|
17076
|
-
const legacyMatch = trimmed.match(/\n\s*Task:\s*([^\n]+)/i);
|
|
17077
|
-
if (legacyMatch && legacyMatch[1]?.trim()) {
|
|
17078
|
-
return `Task: ${legacyMatch[1].trim()}`;
|
|
17079
|
-
}
|
|
17080
|
-
if (trimmed.includes(INCIDENT_PROMPT_DELIMITER_START)) {
|
|
17081
|
-
const before = trimmed.split(INCIDENT_PROMPT_DELIMITER_START)[0]?.trim();
|
|
17082
|
-
if (before) return before;
|
|
17083
|
-
}
|
|
17084
|
-
return trimmed;
|
|
17085
|
-
};
|
|
17086
|
-
var composeIncidentPromptInput = (rawInput, context) => {
|
|
17087
|
-
if (!rawInput || typeof rawInput !== "string") return "";
|
|
17088
|
-
const trimmed = rawInput.trim();
|
|
17089
|
-
if (isIncidentInjectedPrompt(trimmed)) {
|
|
17090
|
-
return trimmed;
|
|
17091
|
-
}
|
|
17092
|
-
const ctx = context || getActiveIncidentPromptContext();
|
|
17093
|
-
if (!ctx || !ctx.id) {
|
|
17094
|
-
return trimmed;
|
|
17095
|
-
}
|
|
17096
|
-
const incidentRef = ctx.id ? `#${ctx.id}` : "";
|
|
17097
|
-
const incidentTitle = ctx.title?.trim() || "Incident";
|
|
17098
|
-
const severity = ctx.severity || "Medium";
|
|
17099
|
-
const status = ctx.status || "In Progress";
|
|
17100
|
-
const description = ctx.description?.trim() || "No description provided.";
|
|
17101
|
-
const seenObservables = /* @__PURE__ */ new Set();
|
|
17102
|
-
const formattedObservables = [];
|
|
17103
|
-
if (Array.isArray(ctx.observables)) {
|
|
17104
|
-
for (const obs of ctx.observables) {
|
|
17105
|
-
if (!obs || obs.archived || !obs.value?.trim()) continue;
|
|
17106
|
-
const val = obs.value.trim();
|
|
17107
|
-
const typeLabel = formatObservableType(obs.type);
|
|
17108
|
-
const key = `${typeLabel.toLowerCase()}:${val.toLowerCase()}`;
|
|
17109
|
-
if (seenObservables.has(key)) continue;
|
|
17110
|
-
seenObservables.add(key);
|
|
17111
|
-
formattedObservables.push(`- ${typeLabel}: ${val}`);
|
|
17112
|
-
}
|
|
17113
|
-
}
|
|
17114
|
-
const observablesBlock = formattedObservables.length > 0 ? formattedObservables.slice(0, 50).join("\n") : "- None recorded yet";
|
|
17115
|
-
return `${trimmed}
|
|
17116
|
-
|
|
17117
|
-
${INCIDENT_PROMPT_DELIMITER_START}
|
|
17118
|
-
INCIDENT CONTEXT:
|
|
17119
|
-
- Incident: ${incidentRef} ("${incidentTitle}")
|
|
17120
|
-
- Severity: ${severity} | Status: ${status}
|
|
17121
|
-
- Summary: ${description}
|
|
17122
|
-
|
|
17123
|
-
INCIDENT OBSERVABLES / IOCs:
|
|
17124
|
-
${observablesBlock}
|
|
17125
|
-
${INCIDENT_PROMPT_DELIMITER_END}
|
|
17126
|
-
|
|
17127
|
-
DIRECTIVES:
|
|
17128
|
-
- Relevant incident details and observables are provided above. Do not call "get_incident" unless raw external logs are required.`;
|
|
17129
|
-
};
|
|
17130
|
-
|
|
17131
|
-
// ../lib/usecasePromptContext.ts
|
|
17132
|
-
var GLOBAL_KEY = "__shuffleActiveUsecaseContext";
|
|
17133
|
-
var GLOBAL_OVERVIEW_KEY = "__shuffleActiveUsecasesOverviewContext";
|
|
17134
|
-
var USECASE_CONTEXT_EVENT = "shuffle-usecase-context-changed";
|
|
17135
|
-
var scope = () => typeof globalThis !== "undefined" ? globalThis : null;
|
|
17136
|
-
var getActiveUsecasePromptContext = () => {
|
|
17137
|
-
const v = scope()?.[GLOBAL_KEY];
|
|
17138
|
-
return v && typeof v === "object" ? v : null;
|
|
17139
|
-
};
|
|
17140
|
-
var getActiveUsecasesOverviewContext = () => {
|
|
17141
|
-
const v = scope()?.[GLOBAL_OVERVIEW_KEY];
|
|
17142
|
-
return v && typeof v === "object" ? v : null;
|
|
17143
|
-
};
|
|
17144
|
-
var humanizeUsecaseId = (id) => id.replace(/_\d+$/, "").replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()).trim();
|
|
17145
|
-
var getUsecaseDisplayName = (flowId) => {
|
|
17146
|
-
const ctx = getActiveUsecasePromptContext();
|
|
17147
|
-
if (ctx?.label) return ctx.label;
|
|
17148
|
-
return flowId ? humanizeUsecaseId(flowId) : "Usecases";
|
|
17149
|
-
};
|
|
17150
|
-
var USECASE_PROMPT_START = "=== USECASE CONTEXT ===";
|
|
17151
|
-
var USECASE_PROMPT_END = "=== END USECASE CONTEXT ===";
|
|
17152
|
-
var isUsecaseInjectedPrompt = (prompt) => {
|
|
17153
|
-
if (!prompt || typeof prompt !== "string") return false;
|
|
17154
|
-
return prompt.includes(USECASE_PROMPT_START);
|
|
17155
|
-
};
|
|
17156
|
-
var extractCleanUsecasePrompt = (prompt) => {
|
|
17157
|
-
if (!prompt || typeof prompt !== "string") return "";
|
|
17158
|
-
if (!isUsecaseInjectedPrompt(prompt)) return prompt;
|
|
17159
|
-
return prompt.replace(/\n*=== USECASE CONTEXT ===[\s\S]*?(?:=== END USECASE CONTEXT ===|$)/g, "").trim();
|
|
15565
|
+
var extractCleanUsecasePrompt = (prompt) => {
|
|
15566
|
+
if (!prompt || typeof prompt !== "string") return "";
|
|
15567
|
+
if (!isUsecaseInjectedPrompt(prompt)) return prompt;
|
|
15568
|
+
return prompt.replace(/\n*=== USECASE CONTEXT ===[\s\S]*?(?:=== END USECASE CONTEXT ===|$)/g, "").trim();
|
|
17160
15569
|
};
|
|
17161
15570
|
var USECASE_REQUIRED_APIS = [
|
|
17162
15571
|
"GET /api/v1/workflows/usecases (list all usecase definitions, steps, source/target categories, and workflow templates)",
|
|
@@ -17503,392 +15912,1984 @@ var DEFAULT_USECASE_CATALOG = [
|
|
|
17503
15912
|
automationLabel: "Block Threat Intel IOCs",
|
|
17504
15913
|
automationCategory: "intel",
|
|
17505
15914
|
automationArea: "Threat Intelligence (TIP)",
|
|
17506
|
-
summary: "Automatically feeds high-confidence Threat Intelligence indicators directly into network firewalls and perimeter blocklists."
|
|
15915
|
+
summary: "Automatically feeds high-confidence Threat Intelligence indicators directly into network firewalls and perimeter blocklists."
|
|
15916
|
+
},
|
|
15917
|
+
{
|
|
15918
|
+
id: "intel_to_edr_1",
|
|
15919
|
+
slug: "push_detections",
|
|
15920
|
+
label: "Push detections",
|
|
15921
|
+
phase: "Step 3: Agents & Response Actions",
|
|
15922
|
+
source: "threat_intel",
|
|
15923
|
+
target: "edr",
|
|
15924
|
+
automationLabel: "Push Threat Intel to EDR",
|
|
15925
|
+
automationCategory: "intel",
|
|
15926
|
+
automationArea: "Threat Intelligence (TIP)",
|
|
15927
|
+
summary: "Pushes file hashes and C2 indicators from Threat Intelligence feeds into EDR custom IOC prevention and hunting lists."
|
|
15928
|
+
}
|
|
15929
|
+
];
|
|
15930
|
+
var USECASE_TOOL_CATEGORIES = [
|
|
15931
|
+
"case_management (Cases / SOAR): Shuffle Security Incidents, TheHive, Jira, ServiceNow, PagerDuty, Linear",
|
|
15932
|
+
"siem (SIEM & Log Analytics): Wazuh, Splunk, Elastic Security, Microsoft Sentinel, QRadar, Google Chronicle, Graylog",
|
|
15933
|
+
"edr (EDR / Endpoint): CrowdStrike Falcon, SentinelOne, Microsoft Defender for Endpoint, Wazuh, Carbon Black, Velociraptor, Shuffle Host-Monitors",
|
|
15934
|
+
"email (Email Security): Microsoft Office 365, Google Workspace / Gmail, IMAP/SMTP, Proofpoint, Mimecast",
|
|
15935
|
+
"threat_intel (Threat Intelligence): MISP, OpenCTI, VirusTotal, AbuseIPDB, Shodan, GreyNoise, AlienVault OTX, URLhaus",
|
|
15936
|
+
"asset_management (Assets & Vulnerabilities): Shuffle Assets & Vulnerabilities, Tenable, Qualys, Rapid7, NetBox, Snipe-IT, ServiceNow CMDB",
|
|
15937
|
+
"iam (Identity & Access Management): Okta, Microsoft Entra ID (Azure AD), Active Directory LDAP, Google Workspace Admin, Duo, Keycloak",
|
|
15938
|
+
"network (Network & Firewalls): Palo Alto PAN-OS, Fortinet FortiGate, Cloudflare, Suricata, Zeek, Zscaler, Cisco Meraki/ASA",
|
|
15939
|
+
"cloud (Cloud Security): AWS (GuardDuty, CloudTrail, EC2, IAM), Azure Security Center, Google Cloud SCC, Wiz, Prisma Cloud",
|
|
15940
|
+
"communication (Notifications & On-Call): Slack, Microsoft Teams, Email, PagerDuty, Opsgenie, Discord, Shuffle Mobile App"
|
|
15941
|
+
];
|
|
15942
|
+
var USECASE_ARCHITECTURE_OVERVIEW = [
|
|
15943
|
+
"How Usecases Work in Shuffle Security:",
|
|
15944
|
+
" 1. Three-Phase Operational Pipeline:",
|
|
15945
|
+
" - 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`).",
|
|
15946
|
+
" - 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.",
|
|
15947
|
+
" - 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`).",
|
|
15948
|
+
" 2. Enabling & Configuring a Usecase:",
|
|
15949
|
+
" - 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`).",
|
|
15950
|
+
" - 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.",
|
|
15951
|
+
" - 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`).",
|
|
15952
|
+
" - Any external source can push JSON events directly into Shuffle Security via the tenant Ingestion Webhook (`POST /api/v1/hooks/webhook_<id>`).",
|
|
15953
|
+
" 3. How You (the Agent) Should Help the User:",
|
|
15954
|
+
" - Recommend which usecases to enable next based on the tenant's currently enabled usecases and connected/authenticated tools.",
|
|
15955
|
+
" - Explain step-by-step how to configure or troubleshoot any usecase, including required app credentials, webhook setup, or datastore triggers.",
|
|
15956
|
+
" - Inspect or generate workflows, check authentication status, and verify datastore automations using the Shuffle APIs below when requested."
|
|
15957
|
+
].join("\n");
|
|
15958
|
+
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");
|
|
15959
|
+
var findCatalogUsecase = (flowId) => {
|
|
15960
|
+
if (!flowId) return void 0;
|
|
15961
|
+
const norm4 = flowId.trim().toLowerCase().replace(/[\s-]+/g, "_");
|
|
15962
|
+
return DEFAULT_USECASE_CATALOG.find(
|
|
15963
|
+
(u) => u.id.toLowerCase() === norm4 || u.slug.toLowerCase() === norm4 || u.label.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "") === norm4
|
|
15964
|
+
);
|
|
15965
|
+
};
|
|
15966
|
+
var composeUsecasePromptInput = (rawInput, flowId) => {
|
|
15967
|
+
const trimmed = (rawInput || "").trim();
|
|
15968
|
+
if (!trimmed || trimmed.includes(USECASE_PROMPT_START)) return trimmed;
|
|
15969
|
+
const ctx = getActiveUsecasePromptContext();
|
|
15970
|
+
const overviewCtx = getActiveUsecasesOverviewContext();
|
|
15971
|
+
const catalogMatch = findCatalogUsecase(ctx?.id || flowId);
|
|
15972
|
+
const lines = [USECASE_PROMPT_START];
|
|
15973
|
+
if (ctx) {
|
|
15974
|
+
lines.push(`Active View: Specific Usecase Drawer (/usecases/${flowId || ctx.id})`);
|
|
15975
|
+
lines.push(`Usecase: ${ctx.label} (id: ${ctx.id})`);
|
|
15976
|
+
if (ctx.source || ctx.target || catalogMatch) {
|
|
15977
|
+
lines.push(`Data flow: ${ctx.source || catalogMatch?.source || "unknown"} -> ${ctx.target || catalogMatch?.target || "unknown"}`);
|
|
15978
|
+
}
|
|
15979
|
+
if (ctx.phase || catalogMatch?.phase) lines.push(`Phase: ${ctx.phase || catalogMatch?.phase}`);
|
|
15980
|
+
if (ctx.automationArea || catalogMatch?.automationArea) {
|
|
15981
|
+
lines.push(`Automation area: ${ctx.automationArea || catalogMatch?.automationArea}`);
|
|
15982
|
+
}
|
|
15983
|
+
if (ctx.tags?.length) lines.push(`Tags: ${ctx.tags.join(", ")}`);
|
|
15984
|
+
if (typeof ctx.enabled === "boolean") lines.push(`Status for this tenant: ${ctx.enabled ? "Enabled" : "Disabled"}`);
|
|
15985
|
+
if (ctx.healthStatus) lines.push(`Health / diagnostics: ${ctx.healthStatus}`);
|
|
15986
|
+
if (typeof ctx.manualVerification === "boolean") {
|
|
15987
|
+
lines.push(`Verification mode: ${ctx.manualVerification ? "Manual / UI-configured flow" : "Automated workflow generation via POST /api/v2/workflows/generate"}`);
|
|
15988
|
+
}
|
|
15989
|
+
if (ctx.customAction || catalogMatch?.customRoute) {
|
|
15990
|
+
const route = ctx.customAction?.href || ctx.customAction?.url || catalogMatch?.customRoute;
|
|
15991
|
+
const actionLabel = ctx.customAction?.label || "Open configuration view";
|
|
15992
|
+
lines.push(`Dedicated UI action: ${actionLabel}${route ? ` (${route})` : ""}${ctx.customAction?.description ? ` \u2014 ${ctx.customAction.description}` : ""}`);
|
|
15993
|
+
}
|
|
15994
|
+
if (ctx.automationLabel || catalogMatch?.automationLabel) {
|
|
15995
|
+
const autoCat = ctx.automationCategory || catalogMatch?.automationCategory;
|
|
15996
|
+
lines.push(`Automation label: ${ctx.automationLabel || catalogMatch?.automationLabel}${autoCat ? ` (category: ${autoCat})` : ""}`);
|
|
15997
|
+
}
|
|
15998
|
+
if (ctx.description || catalogMatch?.summary) {
|
|
15999
|
+
lines.push(`Description: ${ctx.description || catalogMatch?.summary}`);
|
|
16000
|
+
}
|
|
16001
|
+
if (ctx.agenticDescription) lines.push(`Agentic behavior: ${ctx.agenticDescription}`);
|
|
16002
|
+
const connected = list(ctx.connectedApps);
|
|
16003
|
+
if (connected) {
|
|
16004
|
+
lines.push(`Connected tools with validated authentication:
|
|
16005
|
+
${connected}`);
|
|
16006
|
+
} else {
|
|
16007
|
+
lines.push("Connected tools with validated authentication: None detected yet for this usecase category.");
|
|
16008
|
+
}
|
|
16009
|
+
const apps = list(ctx.categoryApps);
|
|
16010
|
+
if (apps) lines.push(`Supported apps per category:
|
|
16011
|
+
${apps}`);
|
|
16012
|
+
if (ctx.linkedWorkflows?.length) {
|
|
16013
|
+
lines.push(`Linked workflows: ${ctx.linkedWorkflows.join(", ")}`);
|
|
16014
|
+
} else {
|
|
16015
|
+
lines.push("Linked workflows: None linked yet.");
|
|
16016
|
+
}
|
|
16017
|
+
lines.push("");
|
|
16018
|
+
lines.push(USECASE_ARCHITECTURE_OVERVIEW);
|
|
16019
|
+
} else if (flowId) {
|
|
16020
|
+
lines.push(`Active View: Specific Usecase (/usecases/${flowId})`);
|
|
16021
|
+
if (catalogMatch) {
|
|
16022
|
+
lines.push(`Usecase: ${catalogMatch.label} (id: ${catalogMatch.id}, slug: ${catalogMatch.slug})`);
|
|
16023
|
+
lines.push(`Phase: ${catalogMatch.phase}`);
|
|
16024
|
+
lines.push(`Data flow: ${catalogMatch.source} -> ${catalogMatch.target}`);
|
|
16025
|
+
if (catalogMatch.automationArea) lines.push(`Automation area: ${catalogMatch.automationArea}`);
|
|
16026
|
+
if (catalogMatch.customRoute) lines.push(`Dedicated configuration route: ${catalogMatch.customRoute}`);
|
|
16027
|
+
lines.push(`Automation label: ${catalogMatch.automationLabel}${catalogMatch.automationCategory ? ` (category: ${catalogMatch.automationCategory})` : ""}`);
|
|
16028
|
+
lines.push(`Description: ${catalogMatch.summary}`);
|
|
16029
|
+
} else {
|
|
16030
|
+
lines.push(`Usecase id/slug: ${flowId} (${humanizeUsecaseId(flowId)})`);
|
|
16031
|
+
}
|
|
16032
|
+
lines.push("");
|
|
16033
|
+
lines.push(USECASE_ARCHITECTURE_OVERVIEW);
|
|
16034
|
+
} else {
|
|
16035
|
+
lines.push("Page: Usecases overview (/usecases \u2014 interactive 3-phase pipeline of all security automation usecases in Shuffle Security).");
|
|
16036
|
+
if (overviewCtx) {
|
|
16037
|
+
lines.push("");
|
|
16038
|
+
lines.push("Current Tenant Live State on /usecases:");
|
|
16039
|
+
if (overviewCtx.enabledUsecases?.length) {
|
|
16040
|
+
lines.push(` - Enabled usecases (${overviewCtx.enabledUsecases.length}): ${overviewCtx.enabledUsecases.map((u) => `${u.label} (${u.id}: ${u.source} -> ${u.target})`).join(", ")}`);
|
|
16041
|
+
} else {
|
|
16042
|
+
lines.push(" - Enabled usecases: None enabled yet.");
|
|
16043
|
+
}
|
|
16044
|
+
if (overviewCtx.disabledUsecases?.length) {
|
|
16045
|
+
lines.push(` - Disabled / available usecases (${overviewCtx.disabledUsecases.length}): ${overviewCtx.disabledUsecases.map((u) => `${u.label} (${u.id})`).join(", ")}`);
|
|
16046
|
+
}
|
|
16047
|
+
if (overviewCtx.validatedApps?.length) {
|
|
16048
|
+
lines.push(` - Connected & authenticated apps: ${overviewCtx.validatedApps.slice(0, 30).join(", ")}`);
|
|
16049
|
+
} else {
|
|
16050
|
+
lines.push(" - Connected & authenticated apps: None authenticated yet.");
|
|
16051
|
+
}
|
|
16052
|
+
if (overviewCtx.validatedCategories?.length) {
|
|
16053
|
+
lines.push(` - Authenticated tool categories: ${overviewCtx.validatedCategories.join(", ")}`);
|
|
16054
|
+
}
|
|
16055
|
+
if (overviewCtx.activeWorkflows?.length) {
|
|
16056
|
+
lines.push(` - Active tenant workflows (${overviewCtx.activeWorkflows.length}): ${overviewCtx.activeWorkflows.slice(0, 20).join(", ")}${overviewCtx.activeWorkflows.length > 20 ? ` (+${overviewCtx.activeWorkflows.length - 20} more)` : ""}`);
|
|
16057
|
+
}
|
|
16058
|
+
if (overviewCtx.activeFilters) {
|
|
16059
|
+
const f = overviewCtx.activeFilters;
|
|
16060
|
+
const activeFilterParts = [
|
|
16061
|
+
f.phase && f.phase !== "all" ? `phase=${f.phase}` : "",
|
|
16062
|
+
f.category && f.category !== "all" ? `category=${f.category}` : "",
|
|
16063
|
+
f.area && f.area !== "all" ? `area=${f.area}` : "",
|
|
16064
|
+
f.tag && f.tag !== "all" ? `tag=${f.tag}` : "",
|
|
16065
|
+
f.search ? `search="${f.search}"` : ""
|
|
16066
|
+
].filter(Boolean);
|
|
16067
|
+
if (activeFilterParts.length > 0) {
|
|
16068
|
+
lines.push(` - User's active UI filters: ${activeFilterParts.join(", ")}`);
|
|
16069
|
+
}
|
|
16070
|
+
}
|
|
16071
|
+
}
|
|
16072
|
+
lines.push("");
|
|
16073
|
+
lines.push(USECASE_ARCHITECTURE_OVERVIEW);
|
|
16074
|
+
lines.push("");
|
|
16075
|
+
lines.push(`Supported Tool Categories:
|
|
16076
|
+
${USECASE_TOOL_CATEGORIES.map((c) => ` - ${c}`).join("\n")}`);
|
|
16077
|
+
lines.push("");
|
|
16078
|
+
lines.push("Complete Shuffle Security Usecases Catalog (All 3 Phases):");
|
|
16079
|
+
for (const item of DEFAULT_USECASE_CATALOG) {
|
|
16080
|
+
lines.push(
|
|
16081
|
+
` - [${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}`
|
|
16082
|
+
);
|
|
16083
|
+
}
|
|
16084
|
+
}
|
|
16085
|
+
lines.push("");
|
|
16086
|
+
lines.push(`Required Shuffle APIs:
|
|
16087
|
+
${USECASE_REQUIRED_APIS.map((a) => ` - ${a}`).join("\n")}`);
|
|
16088
|
+
lines.push(USECASE_PROMPT_END);
|
|
16089
|
+
return `${trimmed}
|
|
16090
|
+
|
|
16091
|
+
${lines.join("\n")}`;
|
|
16092
|
+
};
|
|
16093
|
+
|
|
16094
|
+
// ../lib/docsPromptContext.ts
|
|
16095
|
+
var DOC_SELECTION_EVENT = "shuffle:doc-selection-change";
|
|
16096
|
+
var GLOBAL_DOC_SELECTION_KEY = "__shuffleActiveDocSelection";
|
|
16097
|
+
var truncateDocSelectionPreview = (text2, maxChars = 36) => {
|
|
16098
|
+
const clean = (text2 || "").replace(/\s+/g, " ").trim();
|
|
16099
|
+
if (!clean) return "";
|
|
16100
|
+
if (clean.length <= maxChars) return clean;
|
|
16101
|
+
return `${clean.slice(0, maxChars).trimEnd()}\u2026`;
|
|
16102
|
+
};
|
|
16103
|
+
var GLOBAL_DOC_KEY = "__shuffleActiveDocInfo";
|
|
16104
|
+
var getGlobalScope2 = () => {
|
|
16105
|
+
if (typeof globalThis !== "undefined") return globalThis;
|
|
16106
|
+
if (typeof window !== "undefined") return window;
|
|
16107
|
+
if (typeof global !== "undefined") return global;
|
|
16108
|
+
return null;
|
|
16109
|
+
};
|
|
16110
|
+
var setActiveDocSelectedText = (selectedText) => {
|
|
16111
|
+
const scope2 = getGlobalScope2();
|
|
16112
|
+
if (!scope2) return;
|
|
16113
|
+
const cleaned = (selectedText || "").trim();
|
|
16114
|
+
if (cleaned) {
|
|
16115
|
+
scope2[GLOBAL_DOC_SELECTION_KEY] = cleaned;
|
|
16116
|
+
if (scope2[GLOBAL_DOC_KEY] && typeof scope2[GLOBAL_DOC_KEY] === "object") {
|
|
16117
|
+
scope2[GLOBAL_DOC_KEY].selectedText = cleaned;
|
|
16118
|
+
}
|
|
16119
|
+
} else {
|
|
16120
|
+
delete scope2[GLOBAL_DOC_SELECTION_KEY];
|
|
16121
|
+
if (scope2[GLOBAL_DOC_KEY] && typeof scope2[GLOBAL_DOC_KEY] === "object") {
|
|
16122
|
+
delete scope2[GLOBAL_DOC_KEY].selectedText;
|
|
16123
|
+
}
|
|
16124
|
+
}
|
|
16125
|
+
if (typeof window !== "undefined") {
|
|
16126
|
+
try {
|
|
16127
|
+
window.dispatchEvent(
|
|
16128
|
+
new CustomEvent(DOC_SELECTION_EVENT, {
|
|
16129
|
+
detail: { selectedText: cleaned || null }
|
|
16130
|
+
})
|
|
16131
|
+
);
|
|
16132
|
+
} catch {
|
|
16133
|
+
}
|
|
16134
|
+
}
|
|
16135
|
+
};
|
|
16136
|
+
var getActiveDocSelectedText = () => {
|
|
16137
|
+
const scope2 = getGlobalScope2();
|
|
16138
|
+
if (!scope2) return null;
|
|
16139
|
+
const sel = scope2[GLOBAL_DOC_SELECTION_KEY] || scope2[GLOBAL_DOC_KEY]?.selectedText;
|
|
16140
|
+
return typeof sel === "string" && sel.trim().length > 0 ? sel.trim() : null;
|
|
16141
|
+
};
|
|
16142
|
+
var getActiveDocPromptContext = () => {
|
|
16143
|
+
const scope2 = getGlobalScope2();
|
|
16144
|
+
if (!scope2) return null;
|
|
16145
|
+
const info = scope2[GLOBAL_DOC_KEY];
|
|
16146
|
+
if (info && typeof info === "object") {
|
|
16147
|
+
return info;
|
|
16148
|
+
}
|
|
16149
|
+
return null;
|
|
16150
|
+
};
|
|
16151
|
+
var isDocsRoute = (pathname) => {
|
|
16152
|
+
if (!pathname) return false;
|
|
16153
|
+
return pathname === "/docs" || pathname.startsWith("/docs/") || pathname === "/legal" || pathname.startsWith("/legal/") || pathname === "/articles" || pathname.startsWith("/articles/");
|
|
16154
|
+
};
|
|
16155
|
+
var DOC_PROMPT_DELIMITER_START = "--- BEGIN DOCUMENTATION ---";
|
|
16156
|
+
var DOC_PROMPT_DELIMITER_END = "--- END DOCUMENTATION ---";
|
|
16157
|
+
var DOC_PROMPT_PREFIX_REGEX = /^Answer the users? question about (?:["']?)(.*?)(?:["']?) based on(?: on)? the following (?:documentation )?content:/i;
|
|
16158
|
+
var sanitizeDocMarkdown = (markdown, maxChars = 35e3) => {
|
|
16159
|
+
if (!markdown || typeof markdown !== "string") return "";
|
|
16160
|
+
let cleaned = markdown;
|
|
16161
|
+
cleaned = cleaned.replace(/^---\s*\r?\n[\s\S]*?\r?\n---\s*(?:\r?\n|$)/, "");
|
|
16162
|
+
cleaned = stripInContentToc(cleaned);
|
|
16163
|
+
cleaned = cleaned.replace(/<!--[\s\S]*?-->/g, "");
|
|
16164
|
+
cleaned = cleaned.replace(/<(script|style|iframe)[^>]*>[\s\S]*?<\/\1>/gi, "");
|
|
16165
|
+
cleaned = cleaned.replace(/data:image\/[a-zA-Z0-9+.-]+;base64,[A-Za-z0-9+/=]+/g, "[embedded image]");
|
|
16166
|
+
cleaned = cleaned.replace(/\n{3,}/g, "\n\n");
|
|
16167
|
+
cleaned = cleaned.trim();
|
|
16168
|
+
if (cleaned.length > maxChars) {
|
|
16169
|
+
const cut = cleaned.lastIndexOf("\n\n", maxChars);
|
|
16170
|
+
const splitIndex = cut > maxChars * 0.7 ? cut : maxChars;
|
|
16171
|
+
cleaned = `${cleaned.slice(0, splitIndex).trim()}
|
|
16172
|
+
|
|
16173
|
+
[Documentation content truncated for length...]`;
|
|
16174
|
+
}
|
|
16175
|
+
return cleaned;
|
|
16176
|
+
};
|
|
16177
|
+
var isDocInjectedPrompt = (prompt) => {
|
|
16178
|
+
if (!prompt || typeof prompt !== "string") return false;
|
|
16179
|
+
return prompt.includes(DOC_PROMPT_DELIMITER_START) || DOC_PROMPT_PREFIX_REGEX.test(prompt);
|
|
16180
|
+
};
|
|
16181
|
+
var stripSystemNotes = (text2) => text2.replace(/\s*\[System note:[^\]]*\]/g, "");
|
|
16182
|
+
var extractCleanDisplayPrompt = (prompt) => {
|
|
16183
|
+
if (!prompt || typeof prompt !== "string") return "";
|
|
16184
|
+
let trimmed = stripSystemNotes(prompt).trim();
|
|
16185
|
+
if (isUsecaseInjectedPrompt(trimmed)) {
|
|
16186
|
+
trimmed = extractCleanUsecasePrompt(trimmed);
|
|
16187
|
+
}
|
|
16188
|
+
if (isIncidentInjectedPrompt(trimmed)) {
|
|
16189
|
+
return extractCleanIncidentPrompt(trimmed);
|
|
16190
|
+
}
|
|
16191
|
+
if (!isDocInjectedPrompt(trimmed)) return trimmed;
|
|
16192
|
+
if (trimmed.includes(DOC_PROMPT_DELIMITER_END)) {
|
|
16193
|
+
const parts = trimmed.split(DOC_PROMPT_DELIMITER_END);
|
|
16194
|
+
const after = parts[parts.length - 1]?.trim() || "";
|
|
16195
|
+
const cleaned = after.replace(/^(?:User\s+Question:\s*|Question:\s*)/i, "").trim();
|
|
16196
|
+
if (cleaned) return cleaned;
|
|
16197
|
+
}
|
|
16198
|
+
if (DOC_PROMPT_PREFIX_REGEX.test(trimmed)) {
|
|
16199
|
+
const paragraphs = trimmed.split(/\n\s*\n/).filter(Boolean);
|
|
16200
|
+
if (paragraphs.length > 1) {
|
|
16201
|
+
const last = paragraphs[paragraphs.length - 1].trim();
|
|
16202
|
+
const cleaned = last.replace(/^(?:User\s+Question:\s*|Question:\s*)/i, "").trim();
|
|
16203
|
+
if (cleaned) return cleaned;
|
|
16204
|
+
}
|
|
16205
|
+
}
|
|
16206
|
+
return trimmed;
|
|
16207
|
+
};
|
|
16208
|
+
var normalizeDocPromptTitle = (title) => {
|
|
16209
|
+
const t = (title || "").trim();
|
|
16210
|
+
if (!t || t.toLowerCase() === "index" || t.toLowerCase() === "documentation" || t.toLowerCase() === "docs") {
|
|
16211
|
+
return "Shuffle documentation";
|
|
16212
|
+
}
|
|
16213
|
+
const lower = t.toLowerCase();
|
|
16214
|
+
if (lower === "about" || lower === "about shuffle" || lower === "about us" || lower === "about-shuffle" || lower === "about-us") {
|
|
16215
|
+
return "Shuffle";
|
|
16216
|
+
}
|
|
16217
|
+
if (/^about\s+/i.test(t)) {
|
|
16218
|
+
const stripped = t.replace(/^about\s+/i, "").trim();
|
|
16219
|
+
return stripped || "Shuffle";
|
|
16220
|
+
}
|
|
16221
|
+
return t;
|
|
16222
|
+
};
|
|
16223
|
+
var composeDocPromptInput = (userQuestion, pathname, fallbackTitle, overrideDocInfo) => {
|
|
16224
|
+
const trimmedQuestion = (userQuestion || "").trim();
|
|
16225
|
+
if (!trimmedQuestion) return "";
|
|
16226
|
+
if (isDocInjectedPrompt(trimmedQuestion)) {
|
|
16227
|
+
return trimmedQuestion;
|
|
16228
|
+
}
|
|
16229
|
+
const activeDoc = overrideDocInfo ?? getActiveDocPromptContext();
|
|
16230
|
+
const rawTitle = (activeDoc?.title || fallbackTitle || getGlobalScope2()?.__shuffleActiveEntityTitle || "Shuffle documentation").trim();
|
|
16231
|
+
const title = normalizeDocPromptTitle(rawTitle);
|
|
16232
|
+
const groupLabel = activeDoc?.groupLabel;
|
|
16233
|
+
const groupId = activeDoc?.groupId;
|
|
16234
|
+
const currentSlug = (activeDoc?.slug || "").toLowerCase().replace(/_+/g, "-");
|
|
16235
|
+
let categoryDocs = activeDoc?.groupDocs || [];
|
|
16236
|
+
if (categoryDocs.length === 0 && groupId) {
|
|
16237
|
+
categoryDocs = getCachedGroupDocs(groupId);
|
|
16238
|
+
}
|
|
16239
|
+
if (categoryDocs.length === 0 && currentSlug) {
|
|
16240
|
+
const groupDef2 = getDocGroup(currentSlug);
|
|
16241
|
+
if (groupDef2) {
|
|
16242
|
+
categoryDocs = getCachedGroupDocs(groupDef2.id);
|
|
16243
|
+
}
|
|
16244
|
+
}
|
|
16245
|
+
const groupDef = currentSlug ? getDocGroup(currentSlug) : null;
|
|
16246
|
+
const categoryName = groupLabel || groupDef?.label || title;
|
|
16247
|
+
const rawMarkdown = activeDoc?.content || "";
|
|
16248
|
+
const renderedDocs = [];
|
|
16249
|
+
if (categoryDocs.length > 0) {
|
|
16250
|
+
for (const doc of categoryDocs) {
|
|
16251
|
+
const docSlugClean = (doc.slug || "").toLowerCase().replace(/_+/g, "-");
|
|
16252
|
+
const isCurrent = docSlugClean === currentSlug;
|
|
16253
|
+
const contentToUse = isCurrent && rawMarkdown ? rawMarkdown : doc.content;
|
|
16254
|
+
renderedDocs.push({
|
|
16255
|
+
title: doc.title || title,
|
|
16256
|
+
slug: doc.slug,
|
|
16257
|
+
content: sanitizeDocMarkdown(contentToUse, 16e3),
|
|
16258
|
+
isCurrent
|
|
16259
|
+
});
|
|
16260
|
+
}
|
|
16261
|
+
} else if (rawMarkdown) {
|
|
16262
|
+
renderedDocs.push({
|
|
16263
|
+
title,
|
|
16264
|
+
slug: activeDoc?.slug,
|
|
16265
|
+
content: sanitizeDocMarkdown(rawMarkdown, 3e4),
|
|
16266
|
+
isCurrent: true
|
|
16267
|
+
});
|
|
16268
|
+
}
|
|
16269
|
+
let docSection = "";
|
|
16270
|
+
if (renderedDocs.length > 0) {
|
|
16271
|
+
const docBlocks = renderedDocs.filter((d) => d.content && d.content.trim().length > 0).map((d) => {
|
|
16272
|
+
const marker = d.isCurrent ? " (Currently Viewed)" : "";
|
|
16273
|
+
return `## Document: ${d.title}${marker}
|
|
16274
|
+
|
|
16275
|
+
${d.content}`;
|
|
16276
|
+
}).join("\n\n---\n\n");
|
|
16277
|
+
docSection = `# Documentation Category: ${categoryName}
|
|
16278
|
+
|
|
16279
|
+
${docBlocks}`;
|
|
16280
|
+
}
|
|
16281
|
+
const markedExcerpt = (activeDoc?.selectedText || getActiveDocSelectedText() || "").trim();
|
|
16282
|
+
const excerptBlock = markedExcerpt ? `
|
|
16283
|
+
|
|
16284
|
+
## User-Marked Excerpt (Focus of Question)
|
|
16285
|
+
The user highlighted the following text in "${title}":
|
|
16286
|
+
"""
|
|
16287
|
+
${markedExcerpt.slice(0, 4e3)}
|
|
16288
|
+
"""` : "";
|
|
16289
|
+
if (docSection) {
|
|
16290
|
+
return `Answer the users question about Shuffle ${categoryName} based on the following complete documentation for the ${categoryName} category:
|
|
16291
|
+
${DOC_PROMPT_DELIMITER_START}
|
|
16292
|
+
${docSection}${excerptBlock}
|
|
16293
|
+
${DOC_PROMPT_DELIMITER_END}
|
|
16294
|
+
|
|
16295
|
+
User Question:
|
|
16296
|
+
${trimmedQuestion}`;
|
|
16297
|
+
}
|
|
16298
|
+
return `Answer the users question about Shuffle ${categoryName}:${excerptBlock}
|
|
16299
|
+
|
|
16300
|
+
${trimmedQuestion}`;
|
|
16301
|
+
};
|
|
16302
|
+
|
|
16303
|
+
// components/AgentActivityList.tsx
|
|
16304
|
+
var import_lucide_react8 = require("lucide-react");
|
|
16305
|
+
|
|
16306
|
+
// ../Shuffle-Core/components/SegmentedControl.tsx
|
|
16307
|
+
var React6 = __toESM(require("react"));
|
|
16308
|
+
var import_framer_motion6 = require("framer-motion");
|
|
16309
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
16310
|
+
var SIZE = {
|
|
16311
|
+
sm: {
|
|
16312
|
+
containerPadding: "2px",
|
|
16313
|
+
fontSize: "11px",
|
|
16314
|
+
itemPaddingY: "4px",
|
|
16315
|
+
itemPaddingX: "10px",
|
|
16316
|
+
itemGap: "6px",
|
|
16317
|
+
countMin: "16px",
|
|
16318
|
+
countH: "16px",
|
|
16319
|
+
countPx: "4px",
|
|
16320
|
+
countFs: "10px",
|
|
16321
|
+
dividerH: "16px",
|
|
16322
|
+
dividerMx: "2px"
|
|
17507
16323
|
},
|
|
17508
|
-
{
|
|
17509
|
-
|
|
17510
|
-
|
|
17511
|
-
|
|
17512
|
-
|
|
17513
|
-
|
|
17514
|
-
|
|
17515
|
-
|
|
17516
|
-
|
|
17517
|
-
|
|
17518
|
-
|
|
16324
|
+
md: {
|
|
16325
|
+
containerPadding: "4px",
|
|
16326
|
+
fontSize: "12px",
|
|
16327
|
+
itemPaddingY: "6px",
|
|
16328
|
+
itemPaddingX: "12px",
|
|
16329
|
+
itemGap: "6px",
|
|
16330
|
+
countMin: "18px",
|
|
16331
|
+
countH: "18px",
|
|
16332
|
+
countPx: "6px",
|
|
16333
|
+
countFs: "10px",
|
|
16334
|
+
dividerH: "20px",
|
|
16335
|
+
dividerMx: "4px"
|
|
16336
|
+
}
|
|
16337
|
+
};
|
|
16338
|
+
var uid = 0;
|
|
16339
|
+
function useUniqueId(prefix) {
|
|
16340
|
+
const ref = React6.useRef(void 0);
|
|
16341
|
+
if (!ref.current) {
|
|
16342
|
+
uid += 1;
|
|
16343
|
+
ref.current = `${prefix}-${uid}`;
|
|
16344
|
+
}
|
|
16345
|
+
return ref.current;
|
|
16346
|
+
}
|
|
16347
|
+
function SegmentedControl({
|
|
16348
|
+
options,
|
|
16349
|
+
value,
|
|
16350
|
+
onChange,
|
|
16351
|
+
size = "md",
|
|
16352
|
+
variant = "outline",
|
|
16353
|
+
className,
|
|
16354
|
+
ariaLabel,
|
|
16355
|
+
layoutId,
|
|
16356
|
+
trailing
|
|
16357
|
+
}) {
|
|
16358
|
+
const autoId = useUniqueId("segmented");
|
|
16359
|
+
const groupId = layoutId ?? autoId;
|
|
16360
|
+
const s = SIZE[size];
|
|
16361
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_framer_motion6.LayoutGroup, { id: groupId, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
16362
|
+
"div",
|
|
16363
|
+
{
|
|
16364
|
+
role: "tablist",
|
|
16365
|
+
"aria-label": ariaLabel,
|
|
16366
|
+
className,
|
|
16367
|
+
style: {
|
|
16368
|
+
display: "inline-flex",
|
|
16369
|
+
alignItems: "center",
|
|
16370
|
+
gap: "2px",
|
|
16371
|
+
borderRadius: 9999,
|
|
16372
|
+
padding: s.containerPadding,
|
|
16373
|
+
fontSize: s.fontSize,
|
|
16374
|
+
border: variant === "outline" ? "1px solid hsl(var(--border))" : "1px solid transparent",
|
|
16375
|
+
background: variant === "outline" ? "transparent" : "hsl(var(--muted) / 0.4)"
|
|
16376
|
+
},
|
|
16377
|
+
children: [
|
|
16378
|
+
options.map((opt, i) => {
|
|
16379
|
+
if (opt.type === "divider") {
|
|
16380
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
16381
|
+
"span",
|
|
16382
|
+
{
|
|
16383
|
+
"aria-hidden": true,
|
|
16384
|
+
style: {
|
|
16385
|
+
width: 1,
|
|
16386
|
+
height: s.dividerH,
|
|
16387
|
+
margin: `0 ${s.dividerMx}`,
|
|
16388
|
+
background: "hsl(var(--border))",
|
|
16389
|
+
alignSelf: "center"
|
|
16390
|
+
}
|
|
16391
|
+
},
|
|
16392
|
+
opt.key ?? `divider-${i}`
|
|
16393
|
+
);
|
|
16394
|
+
}
|
|
16395
|
+
const isAction2 = opt.type === "action";
|
|
16396
|
+
const active2 = !isAction2 && opt.value === value;
|
|
16397
|
+
const key = isAction2 ? opt.key : opt.value;
|
|
16398
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
16399
|
+
"button",
|
|
16400
|
+
{
|
|
16401
|
+
type: "button",
|
|
16402
|
+
"data-tour": opt.dataTour,
|
|
16403
|
+
"data-active": active2 ? "true" : "false",
|
|
16404
|
+
role: isAction2 ? "button" : "tab",
|
|
16405
|
+
"aria-selected": isAction2 ? void 0 : active2,
|
|
16406
|
+
title: opt.title,
|
|
16407
|
+
disabled: opt.disabled,
|
|
16408
|
+
onClick: () => {
|
|
16409
|
+
if (opt.disabled) return;
|
|
16410
|
+
if (isAction2) opt.onClick();
|
|
16411
|
+
else onChange(opt.value);
|
|
16412
|
+
},
|
|
16413
|
+
style: {
|
|
16414
|
+
position: "relative",
|
|
16415
|
+
display: "inline-flex",
|
|
16416
|
+
alignItems: "center",
|
|
16417
|
+
gap: s.itemGap,
|
|
16418
|
+
padding: `${s.itemPaddingY} ${s.itemPaddingX}`,
|
|
16419
|
+
borderRadius: 9999,
|
|
16420
|
+
fontWeight: 500,
|
|
16421
|
+
fontSize: s.fontSize,
|
|
16422
|
+
background: "transparent",
|
|
16423
|
+
border: "none",
|
|
16424
|
+
cursor: opt.disabled ? "not-allowed" : "pointer",
|
|
16425
|
+
color: active2 ? "hsl(var(--foreground))" : "hsl(var(--muted-foreground))",
|
|
16426
|
+
opacity: opt.disabled ? 0.5 : 1,
|
|
16427
|
+
transition: "color 200ms ease",
|
|
16428
|
+
outline: "none",
|
|
16429
|
+
fontFamily: "inherit"
|
|
16430
|
+
},
|
|
16431
|
+
onMouseEnter: (e) => {
|
|
16432
|
+
if (!active2 && !opt.disabled) {
|
|
16433
|
+
e.currentTarget.style.color = "hsl(var(--foreground))";
|
|
16434
|
+
}
|
|
16435
|
+
},
|
|
16436
|
+
onMouseLeave: (e) => {
|
|
16437
|
+
if (!active2 && !opt.disabled) {
|
|
16438
|
+
e.currentTarget.style.color = "hsl(var(--muted-foreground))";
|
|
16439
|
+
}
|
|
16440
|
+
},
|
|
16441
|
+
children: [
|
|
16442
|
+
active2 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
16443
|
+
import_framer_motion6.motion.span,
|
|
16444
|
+
{
|
|
16445
|
+
layoutId: `${groupId}-pill`,
|
|
16446
|
+
style: {
|
|
16447
|
+
position: "absolute",
|
|
16448
|
+
inset: 0,
|
|
16449
|
+
borderRadius: 9999,
|
|
16450
|
+
background: "hsl(var(--muted))",
|
|
16451
|
+
border: "1px solid hsl(var(--border))"
|
|
16452
|
+
},
|
|
16453
|
+
initial: false,
|
|
16454
|
+
transition: {
|
|
16455
|
+
type: "spring",
|
|
16456
|
+
stiffness: 260,
|
|
16457
|
+
damping: 30
|
|
16458
|
+
}
|
|
16459
|
+
}
|
|
16460
|
+
),
|
|
16461
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
16462
|
+
"span",
|
|
16463
|
+
{
|
|
16464
|
+
style: {
|
|
16465
|
+
position: "relative",
|
|
16466
|
+
zIndex: 1,
|
|
16467
|
+
display: "inline-flex",
|
|
16468
|
+
alignItems: "center",
|
|
16469
|
+
gap: s.itemGap
|
|
16470
|
+
},
|
|
16471
|
+
children: opt.label
|
|
16472
|
+
}
|
|
16473
|
+
),
|
|
16474
|
+
!isAction2 && (typeof opt.count === "number" || typeof opt.count === "string") && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
16475
|
+
"span",
|
|
16476
|
+
{
|
|
16477
|
+
style: {
|
|
16478
|
+
position: "relative",
|
|
16479
|
+
zIndex: 1,
|
|
16480
|
+
display: "inline-flex",
|
|
16481
|
+
alignItems: "center",
|
|
16482
|
+
justifyContent: "center",
|
|
16483
|
+
minWidth: s.countMin,
|
|
16484
|
+
height: s.countH,
|
|
16485
|
+
padding: `0 ${s.countPx}`,
|
|
16486
|
+
fontSize: s.countFs,
|
|
16487
|
+
fontWeight: 600,
|
|
16488
|
+
fontVariantNumeric: "tabular-nums",
|
|
16489
|
+
borderRadius: 6,
|
|
16490
|
+
border: "1px solid hsl(var(--border) / 0.6)",
|
|
16491
|
+
color: active2 ? "hsl(var(--foreground))" : "hsl(var(--muted-foreground))",
|
|
16492
|
+
background: "transparent"
|
|
16493
|
+
},
|
|
16494
|
+
children: opt.count
|
|
16495
|
+
}
|
|
16496
|
+
)
|
|
16497
|
+
]
|
|
16498
|
+
},
|
|
16499
|
+
key
|
|
16500
|
+
);
|
|
16501
|
+
}),
|
|
16502
|
+
trailing != null && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
16503
|
+
"span",
|
|
16504
|
+
{
|
|
16505
|
+
style: {
|
|
16506
|
+
display: "inline-flex",
|
|
16507
|
+
alignItems: "center",
|
|
16508
|
+
alignSelf: "center"
|
|
16509
|
+
},
|
|
16510
|
+
children: trailing
|
|
16511
|
+
}
|
|
16512
|
+
)
|
|
16513
|
+
]
|
|
16514
|
+
}
|
|
16515
|
+
) });
|
|
16516
|
+
}
|
|
16517
|
+
|
|
16518
|
+
// components/AgentActivityList.tsx
|
|
16519
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
16520
|
+
var STATUS_CONFIG = {
|
|
16521
|
+
FINISHED: { icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.CheckCircle, { size: 16 }), color: "hsl(var(--severity-low, 142 71% 45%))", label: "Completed" },
|
|
16522
|
+
SUCCESS: { icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.CheckCircle, { size: 16 }), color: "hsl(var(--severity-low, 142 71% 45%))", label: "Completed" },
|
|
16523
|
+
FAILED: { icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.XCircle, { size: 16 }), color: "hsl(var(--severity-critical, 0 72% 55%))", label: "Failed" },
|
|
16524
|
+
ABORTED: { icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.XCircle, { size: 16 }), color: "hsl(var(--severity-critical, 0 72% 55%))", label: "Aborted" },
|
|
16525
|
+
EXECUTING: { icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Loader2, { size: 16 }), color: "hsl(var(--severity-medium, 38 92% 50%))", label: "Running" },
|
|
16526
|
+
RUNNING: { icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Loader2, { size: 16 }), color: "hsl(var(--severity-medium, 38 92% 50%))", label: "Running" },
|
|
16527
|
+
WAITING: { icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Clock, { size: 16 }), color: "hsl(var(--severity-info, 217 91% 60%))", label: "Waiting" },
|
|
16528
|
+
LIMIT_REACHED: { icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react8.AlertTriangle, { size: 16 }), color: "hsl(var(--severity-critical, 0 72% 55%))", label: "Limit reached" }
|
|
16529
|
+
};
|
|
16530
|
+
var getEffectiveStatus = (run2) => {
|
|
16531
|
+
const raw = (run2.status || "").toUpperCase();
|
|
16532
|
+
if (raw === "FINISHED" || raw === "SUCCESS" || raw === "FAILED" || raw === "ABORTED") {
|
|
16533
|
+
try {
|
|
16534
|
+
const d = diagnoseOutputWarning(run2);
|
|
16535
|
+
if (d?.kind === "token_limit") return "LIMIT_REACHED";
|
|
16536
|
+
} catch {
|
|
16537
|
+
}
|
|
16538
|
+
}
|
|
16539
|
+
return raw;
|
|
16540
|
+
};
|
|
16541
|
+
var SOURCE_ICON_SIZE = 18;
|
|
16542
|
+
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());
|
|
16543
|
+
var DATASTORE_KEY_RE = /\bKey:\s*(\S+)/i;
|
|
16544
|
+
var DATASTORE_CATEGORY_RE = /\bCategory:\s*(\S+)/i;
|
|
16545
|
+
var parseDatastoreTask = (arg) => {
|
|
16546
|
+
if (!arg) return null;
|
|
16547
|
+
const k = arg.match(DATASTORE_KEY_RE);
|
|
16548
|
+
const c = arg.match(DATASTORE_CATEGORY_RE);
|
|
16549
|
+
if (!k || !c) return null;
|
|
16550
|
+
const key = (k[1] || "").trim();
|
|
16551
|
+
const category = (c[1] || "").trim();
|
|
16552
|
+
if (!key || !category) return null;
|
|
16553
|
+
return { key, category };
|
|
16554
|
+
};
|
|
16555
|
+
var isAIAgentResult = (result) => String(result?.action?.app_name || "").trim().toLowerCase() === "ai agent";
|
|
16556
|
+
var getAIAgentResultPayload = (run2) => {
|
|
16557
|
+
const results = Array.isArray(run2.results) ? run2.results : [];
|
|
16558
|
+
const agentResult = results.find(isAIAgentResult);
|
|
16559
|
+
if (!agentResult || typeof agentResult !== "object" || !("result" in agentResult)) return null;
|
|
16560
|
+
return tryParseJson(agentResult.result);
|
|
16561
|
+
};
|
|
16562
|
+
var getDirectAgentInput = (payload) => {
|
|
16563
|
+
if (!payload || typeof payload !== "object") return null;
|
|
16564
|
+
const obj = payload;
|
|
16565
|
+
if (typeof obj.original_input === "string" && obj.original_input.trim()) {
|
|
16566
|
+
return obj.original_input.trim();
|
|
16567
|
+
}
|
|
16568
|
+
if (typeof obj.input === "string" && obj.input.trim()) {
|
|
16569
|
+
return obj.input.trim();
|
|
16570
|
+
}
|
|
16571
|
+
return null;
|
|
16572
|
+
};
|
|
16573
|
+
var findDatastoreTaskInRun = (run2) => {
|
|
16574
|
+
const agentInput = getDirectAgentInput(getAIAgentResultPayload(run2));
|
|
16575
|
+
const agentHit = parseDatastoreTask(agentInput || void 0);
|
|
16576
|
+
if (agentHit) return agentHit;
|
|
16577
|
+
const anyRun = run2;
|
|
16578
|
+
const candidates = [];
|
|
16579
|
+
if (typeof anyRun.original_input === "string") candidates.push(anyRun.original_input);
|
|
16580
|
+
if (typeof anyRun.input === "string") candidates.push(anyRun.input);
|
|
16581
|
+
if (run2.execution_argument) candidates.push(run2.execution_argument);
|
|
16582
|
+
if (run2.result) candidates.push(run2.result);
|
|
16583
|
+
if (Array.isArray(anyRun.results)) {
|
|
16584
|
+
try {
|
|
16585
|
+
candidates.push(JSON.stringify(anyRun.results));
|
|
16586
|
+
} catch {
|
|
16587
|
+
}
|
|
16588
|
+
}
|
|
16589
|
+
for (const c of candidates) {
|
|
16590
|
+
const hit = parseDatastoreTask(c);
|
|
16591
|
+
if (hit) return hit;
|
|
16592
|
+
}
|
|
16593
|
+
return null;
|
|
16594
|
+
};
|
|
16595
|
+
var classifyRunSource = (run2) => {
|
|
16596
|
+
const raw = (run2.execution_source || "").trim();
|
|
16597
|
+
const src = raw.toLowerCase();
|
|
16598
|
+
const sourceParts = raw.split("|");
|
|
16599
|
+
const fromSource = src.startsWith("datastore") && sourceParts.length > 2 ? { category: sourceParts[1].trim(), key: sourceParts[2].trim() } : null;
|
|
16600
|
+
const ds = findDatastoreTaskInRun(run2) || fromSource;
|
|
16601
|
+
if (ds || src.includes("datastore") || src.includes("enrichment") || src.includes("automation")) {
|
|
16602
|
+
return {
|
|
16603
|
+
kind: "datastore",
|
|
16604
|
+
label: "Datastore automation",
|
|
16605
|
+
reason: ds ? `Category: ${ds.category}
|
|
16606
|
+
Key: ${ds.key}
|
|
16607
|
+
Click to open it in the Datastore.` : "Started by a datastore automation (e.g. enrichment trigger).",
|
|
16608
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Database, { size: SOURCE_ICON_SIZE }),
|
|
16609
|
+
datastoreKey: ds?.key,
|
|
16610
|
+
datastoreCategory: ds?.category
|
|
16611
|
+
};
|
|
16612
|
+
}
|
|
16613
|
+
if (src.includes("schedule") || src.includes("cron")) {
|
|
16614
|
+
return {
|
|
16615
|
+
kind: "schedule",
|
|
16616
|
+
label: "Schedule",
|
|
16617
|
+
reason: "Started by a scheduled trigger (cron).",
|
|
16618
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Clock, { size: SOURCE_ICON_SIZE })
|
|
16619
|
+
};
|
|
16620
|
+
}
|
|
16621
|
+
if (src.includes("webhook") || src.includes("http")) {
|
|
16622
|
+
return {
|
|
16623
|
+
kind: "webhook",
|
|
16624
|
+
label: "Webhook",
|
|
16625
|
+
reason: "Started by an incoming webhook / HTTP trigger.",
|
|
16626
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Globe, { size: SOURCE_ICON_SIZE })
|
|
16627
|
+
};
|
|
16628
|
+
}
|
|
16629
|
+
if (src.includes("form")) {
|
|
16630
|
+
return {
|
|
16631
|
+
kind: "form",
|
|
16632
|
+
label: "Form",
|
|
16633
|
+
reason: "Started by a form submission.",
|
|
16634
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.FileText, { size: SOURCE_ICON_SIZE })
|
|
16635
|
+
};
|
|
16636
|
+
}
|
|
16637
|
+
if (raw && looksLikeExecutionId(raw)) {
|
|
16638
|
+
return {
|
|
16639
|
+
kind: "workflow",
|
|
16640
|
+
label: "Workflow",
|
|
16641
|
+
reason: "Started by another workflow as a subflow.",
|
|
16642
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Workflow, { size: SOURCE_ICON_SIZE })
|
|
16643
|
+
};
|
|
16644
|
+
}
|
|
16645
|
+
if (src === "" || src === "manual" || src === "default" || src === "demo") {
|
|
16646
|
+
return {
|
|
16647
|
+
kind: "manual",
|
|
16648
|
+
label: "Manual",
|
|
16649
|
+
reason: "Started manually from the Agents UI.",
|
|
16650
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Hand, { size: SOURCE_ICON_SIZE })
|
|
16651
|
+
};
|
|
16652
|
+
}
|
|
16653
|
+
return {
|
|
16654
|
+
kind: "unknown",
|
|
16655
|
+
label: raw || "Unknown",
|
|
16656
|
+
reason: raw ? `Started by "${raw}".` : "Trigger source unknown.",
|
|
16657
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Zap, { size: SOURCE_ICON_SIZE })
|
|
16658
|
+
};
|
|
16659
|
+
};
|
|
16660
|
+
var getRunIconColor = (run2) => {
|
|
16661
|
+
const status = getEffectiveStatus(run2);
|
|
16662
|
+
if (status === "FINISHED" || status === "SUCCESS") return "hsl(var(--severity-low, 142 71% 45%))";
|
|
16663
|
+
if (status === "FAILED" || status === "ABORTED") return "hsl(var(--severity-critical, 0 72% 55%))";
|
|
16664
|
+
if (status === "LIMIT_REACHED") return "hsl(var(--severity-critical, 0 72% 55%))";
|
|
16665
|
+
if (status === "EXECUTING" || status === "RUNNING") return "hsl(var(--severity-medium, 38 92% 50%))";
|
|
16666
|
+
return "hsl(var(--primary, 24 100% 50%))";
|
|
16667
|
+
};
|
|
16668
|
+
var formatDuration = (run2) => {
|
|
16669
|
+
if (run2.started_at && run2.completed_at) {
|
|
16670
|
+
const start = Number(run2.started_at);
|
|
16671
|
+
const end = Number(run2.completed_at);
|
|
16672
|
+
if (!isNaN(start) && !isNaN(end)) {
|
|
16673
|
+
const toMs = (n) => n > 1e12 ? n : n * 1e3;
|
|
16674
|
+
const ms = Math.max(0, toMs(end) - toMs(start));
|
|
16675
|
+
if (ms < 1e3) return `${(ms / 1e3).toFixed(2)}s`;
|
|
16676
|
+
if (ms < 6e4) return `${(ms / 1e3).toFixed(2)}s`;
|
|
16677
|
+
return `${Math.floor(ms / 6e4)}m ${Math.floor(ms % 6e4 / 1e3)}s`;
|
|
16678
|
+
}
|
|
16679
|
+
}
|
|
16680
|
+
if (run2.duration) return `${run2.duration.toFixed(2)}s`;
|
|
16681
|
+
return "";
|
|
16682
|
+
};
|
|
16683
|
+
var getTimeAgo = (dateStr) => {
|
|
16684
|
+
try {
|
|
16685
|
+
const ts = isNaN(Number(dateStr)) ? new Date(dateStr).getTime() : Number(dateStr) * 1e3;
|
|
16686
|
+
if (isNaN(ts)) return dateStr;
|
|
16687
|
+
const diffSec = Math.max(0, Math.floor((Date.now() - ts) / 1e3));
|
|
16688
|
+
if (diffSec < 60) return `${diffSec}s ago`;
|
|
16689
|
+
if (diffSec < 3600) return `${Math.floor(diffSec / 60)}m ago`;
|
|
16690
|
+
if (diffSec < 86400) return `${Math.floor(diffSec / 3600)}h ago`;
|
|
16691
|
+
return `${Math.floor(diffSec / 86400)}d ago`;
|
|
16692
|
+
} catch {
|
|
16693
|
+
return dateStr;
|
|
17519
16694
|
}
|
|
17520
|
-
];
|
|
17521
|
-
var USECASE_TOOL_CATEGORIES = [
|
|
17522
|
-
"case_management (Cases / SOAR): Shuffle Security Incidents, TheHive, Jira, ServiceNow, PagerDuty, Linear",
|
|
17523
|
-
"siem (SIEM & Log Analytics): Wazuh, Splunk, Elastic Security, Microsoft Sentinel, QRadar, Google Chronicle, Graylog",
|
|
17524
|
-
"edr (EDR / Endpoint): CrowdStrike Falcon, SentinelOne, Microsoft Defender for Endpoint, Wazuh, Carbon Black, Velociraptor, Shuffle Host-Monitors",
|
|
17525
|
-
"email (Email Security): Microsoft Office 365, Google Workspace / Gmail, IMAP/SMTP, Proofpoint, Mimecast",
|
|
17526
|
-
"threat_intel (Threat Intelligence): MISP, OpenCTI, VirusTotal, AbuseIPDB, Shodan, GreyNoise, AlienVault OTX, URLhaus",
|
|
17527
|
-
"asset_management (Assets & Vulnerabilities): Shuffle Assets & Vulnerabilities, Tenable, Qualys, Rapid7, NetBox, Snipe-IT, ServiceNow CMDB",
|
|
17528
|
-
"iam (Identity & Access Management): Okta, Microsoft Entra ID (Azure AD), Active Directory LDAP, Google Workspace Admin, Duo, Keycloak",
|
|
17529
|
-
"network (Network & Firewalls): Palo Alto PAN-OS, Fortinet FortiGate, Cloudflare, Suricata, Zeek, Zscaler, Cisco Meraki/ASA",
|
|
17530
|
-
"cloud (Cloud Security): AWS (GuardDuty, CloudTrail, EC2, IAM), Azure Security Center, Google Cloud SCC, Wiz, Prisma Cloud",
|
|
17531
|
-
"communication (Notifications & On-Call): Slack, Microsoft Teams, Email, PagerDuty, Opsgenie, Discord, Shuffle Mobile App"
|
|
17532
|
-
];
|
|
17533
|
-
var USECASE_ARCHITECTURE_OVERVIEW = [
|
|
17534
|
-
"How Usecases Work in Shuffle Security:",
|
|
17535
|
-
" 1. Three-Phase Operational Pipeline:",
|
|
17536
|
-
" - 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`).",
|
|
17537
|
-
" - 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.",
|
|
17538
|
-
" - 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`).",
|
|
17539
|
-
" 2. Enabling & Configuring a Usecase:",
|
|
17540
|
-
" - 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`).",
|
|
17541
|
-
" - 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.",
|
|
17542
|
-
" - 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`).",
|
|
17543
|
-
" - Any external source can push JSON events directly into Shuffle Security via the tenant Ingestion Webhook (`POST /api/v1/hooks/webhook_<id>`).",
|
|
17544
|
-
" 3. How You (the Agent) Should Help the User:",
|
|
17545
|
-
" - Recommend which usecases to enable next based on the tenant's currently enabled usecases and connected/authenticated tools.",
|
|
17546
|
-
" - Explain step-by-step how to configure or troubleshoot any usecase, including required app credentials, webhook setup, or datastore triggers.",
|
|
17547
|
-
" - Inspect or generate workflows, check authentication status, and verify datastore automations using the Shuffle APIs below when requested."
|
|
17548
|
-
].join("\n");
|
|
17549
|
-
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");
|
|
17550
|
-
var findCatalogUsecase = (flowId) => {
|
|
17551
|
-
if (!flowId) return void 0;
|
|
17552
|
-
const norm4 = flowId.trim().toLowerCase().replace(/[\s-]+/g, "_");
|
|
17553
|
-
return DEFAULT_USECASE_CATALOG.find(
|
|
17554
|
-
(u) => u.id.toLowerCase() === norm4 || u.slug.toLowerCase() === norm4 || u.label.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "") === norm4
|
|
17555
|
-
);
|
|
17556
16695
|
};
|
|
17557
|
-
var
|
|
17558
|
-
|
|
17559
|
-
|
|
17560
|
-
|
|
17561
|
-
|
|
17562
|
-
|
|
17563
|
-
|
|
17564
|
-
|
|
17565
|
-
|
|
17566
|
-
|
|
17567
|
-
|
|
17568
|
-
|
|
16696
|
+
var PROMPT_KEYS = [
|
|
16697
|
+
"original_input",
|
|
16698
|
+
"originalinput",
|
|
16699
|
+
"input",
|
|
16700
|
+
"prompt",
|
|
16701
|
+
"user_input",
|
|
16702
|
+
"userinput",
|
|
16703
|
+
"user_prompt",
|
|
16704
|
+
"userprompt",
|
|
16705
|
+
"question",
|
|
16706
|
+
"query",
|
|
16707
|
+
"message",
|
|
16708
|
+
"text",
|
|
16709
|
+
"content",
|
|
16710
|
+
"task",
|
|
16711
|
+
"instruction",
|
|
16712
|
+
"instructions"
|
|
16713
|
+
];
|
|
16714
|
+
var deepFindPrompt = (value, depth = 0) => {
|
|
16715
|
+
if (value == null || depth > 6) return null;
|
|
16716
|
+
if (typeof value === "string") {
|
|
16717
|
+
const s = value.trim();
|
|
16718
|
+
if (s.startsWith("{") && s.endsWith("}") || s.startsWith("[") && s.endsWith("]")) {
|
|
16719
|
+
try {
|
|
16720
|
+
return deepFindPrompt(JSON.parse(s), depth + 1);
|
|
16721
|
+
} catch {
|
|
16722
|
+
}
|
|
17569
16723
|
}
|
|
17570
|
-
|
|
17571
|
-
|
|
17572
|
-
|
|
16724
|
+
return null;
|
|
16725
|
+
}
|
|
16726
|
+
if (Array.isArray(value)) {
|
|
16727
|
+
for (const item of value) {
|
|
16728
|
+
const hit = deepFindPrompt(item, depth + 1);
|
|
16729
|
+
if (hit) return hit;
|
|
17573
16730
|
}
|
|
17574
|
-
|
|
17575
|
-
|
|
17576
|
-
|
|
17577
|
-
|
|
17578
|
-
|
|
16731
|
+
return null;
|
|
16732
|
+
}
|
|
16733
|
+
if (typeof value === "object") {
|
|
16734
|
+
const obj = value;
|
|
16735
|
+
for (const key of Object.keys(obj)) {
|
|
16736
|
+
if (PROMPT_KEYS.includes(key.toLowerCase())) {
|
|
16737
|
+
const v = obj[key];
|
|
16738
|
+
if (typeof v === "string" && v.trim()) return v.trim();
|
|
16739
|
+
const nested = deepFindPrompt(v, depth + 1);
|
|
16740
|
+
if (nested) return nested;
|
|
16741
|
+
}
|
|
17579
16742
|
}
|
|
17580
|
-
|
|
17581
|
-
|
|
17582
|
-
const
|
|
17583
|
-
|
|
16743
|
+
for (const key of Object.keys(obj)) {
|
|
16744
|
+
if (PROMPT_KEYS.includes(key.toLowerCase())) continue;
|
|
16745
|
+
const hit = deepFindPrompt(obj[key], depth + 1);
|
|
16746
|
+
if (hit) return hit;
|
|
17584
16747
|
}
|
|
17585
|
-
|
|
17586
|
-
|
|
17587
|
-
|
|
16748
|
+
}
|
|
16749
|
+
return null;
|
|
16750
|
+
};
|
|
16751
|
+
var tryParseJson = (s) => {
|
|
16752
|
+
if (typeof s !== "string") return s;
|
|
16753
|
+
const trimmed = s.trim();
|
|
16754
|
+
if (!trimmed) return null;
|
|
16755
|
+
try {
|
|
16756
|
+
return JSON.parse(trimmed);
|
|
16757
|
+
} catch {
|
|
16758
|
+
return trimmed;
|
|
16759
|
+
}
|
|
16760
|
+
};
|
|
16761
|
+
var getRunPrompt = (run2) => {
|
|
16762
|
+
const anyRun = run2;
|
|
16763
|
+
const agentInput = getDirectAgentInput(getAIAgentResultPayload(run2));
|
|
16764
|
+
if (agentInput) return agentInput;
|
|
16765
|
+
const results = Array.isArray(anyRun.results) ? anyRun.results : null;
|
|
16766
|
+
if (results) {
|
|
16767
|
+
const agentResult = results.find(isAIAgentResult);
|
|
16768
|
+
if (agentResult?.result) {
|
|
16769
|
+
const hit = deepFindPrompt(tryParseJson(agentResult.result));
|
|
16770
|
+
if (hit) return hit;
|
|
17588
16771
|
}
|
|
17589
|
-
|
|
17590
|
-
|
|
16772
|
+
for (const r of results) {
|
|
16773
|
+
if (r === agentResult) continue;
|
|
16774
|
+
const hit = deepFindPrompt(tryParseJson(r?.result));
|
|
16775
|
+
if (hit) return hit;
|
|
17591
16776
|
}
|
|
17592
|
-
|
|
17593
|
-
|
|
17594
|
-
|
|
17595
|
-
|
|
17596
|
-
|
|
17597
|
-
|
|
17598
|
-
|
|
16777
|
+
}
|
|
16778
|
+
if (typeof anyRun.original_input === "string" && anyRun.original_input.trim()) {
|
|
16779
|
+
return anyRun.original_input.trim();
|
|
16780
|
+
}
|
|
16781
|
+
if (typeof anyRun.input === "string" && anyRun.input.trim()) {
|
|
16782
|
+
return anyRun.input.trim();
|
|
16783
|
+
}
|
|
16784
|
+
if (run2.result) {
|
|
16785
|
+
const hit = deepFindPrompt(tryParseJson(run2.result));
|
|
16786
|
+
if (hit) return hit;
|
|
16787
|
+
}
|
|
16788
|
+
if (run2.execution_argument) {
|
|
16789
|
+
const parsed = tryParseJson(run2.execution_argument);
|
|
16790
|
+
const hit = deepFindPrompt(parsed);
|
|
16791
|
+
if (hit) return hit;
|
|
16792
|
+
if (typeof parsed === "string" && parsed.length < 240) return parsed;
|
|
16793
|
+
}
|
|
16794
|
+
return null;
|
|
16795
|
+
};
|
|
16796
|
+
var stripMarkdown = (input) => {
|
|
16797
|
+
if (!input) return "";
|
|
16798
|
+
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, "");
|
|
16799
|
+
s = s.replace(/```[\s\S]*?```/g, " ");
|
|
16800
|
+
s = s.replace(/`([^`]*)`/g, "$1");
|
|
16801
|
+
s = s.replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1");
|
|
16802
|
+
s = s.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1");
|
|
16803
|
+
s = s.replace(/(\*\*|__)(.*?)\1/g, "$2");
|
|
16804
|
+
s = s.replace(/(\*|_)(?=\S)([^*_\n]+?)\1/g, "$2");
|
|
16805
|
+
s = s.replace(/^\s{0,3}#{1,6}\s+/gm, "");
|
|
16806
|
+
s = s.replace(/^\s{0,3}>\s?/gm, "");
|
|
16807
|
+
s = s.replace(/^\s*[-*+]\s+/gm, "");
|
|
16808
|
+
s = s.replace(/^\s*\d+\.\s+/gm, "");
|
|
16809
|
+
s = s.replace(/\s+/g, " ").trim();
|
|
16810
|
+
return s;
|
|
16811
|
+
};
|
|
16812
|
+
var getRunTitle = (run2) => {
|
|
16813
|
+
const prompt = getRunPrompt(run2);
|
|
16814
|
+
if (prompt) {
|
|
16815
|
+
const oneLine = stripMarkdown(prompt);
|
|
16816
|
+
return oneLine.length > 80 ? oneLine.slice(0, 80) + "\u2026" : oneLine;
|
|
16817
|
+
}
|
|
16818
|
+
if (run2.workflow?.name) return stripMarkdown(run2.workflow.name);
|
|
16819
|
+
return `Execution ${run2.execution_id?.slice(0, 8) || "\u2014"}`;
|
|
16820
|
+
};
|
|
16821
|
+
var getDecisionCount = (run2) => {
|
|
16822
|
+
if (Array.isArray(run2.decisions)) return run2.decisions.length;
|
|
16823
|
+
return 0;
|
|
16824
|
+
};
|
|
16825
|
+
var getRunSubtitle = (run2) => {
|
|
16826
|
+
if (run2.result) {
|
|
16827
|
+
try {
|
|
16828
|
+
const p = JSON.parse(run2.result);
|
|
16829
|
+
if (p.output && typeof p.output === "string") {
|
|
16830
|
+
const out = stripMarkdown(p.output);
|
|
16831
|
+
return out.length > 120 ? out.slice(0, 120) + "\u2026" : out;
|
|
16832
|
+
}
|
|
16833
|
+
if (p.original_input && typeof p.original_input === "string") {
|
|
16834
|
+
const inp = stripMarkdown(p.original_input);
|
|
16835
|
+
return inp.length > 120 ? inp.slice(0, 120) + "\u2026" : inp;
|
|
16836
|
+
}
|
|
16837
|
+
if (p.message && typeof p.message === "string") return stripMarkdown(p.message);
|
|
16838
|
+
} catch {
|
|
16839
|
+
if (run2.result.length < 120) return stripMarkdown(run2.result);
|
|
17599
16840
|
}
|
|
17600
|
-
|
|
17601
|
-
|
|
17602
|
-
|
|
17603
|
-
|
|
17604
|
-
|
|
17605
|
-
|
|
17606
|
-
|
|
16841
|
+
}
|
|
16842
|
+
const status = getEffectiveStatus(run2);
|
|
16843
|
+
const isRunning = status === "EXECUTING" || status === "RUNNING" || status === "WAITING";
|
|
16844
|
+
return run2.execution_source || (isRunning ? "Agent still running\u2026" : "Agent execution");
|
|
16845
|
+
};
|
|
16846
|
+
var normalizeResultStatus = (s) => {
|
|
16847
|
+
const v = (s || "").toUpperCase();
|
|
16848
|
+
if (v === "SUCCESS" || v === "FINISHED") return "success";
|
|
16849
|
+
if (v === "FAILURE" || v === "FAILED" || v === "ABORTED") return "failure";
|
|
16850
|
+
if (v === "WAITING" || v === "SKIPPED") return "waiting";
|
|
16851
|
+
return "unknown";
|
|
16852
|
+
};
|
|
16853
|
+
var getRunTools = (run2) => {
|
|
16854
|
+
const map = /* @__PURE__ */ new Map();
|
|
16855
|
+
const ids = /* @__PURE__ */ new Map();
|
|
16856
|
+
const skip = (s) => /^(ai\s*agent|shuffle\s*agent|shuffle_agent)$/i.test(s);
|
|
16857
|
+
const rank = { failure: 3, waiting: 2, success: 1, unknown: 0 };
|
|
16858
|
+
const merge2 = (name, status, id) => {
|
|
16859
|
+
if (!name) return;
|
|
16860
|
+
const s = String(name).trim();
|
|
16861
|
+
if (!s || skip(s)) return;
|
|
16862
|
+
const next = status || "unknown";
|
|
16863
|
+
const prev = map.get(s);
|
|
16864
|
+
if (!prev || rank[next] > rank[prev]) map.set(s, next);
|
|
16865
|
+
if (id && !ids.has(s)) ids.set(s, id);
|
|
16866
|
+
};
|
|
16867
|
+
const allowed = run2.allowed_actions;
|
|
16868
|
+
if (Array.isArray(allowed) && allowed.length > 0) {
|
|
16869
|
+
for (const entry of allowed) {
|
|
16870
|
+
if (typeof entry !== "string") continue;
|
|
16871
|
+
const parts = entry.split(":");
|
|
16872
|
+
if (parts.length < 3 || parts[0] !== "app") continue;
|
|
16873
|
+
const id = parts[1];
|
|
16874
|
+
const name = parts.slice(2).join(":");
|
|
16875
|
+
merge2(name, "unknown", id);
|
|
16876
|
+
}
|
|
16877
|
+
const normalize3 = (s) => s.toLowerCase().replace(/[\s_-]+/g, "_");
|
|
16878
|
+
const known = /* @__PURE__ */ new Map();
|
|
16879
|
+
for (const real of map.keys()) known.set(normalize3(real), real);
|
|
16880
|
+
const apply = (name, status) => {
|
|
16881
|
+
if (!name) return;
|
|
16882
|
+
const hit = known.get(normalize3(name));
|
|
16883
|
+
if (hit) merge2(hit, status);
|
|
16884
|
+
};
|
|
16885
|
+
(run2.results || []).forEach((r) => {
|
|
16886
|
+
apply(r?.action?.app_name || r?.action?.label, normalizeResultStatus(r?.status));
|
|
16887
|
+
});
|
|
16888
|
+
(run2.decisions || []).forEach((d) => {
|
|
16889
|
+
if (typeof d?.tool === "string") apply(d.tool, normalizeResultStatus(d?.status));
|
|
16890
|
+
});
|
|
16891
|
+
} else {
|
|
16892
|
+
(run2.results || []).forEach((r) => {
|
|
16893
|
+
merge2(r?.action?.app_name || r?.action?.label, normalizeResultStatus(r?.status));
|
|
16894
|
+
});
|
|
16895
|
+
(run2.decisions || []).forEach((d) => {
|
|
16896
|
+
if (typeof d?.tool === "string") merge2(d.tool, normalizeResultStatus(d?.status));
|
|
16897
|
+
});
|
|
16898
|
+
}
|
|
16899
|
+
return Array.from(map.entries()).slice(0, 6).map(([name, status]) => ({ name, status, id: ids.get(name) }));
|
|
16900
|
+
};
|
|
16901
|
+
var TOOL_STATUS_RING = {
|
|
16902
|
+
success: "hsl(var(--severity-low, 142 71% 45%))",
|
|
16903
|
+
failure: "hsl(var(--severity-critical, 0 72% 55%))",
|
|
16904
|
+
waiting: "hsl(var(--severity-medium, 38 92% 50%))",
|
|
16905
|
+
unknown: "hsl(var(--border))"
|
|
16906
|
+
};
|
|
16907
|
+
var TOOL_STATUS_LABEL = {
|
|
16908
|
+
success: "ran successfully",
|
|
16909
|
+
failure: "failed",
|
|
16910
|
+
waiting: "needs input",
|
|
16911
|
+
unknown: "used"
|
|
16912
|
+
};
|
|
16913
|
+
var normToolKey = (s) => s.toLowerCase().replace(/[\s_\-]+/g, "_");
|
|
16914
|
+
var AgentRunRow = ({ run: run2, onClick, sx, appIcons, onAppClick, apiKey, apiBaseUrl, orgId, abortingIds, onAbort }) => {
|
|
16915
|
+
const navigate = (0, import_router_compat.useNavigate)();
|
|
16916
|
+
const statusKey = getEffectiveStatus(run2);
|
|
16917
|
+
const iconColor = getRunIconColor(run2);
|
|
16918
|
+
const duration = formatDuration(run2);
|
|
16919
|
+
const tools = getRunTools(run2);
|
|
16920
|
+
const decisionCount = getDecisionCount(run2);
|
|
16921
|
+
const attachmentCount = collectLlmImageAttachments(run2).length;
|
|
16922
|
+
const isRunning = statusKey === "EXECUTING" || statusKey === "RUNNING" || statusKey === "WAITING";
|
|
16923
|
+
const isAborting = !!run2.execution_id && !!abortingIds?.has(run2.execution_id);
|
|
16924
|
+
const wfId = run2.workflow_id || run2?.workflow?.id;
|
|
16925
|
+
const canAbort = isRunning && !!run2.execution_id && !!wfId && !isAborting;
|
|
16926
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
16927
|
+
import_material17.Box,
|
|
16928
|
+
{
|
|
16929
|
+
onClick,
|
|
16930
|
+
sx: [
|
|
16931
|
+
{
|
|
16932
|
+
borderRadius: 2,
|
|
16933
|
+
border: "1px solid hsl(var(--border))",
|
|
16934
|
+
bgcolor: "hsl(var(--card))",
|
|
16935
|
+
display: "flex",
|
|
16936
|
+
alignItems: "center",
|
|
16937
|
+
gap: 2,
|
|
16938
|
+
px: 2.5,
|
|
16939
|
+
py: 2,
|
|
16940
|
+
cursor: "pointer",
|
|
16941
|
+
transition: "background 0.15s ease, border-color 0.15s ease",
|
|
16942
|
+
"&:hover": {
|
|
16943
|
+
bgcolor: "hsla(var(--muted) / 0.5)",
|
|
16944
|
+
borderColor: "hsl(var(--muted-foreground) / 0.3)"
|
|
16945
|
+
}
|
|
16946
|
+
},
|
|
16947
|
+
...Array.isArray(sx) ? sx : sx ? [sx] : []
|
|
16948
|
+
],
|
|
16949
|
+
children: [
|
|
16950
|
+
(() => {
|
|
16951
|
+
const sourceInfo = classifyRunSource(run2);
|
|
16952
|
+
const dsLink = sourceInfo.kind === "datastore" && sourceInfo.datastoreKey && sourceInfo.datastoreCategory ? `https://shuffler.io/admin?tab=datastore&category=${encodeURIComponent(sourceInfo.datastoreCategory)}&key=${encodeURIComponent(sourceInfo.datastoreKey)}` : null;
|
|
16953
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
16954
|
+
import_material17.Tooltip,
|
|
16955
|
+
{
|
|
16956
|
+
title: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: { lineHeight: 1.4 }, children: [
|
|
16957
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Box, { sx: { fontWeight: 600 }, children: sourceInfo.label }),
|
|
16958
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Box, { sx: { opacity: 0.85, whiteSpace: "pre-line" }, children: sourceInfo.reason })
|
|
16959
|
+
] }),
|
|
16960
|
+
placement: "top",
|
|
16961
|
+
arrow: true,
|
|
16962
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
16963
|
+
import_material17.Box,
|
|
16964
|
+
{
|
|
16965
|
+
onClick: dsLink ? async (e) => {
|
|
16966
|
+
e.stopPropagation();
|
|
16967
|
+
await navigateToShuffleCore(dsLink, { newTab: true });
|
|
16968
|
+
} : void 0,
|
|
16969
|
+
sx: {
|
|
16970
|
+
width: 40,
|
|
16971
|
+
height: 40,
|
|
16972
|
+
borderRadius: "50%",
|
|
16973
|
+
display: "flex",
|
|
16974
|
+
alignItems: "center",
|
|
16975
|
+
justifyContent: "center",
|
|
16976
|
+
bgcolor: `${iconColor}15`,
|
|
16977
|
+
color: iconColor,
|
|
16978
|
+
flexShrink: 0,
|
|
16979
|
+
cursor: dsLink ? "pointer" : "inherit",
|
|
16980
|
+
transition: "background 0.15s ease",
|
|
16981
|
+
"&:hover": dsLink ? { bgcolor: `${iconColor}30` } : void 0
|
|
16982
|
+
},
|
|
16983
|
+
children: sourceInfo.icon
|
|
16984
|
+
}
|
|
16985
|
+
)
|
|
16986
|
+
}
|
|
16987
|
+
);
|
|
16988
|
+
})(),
|
|
16989
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: { flex: 1, minWidth: 0 }, children: [
|
|
16990
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Box, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 0.25 }, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
16991
|
+
import_material17.Typography,
|
|
16992
|
+
{
|
|
16993
|
+
sx: {
|
|
16994
|
+
fontSize: "0.9rem",
|
|
16995
|
+
fontWeight: 500,
|
|
16996
|
+
color: "hsl(var(--foreground))",
|
|
16997
|
+
overflow: "hidden",
|
|
16998
|
+
textOverflow: "ellipsis",
|
|
16999
|
+
whiteSpace: "nowrap"
|
|
17000
|
+
},
|
|
17001
|
+
children: getRunTitle(run2)
|
|
17002
|
+
}
|
|
17003
|
+
) }),
|
|
17004
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
17005
|
+
import_material17.Typography,
|
|
17006
|
+
{
|
|
17007
|
+
sx: {
|
|
17008
|
+
fontSize: "0.78rem",
|
|
17009
|
+
color: "hsl(var(--muted-foreground))",
|
|
17010
|
+
overflow: "hidden",
|
|
17011
|
+
textOverflow: "ellipsis",
|
|
17012
|
+
whiteSpace: "nowrap"
|
|
17013
|
+
},
|
|
17014
|
+
children: getRunSubtitle(run2)
|
|
17015
|
+
}
|
|
17016
|
+
),
|
|
17017
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: { display: "flex", alignItems: "center", gap: 1, mt: 0.5 }, children: [
|
|
17018
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Typography, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.7 }, children: run2.started_at ? getTimeAgo(run2.started_at) : "\u2014" }),
|
|
17019
|
+
duration && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
|
|
17020
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Typography, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.4 }, children: "\xB7" }),
|
|
17021
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Typography, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.7 }, children: duration })
|
|
17022
|
+
] }),
|
|
17023
|
+
decisionCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
|
|
17024
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Typography, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.4 }, children: "\xB7" }),
|
|
17025
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: { display: "inline-flex", alignItems: "center", gap: 0.5, color: "hsl(var(--muted-foreground))", opacity: 0.7 }, children: [
|
|
17026
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.GitBranch, { size: 11 }),
|
|
17027
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Typography, { sx: { fontSize: "0.72rem", color: "inherit" }, children: [
|
|
17028
|
+
decisionCount,
|
|
17029
|
+
" ",
|
|
17030
|
+
decisionCount === 1 ? "decision" : "decisions"
|
|
17031
|
+
] })
|
|
17032
|
+
] })
|
|
17033
|
+
] }),
|
|
17034
|
+
attachmentCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
|
|
17035
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Typography, { sx: { fontSize: "0.72rem", color: "hsl(var(--muted-foreground))", opacity: 0.4 }, children: "\xB7" }),
|
|
17036
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: { display: "inline-flex", alignItems: "center", gap: 0.5, color: "hsl(var(--muted-foreground))", opacity: 0.7 }, children: [
|
|
17037
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Image, { size: 11 }),
|
|
17038
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Typography, { sx: { fontSize: "0.72rem", color: "inherit" }, children: [
|
|
17039
|
+
attachmentCount,
|
|
17040
|
+
" ",
|
|
17041
|
+
attachmentCount === 1 ? "attachment" : "attachments"
|
|
17042
|
+
] })
|
|
17043
|
+
] })
|
|
17044
|
+
] })
|
|
17045
|
+
] })
|
|
17046
|
+
] }),
|
|
17047
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
17048
|
+
import_material17.Box,
|
|
17049
|
+
{
|
|
17050
|
+
onClick: (e) => e.stopPropagation(),
|
|
17051
|
+
sx: {
|
|
17052
|
+
display: { xs: "none", sm: "flex" },
|
|
17053
|
+
alignItems: "center",
|
|
17054
|
+
justifyContent: "flex-end",
|
|
17055
|
+
flexShrink: 0,
|
|
17056
|
+
ml: tools.length > 0 ? 1 : 0,
|
|
17057
|
+
width: tools.length > 0 ? 140 : 0,
|
|
17058
|
+
minHeight: tools.length > 0 ? 28 : 0
|
|
17059
|
+
},
|
|
17060
|
+
children: tools.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
17061
|
+
import_material17.AvatarGroup,
|
|
17062
|
+
{
|
|
17063
|
+
max: 5,
|
|
17064
|
+
sx: {
|
|
17065
|
+
"& .MuiAvatar-root": {
|
|
17066
|
+
width: 28,
|
|
17067
|
+
height: 28,
|
|
17068
|
+
fontSize: "0.7rem",
|
|
17069
|
+
borderColor: "hsl(var(--border))",
|
|
17070
|
+
bgcolor: "hsl(var(--muted))",
|
|
17071
|
+
color: "hsl(var(--muted-foreground))"
|
|
17072
|
+
}
|
|
17073
|
+
},
|
|
17074
|
+
children: tools.map((t) => {
|
|
17075
|
+
const icon = appIcons?.[normToolKey(t.name)];
|
|
17076
|
+
const label = t.name.replace(/_/g, " ");
|
|
17077
|
+
const slug = t.name.toLowerCase().replace(/\s+/g, "_");
|
|
17078
|
+
const ring = TOOL_STATUS_RING[t.status];
|
|
17079
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Tooltip, { title: `${label} \u2014 ${TOOL_STATUS_LABEL[t.status]}`, arrow: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
17080
|
+
import_material17.Avatar,
|
|
17081
|
+
{
|
|
17082
|
+
src: icon || void 0,
|
|
17083
|
+
alt: label,
|
|
17084
|
+
variant: "rounded",
|
|
17085
|
+
onClick: (e) => {
|
|
17086
|
+
e.stopPropagation();
|
|
17087
|
+
if (onAppClick) {
|
|
17088
|
+
onAppClick({ id: t.id, name: t.name });
|
|
17089
|
+
} else {
|
|
17090
|
+
navigate(`/apps/${encodeURIComponent(t.id || slug)}`);
|
|
17091
|
+
}
|
|
17092
|
+
},
|
|
17093
|
+
sx: {
|
|
17094
|
+
cursor: "pointer",
|
|
17095
|
+
borderColor: `${ring} !important`,
|
|
17096
|
+
borderWidth: "1px",
|
|
17097
|
+
borderStyle: "solid",
|
|
17098
|
+
transition: "transform 0.15s ease, border-color 0.15s ease",
|
|
17099
|
+
"&:hover": {
|
|
17100
|
+
transform: "scale(1.08)",
|
|
17101
|
+
borderColor: "hsl(var(--primary)) !important"
|
|
17102
|
+
}
|
|
17103
|
+
},
|
|
17104
|
+
children: label.charAt(0).toUpperCase()
|
|
17105
|
+
}
|
|
17106
|
+
) }, t.name);
|
|
17107
|
+
})
|
|
17108
|
+
}
|
|
17109
|
+
) : null
|
|
17110
|
+
}
|
|
17111
|
+
),
|
|
17112
|
+
canAbort && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Tooltip, { title: isAborting ? "Aborting\u2026" : "Abort this execution", arrow: true, placement: "top", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
17113
|
+
import_material17.IconButton,
|
|
17114
|
+
{
|
|
17115
|
+
size: "small",
|
|
17116
|
+
disabled: isAborting,
|
|
17117
|
+
onClick: (e) => {
|
|
17118
|
+
e.stopPropagation();
|
|
17119
|
+
onAbort?.(run2);
|
|
17120
|
+
},
|
|
17121
|
+
sx: {
|
|
17122
|
+
flexShrink: 0,
|
|
17123
|
+
ml: 1.5,
|
|
17124
|
+
color: "hsl(var(--muted-foreground))",
|
|
17125
|
+
"&:hover": { color: "hsl(var(--destructive))", bgcolor: "hsl(var(--muted))" }
|
|
17126
|
+
},
|
|
17127
|
+
children: isAborting ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.CircularProgress, { size: 16, sx: { color: "hsl(var(--destructive))" } }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react8.StopCircle, { size: 18 })
|
|
17128
|
+
}
|
|
17129
|
+
) }) })
|
|
17130
|
+
]
|
|
17607
17131
|
}
|
|
17608
|
-
|
|
17609
|
-
|
|
17610
|
-
|
|
17611
|
-
|
|
17612
|
-
|
|
17613
|
-
|
|
17614
|
-
|
|
17615
|
-
|
|
17616
|
-
|
|
17617
|
-
|
|
17618
|
-
|
|
17619
|
-
|
|
17620
|
-
|
|
17621
|
-
|
|
17132
|
+
);
|
|
17133
|
+
};
|
|
17134
|
+
var STATUS_FILTERS = [
|
|
17135
|
+
{ label: "All", value: "" },
|
|
17136
|
+
{ label: "Completed", value: "FINISHED" },
|
|
17137
|
+
{ label: "Running", value: "EXECUTING" },
|
|
17138
|
+
{ label: "Failed", value: "ABORTED" }
|
|
17139
|
+
];
|
|
17140
|
+
var AgentActivityList = ({
|
|
17141
|
+
apiKey,
|
|
17142
|
+
apiBaseUrl,
|
|
17143
|
+
orgId,
|
|
17144
|
+
onRunClick,
|
|
17145
|
+
renderAppDetailDrawer,
|
|
17146
|
+
onEditWorkflow,
|
|
17147
|
+
showSearchBar = true,
|
|
17148
|
+
showStatusChips = true,
|
|
17149
|
+
limit = 50,
|
|
17150
|
+
top,
|
|
17151
|
+
emptyTitle = "No agent activity found",
|
|
17152
|
+
emptySubtitle = "The agent has not performed any actions yet",
|
|
17153
|
+
className,
|
|
17154
|
+
sx,
|
|
17155
|
+
toolbarSx,
|
|
17156
|
+
rowSx,
|
|
17157
|
+
usecaseFilters = [],
|
|
17158
|
+
globalUrl,
|
|
17159
|
+
theme,
|
|
17160
|
+
colorMode,
|
|
17161
|
+
initialRuns,
|
|
17162
|
+
disableFetch = false
|
|
17163
|
+
}) => {
|
|
17164
|
+
const [appDrawer, setAppDrawer] = (0, import_react18.useState)(null);
|
|
17165
|
+
const [runs, setRuns] = (0, import_react18.useState)(
|
|
17166
|
+
() => initialRuns ? initialRuns.slice(0, limit) : []
|
|
17167
|
+
);
|
|
17168
|
+
const [cursor, setCursor] = (0, import_react18.useState)("");
|
|
17169
|
+
const [hasMore, setHasMore] = (0, import_react18.useState)(false);
|
|
17170
|
+
const [isLoading, setIsLoading] = (0, import_react18.useState)(false);
|
|
17171
|
+
const [error, setError] = (0, import_react18.useState)(null);
|
|
17172
|
+
const [statusFilter, setStatusFilter] = (0, import_react18.useState)("");
|
|
17173
|
+
const [searchQuery, setSearchQuery] = (0, import_react18.useState)("");
|
|
17174
|
+
const [debouncedQuery, setDebouncedQuery] = (0, import_react18.useState)("");
|
|
17175
|
+
const [agentWorkflows, setAgentWorkflows] = (0, import_react18.useState)([]);
|
|
17176
|
+
const [workflowFilter, setWorkflowFilter] = (0, import_react18.useState)("");
|
|
17177
|
+
const [usecaseFilter, setUsecaseFilter] = (0, import_react18.useState)("");
|
|
17178
|
+
const [stopOpen, setStopOpen] = (0, import_react18.useState)(false);
|
|
17179
|
+
const [stopLoading, setStopLoading] = (0, import_react18.useState)(false);
|
|
17180
|
+
const [appIcons, setAppIcons] = (0, import_react18.useState)({});
|
|
17181
|
+
const [enrichedRuns, setEnrichedRuns] = (0, import_react18.useState)({});
|
|
17182
|
+
const enrichedRunsRef = (0, import_react18.useRef)({});
|
|
17183
|
+
const processedRunIdsRef = (0, import_react18.useRef)(/* @__PURE__ */ new Set());
|
|
17184
|
+
(0, import_react18.useEffect)(() => {
|
|
17185
|
+
enrichedRunsRef.current = enrichedRuns;
|
|
17186
|
+
}, [enrichedRuns]);
|
|
17187
|
+
const [abortingIds, setAbortingIds] = (0, import_react18.useState)(/* @__PURE__ */ new Set());
|
|
17188
|
+
const [abortedIds, setAbortedIds] = (0, import_react18.useState)(/* @__PURE__ */ new Set());
|
|
17189
|
+
const [lastOpenedRunId, setLastOpenedRunId] = (0, import_react18.useState)(() => getLastOpenedAgentRun());
|
|
17190
|
+
(0, import_react18.useEffect)(() => {
|
|
17191
|
+
const unsubAbort = subscribeAgentAborted((eid) => {
|
|
17192
|
+
setAbortedIds((prev) => prev.has(eid) ? prev : /* @__PURE__ */ new Set([...Array.from(prev), eid]));
|
|
17193
|
+
setRuns((prev) => prev.map((r) => r.execution_id === eid ? { ...r, status: "ABORTED" } : r));
|
|
17194
|
+
});
|
|
17195
|
+
const unsubOpened = subscribeLastOpenedAgentRun((eid) => setLastOpenedRunId(eid));
|
|
17196
|
+
return () => {
|
|
17197
|
+
unsubAbort();
|
|
17198
|
+
unsubOpened();
|
|
17199
|
+
};
|
|
17200
|
+
}, []);
|
|
17201
|
+
const debounceRef = (0, import_react18.useRef)(void 0);
|
|
17202
|
+
const selectedAgentWorkflow = agentWorkflows.find((w) => w.id === workflowFilter) || null;
|
|
17203
|
+
const openEditPrompt = (0, import_react18.useCallback)(async () => {
|
|
17204
|
+
if (!workflowFilter) return;
|
|
17205
|
+
try {
|
|
17206
|
+
const { prompt, apps } = await getAgentScheduleConfig(workflowFilter, { apiKey, apiBaseUrl, orgId });
|
|
17207
|
+
onEditWorkflow?.({
|
|
17208
|
+
workflowId: workflowFilter,
|
|
17209
|
+
name: selectedAgentWorkflow?.name || "Schedule",
|
|
17210
|
+
prompt,
|
|
17211
|
+
apps
|
|
17212
|
+
});
|
|
17213
|
+
} catch (e) {
|
|
17214
|
+
setError(e instanceof Error ? e.message : "Failed to load workflow");
|
|
17622
17215
|
}
|
|
17623
|
-
|
|
17624
|
-
|
|
17625
|
-
|
|
17626
|
-
|
|
17627
|
-
|
|
17628
|
-
|
|
17629
|
-
|
|
17630
|
-
|
|
17631
|
-
|
|
17632
|
-
|
|
17633
|
-
|
|
17216
|
+
}, [workflowFilter, selectedAgentWorkflow, apiKey, apiBaseUrl, orgId, onEditWorkflow]);
|
|
17217
|
+
const confirmStop = (0, import_react18.useCallback)(async () => {
|
|
17218
|
+
if (!workflowFilter) return;
|
|
17219
|
+
setStopLoading(true);
|
|
17220
|
+
try {
|
|
17221
|
+
await stopAgentSchedule(workflowFilter, { apiKey, apiBaseUrl, orgId });
|
|
17222
|
+
setStopOpen(false);
|
|
17223
|
+
const items = await listAgentScheduleWorkflows({ apiKey, apiBaseUrl, orgId });
|
|
17224
|
+
setAgentWorkflows(items);
|
|
17225
|
+
setWorkflowFilter("");
|
|
17226
|
+
} catch (e) {
|
|
17227
|
+
setError(e instanceof Error ? e.message : "Failed to stop schedule");
|
|
17228
|
+
} finally {
|
|
17229
|
+
setStopLoading(false);
|
|
17230
|
+
}
|
|
17231
|
+
}, [workflowFilter, apiKey, apiBaseUrl, orgId]);
|
|
17232
|
+
const handleAbort = (0, import_react18.useCallback)(
|
|
17233
|
+
async (run2) => {
|
|
17234
|
+
const eid = run2.execution_id;
|
|
17235
|
+
const wfId = run2.workflow_id || run2?.workflow?.id;
|
|
17236
|
+
if (!eid || !wfId) return;
|
|
17237
|
+
const prevStatus = run2.status;
|
|
17238
|
+
setAbortingIds((prev) => /* @__PURE__ */ new Set([...Array.from(prev), eid]));
|
|
17239
|
+
setAbortedIds((prev) => /* @__PURE__ */ new Set([...Array.from(prev), eid]));
|
|
17240
|
+
broadcastAgentAborted(eid);
|
|
17241
|
+
try {
|
|
17242
|
+
await abortAgentExecution({
|
|
17243
|
+
workflowId: wfId,
|
|
17244
|
+
executionId: eid,
|
|
17245
|
+
authorization: run2.authorization,
|
|
17246
|
+
apiKey,
|
|
17247
|
+
apiBaseUrl,
|
|
17248
|
+
orgId
|
|
17249
|
+
});
|
|
17250
|
+
setRuns(
|
|
17251
|
+
(prev) => prev.map((r) => r.execution_id === eid ? { ...r, status: "ABORTED" } : r)
|
|
17252
|
+
);
|
|
17253
|
+
} catch (e) {
|
|
17254
|
+
setAbortedIds((prev) => {
|
|
17255
|
+
const next = new Set(Array.from(prev));
|
|
17256
|
+
next.delete(eid);
|
|
17257
|
+
return next;
|
|
17258
|
+
});
|
|
17259
|
+
setRuns(
|
|
17260
|
+
(prev) => prev.map((r) => r.execution_id === eid ? { ...r, status: prevStatus } : r)
|
|
17261
|
+
);
|
|
17262
|
+
toast2({
|
|
17263
|
+
title: "Abort failed",
|
|
17264
|
+
description: e instanceof Error ? e.message : "Failed to abort execution",
|
|
17265
|
+
variant: "destructive"
|
|
17266
|
+
});
|
|
17267
|
+
} finally {
|
|
17268
|
+
setAbortingIds((prev) => {
|
|
17269
|
+
const next = new Set(Array.from(prev));
|
|
17270
|
+
next.delete(eid);
|
|
17271
|
+
return next;
|
|
17272
|
+
});
|
|
17634
17273
|
}
|
|
17635
|
-
|
|
17636
|
-
|
|
17274
|
+
},
|
|
17275
|
+
[apiKey, apiBaseUrl, orgId]
|
|
17276
|
+
);
|
|
17277
|
+
const updateSearchQuery = (0, import_react18.useCallback)((q) => {
|
|
17278
|
+
setSearchQuery(q);
|
|
17279
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
17280
|
+
debounceRef.current = setTimeout(() => setDebouncedQuery(q), 350);
|
|
17281
|
+
}, []);
|
|
17282
|
+
(0, import_react18.useEffect)(() => () => {
|
|
17283
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
17284
|
+
}, []);
|
|
17285
|
+
const fetchRuns = (0, import_react18.useCallback)(
|
|
17286
|
+
async (append = false, cursorParam = "") => {
|
|
17287
|
+
if (disableFetch) {
|
|
17288
|
+
if (initialRuns && !append) {
|
|
17289
|
+
setRuns(initialRuns.slice(0, limit));
|
|
17290
|
+
}
|
|
17291
|
+
return;
|
|
17637
17292
|
}
|
|
17638
|
-
|
|
17639
|
-
|
|
17640
|
-
|
|
17641
|
-
|
|
17293
|
+
setIsLoading(true);
|
|
17294
|
+
setError(null);
|
|
17295
|
+
try {
|
|
17296
|
+
const result = await searchAgentActivity({
|
|
17297
|
+
limit,
|
|
17298
|
+
top,
|
|
17299
|
+
status: statusFilter,
|
|
17300
|
+
cursor: cursorParam,
|
|
17301
|
+
apiKey,
|
|
17302
|
+
apiBaseUrl,
|
|
17303
|
+
orgId,
|
|
17304
|
+
workflowId: workflowFilter || "AGENT"
|
|
17305
|
+
});
|
|
17306
|
+
if (result.success && result.runs && result.runs.length > 0) {
|
|
17307
|
+
const pageRuns = result.runs.slice(0, limit);
|
|
17308
|
+
setRuns((prev) => append ? [...prev, ...pageRuns] : pageRuns);
|
|
17309
|
+
setCursor(result.cursor);
|
|
17310
|
+
setHasMore(!!result.cursor && result.runs.length > 0);
|
|
17311
|
+
} else if (result.success) {
|
|
17312
|
+
setRuns((prev) => append ? prev : initialRuns && initialRuns.length > 0 ? initialRuns.slice(0, limit) : []);
|
|
17313
|
+
setCursor(result.cursor);
|
|
17314
|
+
setHasMore(false);
|
|
17315
|
+
} else {
|
|
17316
|
+
if (initialRuns && initialRuns.length > 0 && !append) {
|
|
17317
|
+
setRuns(initialRuns.slice(0, limit));
|
|
17318
|
+
} else {
|
|
17319
|
+
setError("Failed to fetch agent activity");
|
|
17320
|
+
}
|
|
17321
|
+
}
|
|
17322
|
+
} catch (err) {
|
|
17323
|
+
if (initialRuns && initialRuns.length > 0 && !append) {
|
|
17324
|
+
setRuns(initialRuns.slice(0, limit));
|
|
17325
|
+
} else {
|
|
17326
|
+
setError(err instanceof Error ? err.message : "Failed to fetch agent activity");
|
|
17327
|
+
}
|
|
17328
|
+
} finally {
|
|
17329
|
+
setIsLoading(false);
|
|
17330
|
+
}
|
|
17331
|
+
},
|
|
17332
|
+
[disableFetch, initialRuns, statusFilter, limit, top, apiKey, apiBaseUrl, orgId, workflowFilter]
|
|
17333
|
+
);
|
|
17334
|
+
(0, import_react18.useEffect)(() => {
|
|
17335
|
+
fetchRuns(false, "");
|
|
17336
|
+
}, [statusFilter, apiKey, apiBaseUrl, orgId, workflowFilter]);
|
|
17337
|
+
(0, import_react18.useEffect)(() => {
|
|
17338
|
+
let cancelled = false;
|
|
17339
|
+
listAgentScheduleWorkflows({ apiKey, apiBaseUrl, orgId }).then((items) => {
|
|
17340
|
+
if (!cancelled) setAgentWorkflows(items);
|
|
17341
|
+
}).catch(() => {
|
|
17342
|
+
});
|
|
17343
|
+
return () => {
|
|
17344
|
+
cancelled = true;
|
|
17345
|
+
};
|
|
17346
|
+
}, [apiKey, apiBaseUrl, orgId]);
|
|
17347
|
+
(0, import_react18.useEffect)(() => {
|
|
17348
|
+
let cancelled = false;
|
|
17349
|
+
fetchAppsViaApiConfig().then((apps) => {
|
|
17350
|
+
if (cancelled || !Array.isArray(apps)) return;
|
|
17351
|
+
const map = {};
|
|
17352
|
+
for (const a of apps) {
|
|
17353
|
+
const name = a?.name;
|
|
17354
|
+
const img = a?.large_image || a?.image_url || a?.image;
|
|
17355
|
+
if (!name || !img) continue;
|
|
17356
|
+
map[normToolKey(name)] = img;
|
|
17642
17357
|
}
|
|
17643
|
-
|
|
17644
|
-
|
|
17358
|
+
setAppIcons(map);
|
|
17359
|
+
}).catch(() => {
|
|
17360
|
+
});
|
|
17361
|
+
return () => {
|
|
17362
|
+
cancelled = true;
|
|
17363
|
+
};
|
|
17364
|
+
}, []);
|
|
17365
|
+
const iconAttemptsRef = (0, import_react18.useRef)(/* @__PURE__ */ new Set());
|
|
17366
|
+
const iconMountedRef = (0, import_react18.useRef)(true);
|
|
17367
|
+
(0, import_react18.useEffect)(() => {
|
|
17368
|
+
iconMountedRef.current = true;
|
|
17369
|
+
return () => {
|
|
17370
|
+
iconMountedRef.current = false;
|
|
17371
|
+
};
|
|
17372
|
+
}, []);
|
|
17373
|
+
(0, import_react18.useEffect)(() => {
|
|
17374
|
+
if (!runs.length) return;
|
|
17375
|
+
const missing = /* @__PURE__ */ new Set();
|
|
17376
|
+
for (const run2 of runs) {
|
|
17377
|
+
for (const t of getRunTools(run2)) {
|
|
17378
|
+
const key = normToolKey(t.name);
|
|
17379
|
+
if (!appIcons[key] && !iconAttemptsRef.current.has(key)) missing.add(t.name);
|
|
17645
17380
|
}
|
|
17646
|
-
|
|
17647
|
-
|
|
17381
|
+
}
|
|
17382
|
+
if (missing.size === 0) return;
|
|
17383
|
+
(async () => {
|
|
17384
|
+
const names = [...missing];
|
|
17385
|
+
const resolved = {};
|
|
17386
|
+
for (let pass = 0; pass < 2; pass++) {
|
|
17387
|
+
for (const name of names) {
|
|
17388
|
+
const key = normToolKey(name);
|
|
17389
|
+
iconAttemptsRef.current.add(key);
|
|
17390
|
+
if (!resolved[key]) {
|
|
17391
|
+
const fb = publicAppImage(name);
|
|
17392
|
+
if (fb) {
|
|
17393
|
+
resolved[key] = fb;
|
|
17394
|
+
} else {
|
|
17395
|
+
try {
|
|
17396
|
+
const hit = await Promise.race([
|
|
17397
|
+
resolveApp(name),
|
|
17398
|
+
new Promise((r) => setTimeout(() => r(null), 4e3))
|
|
17399
|
+
]);
|
|
17400
|
+
if (hit?.image) resolved[key] = hit.image;
|
|
17401
|
+
} catch {
|
|
17402
|
+
}
|
|
17403
|
+
}
|
|
17404
|
+
}
|
|
17405
|
+
await new Promise((r) => setTimeout(r, 120));
|
|
17406
|
+
}
|
|
17407
|
+
if (pass === 0) await new Promise((r) => setTimeout(r, 2e3));
|
|
17648
17408
|
}
|
|
17649
|
-
if (
|
|
17650
|
-
|
|
17651
|
-
|
|
17652
|
-
|
|
17653
|
-
|
|
17654
|
-
|
|
17655
|
-
|
|
17656
|
-
|
|
17657
|
-
|
|
17658
|
-
|
|
17659
|
-
|
|
17409
|
+
if (!iconMountedRef.current || Object.keys(resolved).length === 0) return;
|
|
17410
|
+
setAppIcons((prev) => ({ ...prev, ...resolved }));
|
|
17411
|
+
})();
|
|
17412
|
+
}, [runs, appIcons]);
|
|
17413
|
+
(0, import_react18.useEffect)(() => {
|
|
17414
|
+
if (!runs.length) return;
|
|
17415
|
+
let cancelled = false;
|
|
17416
|
+
const buildPatchFromRun = (r) => {
|
|
17417
|
+
const results = Array.isArray(r.results) ? r.results : null;
|
|
17418
|
+
if (!results || results.length === 0) return null;
|
|
17419
|
+
const agentResult = results.find((x) => x?.action?.app_name === "AI Agent");
|
|
17420
|
+
let decisions = Array.isArray(r.decisions) ? r.decisions : void 0;
|
|
17421
|
+
let originalInput;
|
|
17422
|
+
let allowedActions;
|
|
17423
|
+
if (agentResult?.result) {
|
|
17424
|
+
try {
|
|
17425
|
+
const parsed = JSON.parse(agentResult.result);
|
|
17426
|
+
if (!decisions && Array.isArray(parsed?.decisions)) decisions = parsed.decisions;
|
|
17427
|
+
if (typeof parsed?.original_input === "string") originalInput = parsed.original_input;
|
|
17428
|
+
if (Array.isArray(parsed?.allowed_actions)) allowedActions = parsed.allowed_actions;
|
|
17429
|
+
} catch {
|
|
17660
17430
|
}
|
|
17661
17431
|
}
|
|
17432
|
+
const hasPrompt = !!r.execution_argument || !!originalInput || !!agentResult?.result;
|
|
17433
|
+
if (!decisions || !hasPrompt) return null;
|
|
17434
|
+
const patch2 = {
|
|
17435
|
+
results,
|
|
17436
|
+
execution_argument: r.execution_argument,
|
|
17437
|
+
result: agentResult?.result ?? r.result,
|
|
17438
|
+
decisions,
|
|
17439
|
+
allowed_actions: allowedActions
|
|
17440
|
+
};
|
|
17441
|
+
if (originalInput && !patch2.execution_argument) {
|
|
17442
|
+
patch2.execution_argument = JSON.stringify({ original_input: originalInput });
|
|
17443
|
+
}
|
|
17444
|
+
return patch2;
|
|
17445
|
+
};
|
|
17446
|
+
let pending = {};
|
|
17447
|
+
let flushTimer = null;
|
|
17448
|
+
const flush = () => {
|
|
17449
|
+
flushTimer = null;
|
|
17450
|
+
if (cancelled) return;
|
|
17451
|
+
const batch = pending;
|
|
17452
|
+
pending = {};
|
|
17453
|
+
if (Object.keys(batch).length === 0) return;
|
|
17454
|
+
setEnrichedRuns((prev) => ({ ...prev, ...batch }));
|
|
17455
|
+
};
|
|
17456
|
+
const queuePatch = (id, patch2) => {
|
|
17457
|
+
pending[id] = patch2;
|
|
17458
|
+
if (!flushTimer) flushTimer = setTimeout(flush, 250);
|
|
17459
|
+
};
|
|
17460
|
+
const needsFetch = [];
|
|
17461
|
+
for (const r of runs) {
|
|
17462
|
+
const id = r.execution_id;
|
|
17463
|
+
if (!id || enrichedRunsRef.current[id] || processedRunIdsRef.current.has(id)) continue;
|
|
17464
|
+
if (id.startsWith("demo-") || id.startsWith("dummy-")) {
|
|
17465
|
+
processedRunIdsRef.current.add(id);
|
|
17466
|
+
continue;
|
|
17467
|
+
}
|
|
17468
|
+
const native = buildPatchFromRun(r);
|
|
17469
|
+
if (native) {
|
|
17470
|
+
processedRunIdsRef.current.add(id);
|
|
17471
|
+
queuePatch(id, native);
|
|
17472
|
+
} else {
|
|
17473
|
+
needsFetch.push(id);
|
|
17474
|
+
}
|
|
17662
17475
|
}
|
|
17663
|
-
|
|
17664
|
-
|
|
17665
|
-
|
|
17666
|
-
|
|
17667
|
-
|
|
17668
|
-
|
|
17669
|
-
|
|
17670
|
-
|
|
17671
|
-
|
|
17672
|
-
|
|
17673
|
-
|
|
17674
|
-
|
|
17675
|
-
|
|
17676
|
-
|
|
17677
|
-
|
|
17678
|
-
|
|
17679
|
-
|
|
17680
|
-
|
|
17681
|
-
|
|
17682
|
-
|
|
17683
|
-
|
|
17684
|
-
|
|
17685
|
-
|
|
17686
|
-
|
|
17687
|
-
|
|
17688
|
-
|
|
17689
|
-
|
|
17690
|
-
|
|
17691
|
-
|
|
17692
|
-
|
|
17693
|
-
}
|
|
17694
|
-
|
|
17695
|
-
|
|
17696
|
-
|
|
17697
|
-
|
|
17698
|
-
|
|
17699
|
-
|
|
17700
|
-
|
|
17701
|
-
|
|
17702
|
-
|
|
17703
|
-
|
|
17704
|
-
|
|
17705
|
-
|
|
17706
|
-
|
|
17707
|
-
|
|
17708
|
-
|
|
17709
|
-
}
|
|
17710
|
-
|
|
17711
|
-
|
|
17712
|
-
|
|
17713
|
-
|
|
17714
|
-
}
|
|
17715
|
-
|
|
17716
|
-
|
|
17717
|
-
|
|
17718
|
-
|
|
17719
|
-
|
|
17720
|
-
|
|
17721
|
-
|
|
17722
|
-
|
|
17723
|
-
|
|
17724
|
-
|
|
17725
|
-
}
|
|
17726
|
-
};
|
|
17727
|
-
var getActiveDocSelectedText = () => {
|
|
17728
|
-
const scope2 = getGlobalScope2();
|
|
17729
|
-
if (!scope2) return null;
|
|
17730
|
-
const sel = scope2[GLOBAL_DOC_SELECTION_KEY] || scope2[GLOBAL_DOC_KEY]?.selectedText;
|
|
17731
|
-
return typeof sel === "string" && sel.trim().length > 0 ? sel.trim() : null;
|
|
17732
|
-
};
|
|
17733
|
-
var getActiveDocPromptContext = () => {
|
|
17734
|
-
const scope2 = getGlobalScope2();
|
|
17735
|
-
if (!scope2) return null;
|
|
17736
|
-
const info = scope2[GLOBAL_DOC_KEY];
|
|
17737
|
-
if (info && typeof info === "object") {
|
|
17738
|
-
return info;
|
|
17739
|
-
}
|
|
17740
|
-
return null;
|
|
17741
|
-
};
|
|
17742
|
-
var isDocsRoute = (pathname) => {
|
|
17743
|
-
if (!pathname) return false;
|
|
17744
|
-
return pathname === "/docs" || pathname.startsWith("/docs/") || pathname === "/legal" || pathname.startsWith("/legal/") || pathname === "/articles" || pathname.startsWith("/articles/");
|
|
17745
|
-
};
|
|
17746
|
-
var DOC_PROMPT_DELIMITER_START = "--- BEGIN DOCUMENTATION ---";
|
|
17747
|
-
var DOC_PROMPT_DELIMITER_END = "--- END DOCUMENTATION ---";
|
|
17748
|
-
var DOC_PROMPT_PREFIX_REGEX = /^Answer the users? question about (?:["']?)(.*?)(?:["']?) based on(?: on)? the following (?:documentation )?content:/i;
|
|
17749
|
-
var sanitizeDocMarkdown = (markdown, maxChars = 35e3) => {
|
|
17750
|
-
if (!markdown || typeof markdown !== "string") return "";
|
|
17751
|
-
let cleaned = markdown;
|
|
17752
|
-
cleaned = cleaned.replace(/^---\s*\r?\n[\s\S]*?\r?\n---\s*(?:\r?\n|$)/, "");
|
|
17753
|
-
cleaned = stripInContentToc(cleaned);
|
|
17754
|
-
cleaned = cleaned.replace(/<!--[\s\S]*?-->/g, "");
|
|
17755
|
-
cleaned = cleaned.replace(/<(script|style|iframe)[^>]*>[\s\S]*?<\/\1>/gi, "");
|
|
17756
|
-
cleaned = cleaned.replace(/data:image\/[a-zA-Z0-9+.-]+;base64,[A-Za-z0-9+/=]+/g, "[embedded image]");
|
|
17757
|
-
cleaned = cleaned.replace(/\n{3,}/g, "\n\n");
|
|
17758
|
-
cleaned = cleaned.trim();
|
|
17759
|
-
if (cleaned.length > maxChars) {
|
|
17760
|
-
const cut = cleaned.lastIndexOf("\n\n", maxChars);
|
|
17761
|
-
const splitIndex = cut > maxChars * 0.7 ? cut : maxChars;
|
|
17762
|
-
cleaned = `${cleaned.slice(0, splitIndex).trim()}
|
|
17763
|
-
|
|
17764
|
-
[Documentation content truncated for length...]`;
|
|
17765
|
-
}
|
|
17766
|
-
return cleaned;
|
|
17767
|
-
};
|
|
17768
|
-
var isDocInjectedPrompt = (prompt) => {
|
|
17769
|
-
if (!prompt || typeof prompt !== "string") return false;
|
|
17770
|
-
return prompt.includes(DOC_PROMPT_DELIMITER_START) || DOC_PROMPT_PREFIX_REGEX.test(prompt);
|
|
17771
|
-
};
|
|
17772
|
-
var extractCleanDisplayPrompt = (prompt) => {
|
|
17773
|
-
if (!prompt || typeof prompt !== "string") return "";
|
|
17774
|
-
let trimmed = prompt.trim();
|
|
17775
|
-
if (isUsecaseInjectedPrompt(trimmed)) {
|
|
17776
|
-
trimmed = extractCleanUsecasePrompt(trimmed);
|
|
17777
|
-
}
|
|
17778
|
-
if (isIncidentInjectedPrompt(trimmed)) {
|
|
17779
|
-
return extractCleanIncidentPrompt(trimmed);
|
|
17780
|
-
}
|
|
17781
|
-
if (!isDocInjectedPrompt(trimmed)) return trimmed;
|
|
17782
|
-
if (trimmed.includes(DOC_PROMPT_DELIMITER_END)) {
|
|
17783
|
-
const parts = trimmed.split(DOC_PROMPT_DELIMITER_END);
|
|
17784
|
-
const after = parts[parts.length - 1]?.trim() || "";
|
|
17785
|
-
const cleaned = after.replace(/^(?:User\s+Question:\s*|Question:\s*)/i, "").trim();
|
|
17786
|
-
if (cleaned) return cleaned;
|
|
17787
|
-
}
|
|
17788
|
-
if (DOC_PROMPT_PREFIX_REGEX.test(trimmed)) {
|
|
17789
|
-
const paragraphs = trimmed.split(/\n\s*\n/).filter(Boolean);
|
|
17790
|
-
if (paragraphs.length > 1) {
|
|
17791
|
-
const last = paragraphs[paragraphs.length - 1].trim();
|
|
17792
|
-
const cleaned = last.replace(/^(?:User\s+Question:\s*|Question:\s*)/i, "").trim();
|
|
17793
|
-
if (cleaned) return cleaned;
|
|
17794
|
-
}
|
|
17795
|
-
}
|
|
17796
|
-
return trimmed;
|
|
17797
|
-
};
|
|
17798
|
-
var normalizeDocPromptTitle = (title) => {
|
|
17799
|
-
const t = (title || "").trim();
|
|
17800
|
-
if (!t || t.toLowerCase() === "index" || t.toLowerCase() === "documentation" || t.toLowerCase() === "docs") {
|
|
17801
|
-
return "Shuffle documentation";
|
|
17802
|
-
}
|
|
17803
|
-
const lower = t.toLowerCase();
|
|
17804
|
-
if (lower === "about" || lower === "about shuffle" || lower === "about us" || lower === "about-shuffle" || lower === "about-us") {
|
|
17805
|
-
return "Shuffle";
|
|
17806
|
-
}
|
|
17807
|
-
if (/^about\s+/i.test(t)) {
|
|
17808
|
-
const stripped = t.replace(/^about\s+/i, "").trim();
|
|
17809
|
-
return stripped || "Shuffle";
|
|
17810
|
-
}
|
|
17811
|
-
return t;
|
|
17812
|
-
};
|
|
17813
|
-
var composeDocPromptInput = (userQuestion, pathname, fallbackTitle, overrideDocInfo) => {
|
|
17814
|
-
const trimmedQuestion = (userQuestion || "").trim();
|
|
17815
|
-
if (!trimmedQuestion) return "";
|
|
17816
|
-
if (isDocInjectedPrompt(trimmedQuestion)) {
|
|
17817
|
-
return trimmedQuestion;
|
|
17818
|
-
}
|
|
17819
|
-
const activeDoc = overrideDocInfo ?? getActiveDocPromptContext();
|
|
17820
|
-
const rawTitle = (activeDoc?.title || fallbackTitle || getGlobalScope2()?.__shuffleActiveEntityTitle || "Shuffle documentation").trim();
|
|
17821
|
-
const title = normalizeDocPromptTitle(rawTitle);
|
|
17822
|
-
const groupLabel = activeDoc?.groupLabel;
|
|
17823
|
-
const groupId = activeDoc?.groupId;
|
|
17824
|
-
const currentSlug = (activeDoc?.slug || "").toLowerCase().replace(/_+/g, "-");
|
|
17825
|
-
let categoryDocs = activeDoc?.groupDocs || [];
|
|
17826
|
-
if (categoryDocs.length === 0 && groupId) {
|
|
17827
|
-
categoryDocs = getCachedGroupDocs(groupId);
|
|
17828
|
-
}
|
|
17829
|
-
if (categoryDocs.length === 0 && currentSlug) {
|
|
17830
|
-
const groupDef2 = getDocGroup(currentSlug);
|
|
17831
|
-
if (groupDef2) {
|
|
17832
|
-
categoryDocs = getCachedGroupDocs(groupDef2.id);
|
|
17833
|
-
}
|
|
17834
|
-
}
|
|
17835
|
-
const groupDef = currentSlug ? getDocGroup(currentSlug) : null;
|
|
17836
|
-
const categoryName = groupLabel || groupDef?.label || title;
|
|
17837
|
-
const rawMarkdown = activeDoc?.content || "";
|
|
17838
|
-
const renderedDocs = [];
|
|
17839
|
-
if (categoryDocs.length > 0) {
|
|
17840
|
-
for (const doc of categoryDocs) {
|
|
17841
|
-
const docSlugClean = (doc.slug || "").toLowerCase().replace(/_+/g, "-");
|
|
17842
|
-
const isCurrent = docSlugClean === currentSlug;
|
|
17843
|
-
const contentToUse = isCurrent && rawMarkdown ? rawMarkdown : doc.content;
|
|
17844
|
-
renderedDocs.push({
|
|
17845
|
-
title: doc.title || title,
|
|
17846
|
-
slug: doc.slug,
|
|
17847
|
-
content: sanitizeDocMarkdown(contentToUse, 16e3),
|
|
17848
|
-
isCurrent
|
|
17476
|
+
const CONCURRENCY = 2;
|
|
17477
|
+
let i = 0;
|
|
17478
|
+
const fetchOne = async (executionId) => {
|
|
17479
|
+
try {
|
|
17480
|
+
const resp = await fetch(
|
|
17481
|
+
getApiUrl(
|
|
17482
|
+
`/api/v1/executions/${encodeURIComponent(executionId)}?authorization=${encodeURIComponent(executionId)}`
|
|
17483
|
+
),
|
|
17484
|
+
{
|
|
17485
|
+
method: "GET",
|
|
17486
|
+
credentials: "include",
|
|
17487
|
+
headers: {
|
|
17488
|
+
...apiKey ? { Authorization: `Bearer ${apiKey}` } : getAuthHeader(),
|
|
17489
|
+
...orgId ? { "Org-Id": orgId } : {}
|
|
17490
|
+
}
|
|
17491
|
+
}
|
|
17492
|
+
);
|
|
17493
|
+
if (!resp.ok || cancelled) return;
|
|
17494
|
+
const json = await resp.json().catch(() => null);
|
|
17495
|
+
if (!json || cancelled) return;
|
|
17496
|
+
let decisions;
|
|
17497
|
+
let originalInput;
|
|
17498
|
+
let allowedActions;
|
|
17499
|
+
const agentResult = Array.isArray(json.results) ? json.results.find((r) => r?.action?.app_name === "AI Agent") : null;
|
|
17500
|
+
if (agentResult?.result) {
|
|
17501
|
+
try {
|
|
17502
|
+
const parsed = JSON.parse(agentResult.result);
|
|
17503
|
+
if (Array.isArray(parsed?.decisions)) decisions = parsed.decisions;
|
|
17504
|
+
if (typeof parsed?.original_input === "string") originalInput = parsed.original_input;
|
|
17505
|
+
if (Array.isArray(parsed?.allowed_actions)) allowedActions = parsed.allowed_actions;
|
|
17506
|
+
} catch {
|
|
17507
|
+
}
|
|
17508
|
+
}
|
|
17509
|
+
const patch2 = {
|
|
17510
|
+
results: json.results,
|
|
17511
|
+
execution_argument: json.execution_argument,
|
|
17512
|
+
result: agentResult?.result,
|
|
17513
|
+
decisions,
|
|
17514
|
+
allowed_actions: allowedActions
|
|
17515
|
+
};
|
|
17516
|
+
if (originalInput && !patch2.execution_argument) {
|
|
17517
|
+
patch2.execution_argument = JSON.stringify({ original_input: originalInput });
|
|
17518
|
+
}
|
|
17519
|
+
queuePatch(executionId, patch2);
|
|
17520
|
+
} catch {
|
|
17521
|
+
}
|
|
17522
|
+
};
|
|
17523
|
+
const idle = (fn) => {
|
|
17524
|
+
const ric = window.requestIdleCallback;
|
|
17525
|
+
if (ric) ric(fn, { timeout: 1e3 });
|
|
17526
|
+
else setTimeout(fn, 32);
|
|
17527
|
+
};
|
|
17528
|
+
const nextIdle = () => new Promise((resolve) => idle(resolve));
|
|
17529
|
+
if (needsFetch.length > 0) {
|
|
17530
|
+
for (const id of needsFetch) processedRunIdsRef.current.add(id);
|
|
17531
|
+
const workers = Array.from({ length: Math.min(CONCURRENCY, needsFetch.length) }, async () => {
|
|
17532
|
+
while (!cancelled && i < needsFetch.length) {
|
|
17533
|
+
const id = needsFetch[i++];
|
|
17534
|
+
await nextIdle();
|
|
17535
|
+
if (cancelled) return;
|
|
17536
|
+
await fetchOne(id);
|
|
17537
|
+
}
|
|
17849
17538
|
});
|
|
17539
|
+
Promise.all(workers).catch(() => {
|
|
17540
|
+
});
|
|
17541
|
+
}
|
|
17542
|
+
return () => {
|
|
17543
|
+
cancelled = true;
|
|
17544
|
+
if (flushTimer) clearTimeout(flushTimer);
|
|
17545
|
+
};
|
|
17546
|
+
}, [runs, apiKey, orgId]);
|
|
17547
|
+
const mergedRuns = runs.map((r) => {
|
|
17548
|
+
const patch2 = r.execution_id ? enrichedRuns[r.execution_id] : void 0;
|
|
17549
|
+
const merged = patch2 ? { ...r, ...patch2 } : r;
|
|
17550
|
+
return r.execution_id && abortedIds.has(r.execution_id) ? { ...merged, status: "ABORTED" } : merged;
|
|
17551
|
+
});
|
|
17552
|
+
const loadMore = (0, import_react18.useCallback)(() => {
|
|
17553
|
+
if (cursor && !isLoading) fetchRuns(true, cursor);
|
|
17554
|
+
}, [cursor, isLoading, fetchRuns]);
|
|
17555
|
+
const activeUsecase = usecaseFilters.find((u) => u.id === usecaseFilter) || null;
|
|
17556
|
+
const usecaseMatchedRuns = activeUsecase ? mergedRuns.filter((r) => {
|
|
17557
|
+
const hay = [
|
|
17558
|
+
r.execution_source,
|
|
17559
|
+
r.workflow?.name,
|
|
17560
|
+
r.workflow?.description,
|
|
17561
|
+
r.execution_argument,
|
|
17562
|
+
...(r.workflow?.actions || []).flatMap((a) => [a.app_name, a.label])
|
|
17563
|
+
].filter(Boolean).join(" ").toLowerCase();
|
|
17564
|
+
return activeUsecase.matchTokens.some((t) => hay.includes(t.toLowerCase()));
|
|
17565
|
+
}) : mergedRuns;
|
|
17566
|
+
const filteredRuns = debouncedQuery ? usecaseMatchedRuns.filter((r) => {
|
|
17567
|
+
const hay = [
|
|
17568
|
+
getRunTitle(r),
|
|
17569
|
+
getRunSubtitle(r),
|
|
17570
|
+
r.execution_id,
|
|
17571
|
+
r.execution_source,
|
|
17572
|
+
r.status
|
|
17573
|
+
].filter(Boolean).join(" ").toLowerCase();
|
|
17574
|
+
return hay.includes(debouncedQuery.toLowerCase());
|
|
17575
|
+
}) : usecaseMatchedRuns;
|
|
17576
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
17577
|
+
import_material17.Box,
|
|
17578
|
+
{
|
|
17579
|
+
className,
|
|
17580
|
+
sx: [{ display: "flex", flexDirection: "column", gap: 2 }, ...Array.isArray(sx) ? sx : sx ? [sx] : []],
|
|
17581
|
+
children: [
|
|
17582
|
+
(showSearchBar || showStatusChips) && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: [{ display: "flex", alignItems: "center", justifyContent: "center", gap: 1.5, flexWrap: "wrap" }, ...Array.isArray(toolbarSx) ? toolbarSx : toolbarSx ? [toolbarSx] : []], children: [
|
|
17583
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
17584
|
+
import_material17.Select,
|
|
17585
|
+
{
|
|
17586
|
+
size: "small",
|
|
17587
|
+
value: usecaseFilter ? `uc:${usecaseFilter}` : workflowFilter,
|
|
17588
|
+
onChange: (e) => {
|
|
17589
|
+
const val = String(e.target.value);
|
|
17590
|
+
if (val.startsWith("uc:")) {
|
|
17591
|
+
setUsecaseFilter(val.slice(3));
|
|
17592
|
+
setWorkflowFilter("");
|
|
17593
|
+
} else {
|
|
17594
|
+
setUsecaseFilter("");
|
|
17595
|
+
setWorkflowFilter(val);
|
|
17596
|
+
}
|
|
17597
|
+
},
|
|
17598
|
+
displayEmpty: true,
|
|
17599
|
+
renderValue: (val) => {
|
|
17600
|
+
if (!val) return "All Agent runs";
|
|
17601
|
+
if (String(val).startsWith("uc:")) {
|
|
17602
|
+
const uc = usecaseFilters.find((u) => u.id === String(val).slice(3));
|
|
17603
|
+
return uc?.label || "Usecase agent";
|
|
17604
|
+
}
|
|
17605
|
+
const wf = agentWorkflows.find((w) => w.id === val);
|
|
17606
|
+
return wf?.name || "Selected workflow";
|
|
17607
|
+
},
|
|
17608
|
+
sx: {
|
|
17609
|
+
height: 36,
|
|
17610
|
+
minWidth: 200,
|
|
17611
|
+
maxWidth: 260,
|
|
17612
|
+
fontSize: "0.85rem",
|
|
17613
|
+
bgcolor: "hsl(var(--card))",
|
|
17614
|
+
color: "hsl(var(--foreground))",
|
|
17615
|
+
borderRadius: 1.5,
|
|
17616
|
+
"& .MuiOutlinedInput-notchedOutline": { borderColor: "hsl(var(--border))" },
|
|
17617
|
+
"&:hover .MuiOutlinedInput-notchedOutline": { borderColor: "hsl(var(--muted-foreground) / 0.3)" },
|
|
17618
|
+
"&.Mui-focused .MuiOutlinedInput-notchedOutline": { borderColor: "hsl(var(--primary))" }
|
|
17619
|
+
},
|
|
17620
|
+
MenuProps: {
|
|
17621
|
+
slotProps: {
|
|
17622
|
+
paper: {
|
|
17623
|
+
sx: {
|
|
17624
|
+
bgcolor: "hsl(var(--card))",
|
|
17625
|
+
border: "1px solid hsl(var(--border))",
|
|
17626
|
+
color: "hsl(var(--foreground))",
|
|
17627
|
+
maxHeight: 320
|
|
17628
|
+
}
|
|
17629
|
+
}
|
|
17630
|
+
}
|
|
17631
|
+
},
|
|
17632
|
+
children: [
|
|
17633
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.MenuItem, { value: "", sx: { fontSize: "0.85rem" }, children: "All Agent runs" }),
|
|
17634
|
+
usecaseFilters.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Divider, { sx: { borderColor: "hsl(var(--border))", my: 0.5 } }),
|
|
17635
|
+
usecaseFilters.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
17636
|
+
import_material17.Box,
|
|
17637
|
+
{
|
|
17638
|
+
sx: {
|
|
17639
|
+
px: 1.5,
|
|
17640
|
+
py: 0.5,
|
|
17641
|
+
fontSize: "0.7rem",
|
|
17642
|
+
textTransform: "uppercase",
|
|
17643
|
+
letterSpacing: "0.04em",
|
|
17644
|
+
color: "hsl(var(--muted-foreground))",
|
|
17645
|
+
pointerEvents: "none"
|
|
17646
|
+
},
|
|
17647
|
+
children: "Usecase agents"
|
|
17648
|
+
}
|
|
17649
|
+
),
|
|
17650
|
+
usecaseFilters.map((u) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.MenuItem, { value: `uc:${u.id}`, sx: { fontSize: "0.85rem" }, children: u.label }, u.id)),
|
|
17651
|
+
agentWorkflows.length > 0 && usecaseFilters.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Divider, { sx: { borderColor: "hsl(var(--border))", my: 0.5 } }),
|
|
17652
|
+
agentWorkflows.map((w) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.MenuItem, { value: w.id, sx: { fontSize: "0.85rem" }, children: w.name }, w.id)),
|
|
17653
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Divider, { sx: { borderColor: "hsl(var(--border))", my: 0.5 } }),
|
|
17654
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
17655
|
+
import_material17.Box,
|
|
17656
|
+
{
|
|
17657
|
+
sx: {
|
|
17658
|
+
px: 1.5,
|
|
17659
|
+
py: 1,
|
|
17660
|
+
fontSize: "0.72rem",
|
|
17661
|
+
color: "hsl(var(--muted-foreground))",
|
|
17662
|
+
lineHeight: 1.45,
|
|
17663
|
+
whiteSpace: "normal",
|
|
17664
|
+
maxWidth: 280,
|
|
17665
|
+
pointerEvents: "none"
|
|
17666
|
+
},
|
|
17667
|
+
children: "Scheduled agent runs will appear in this list once you set them up."
|
|
17668
|
+
}
|
|
17669
|
+
)
|
|
17670
|
+
]
|
|
17671
|
+
}
|
|
17672
|
+
),
|
|
17673
|
+
showSearchBar && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
17674
|
+
import_material17.TextField,
|
|
17675
|
+
{
|
|
17676
|
+
placeholder: "Search results...",
|
|
17677
|
+
size: "small",
|
|
17678
|
+
value: searchQuery,
|
|
17679
|
+
onChange: (e) => updateSearchQuery(e.target.value),
|
|
17680
|
+
slotProps: {
|
|
17681
|
+
input: {
|
|
17682
|
+
startAdornment: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.InputAdornment, { position: "start", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Search, { size: 18, color: "hsl(var(--muted-foreground))" }) })
|
|
17683
|
+
}
|
|
17684
|
+
},
|
|
17685
|
+
sx: {
|
|
17686
|
+
flex: 1,
|
|
17687
|
+
maxWidth: 280,
|
|
17688
|
+
minWidth: 160,
|
|
17689
|
+
"& .MuiOutlinedInput-root": {
|
|
17690
|
+
bgcolor: "hsl(var(--card))",
|
|
17691
|
+
borderRadius: 1.5,
|
|
17692
|
+
fontSize: "0.85rem",
|
|
17693
|
+
height: 36,
|
|
17694
|
+
"& fieldset": { borderColor: "hsl(var(--border))" },
|
|
17695
|
+
"&:hover fieldset": { borderColor: "hsl(var(--muted-foreground) / 0.3)" },
|
|
17696
|
+
"&.Mui-focused fieldset": { borderColor: "hsl(var(--primary))" }
|
|
17697
|
+
},
|
|
17698
|
+
"& .MuiInputBase-input": { color: "hsl(var(--foreground))" }
|
|
17699
|
+
}
|
|
17700
|
+
}
|
|
17701
|
+
),
|
|
17702
|
+
showStatusChips && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
17703
|
+
SegmentedControl,
|
|
17704
|
+
{
|
|
17705
|
+
size: "sm",
|
|
17706
|
+
ariaLabel: "Filter by status",
|
|
17707
|
+
value: statusFilter,
|
|
17708
|
+
onChange: setStatusFilter,
|
|
17709
|
+
options: STATUS_FILTERS.map((f) => ({ value: f.value, label: f.label }))
|
|
17710
|
+
}
|
|
17711
|
+
),
|
|
17712
|
+
isLoading && runs.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.CircularProgress, { size: 16, sx: { color: "hsl(var(--primary))", ml: 0.5 } })
|
|
17713
|
+
] }),
|
|
17714
|
+
selectedAgentWorkflow && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
17715
|
+
import_material17.Box,
|
|
17716
|
+
{
|
|
17717
|
+
sx: {
|
|
17718
|
+
display: "flex",
|
|
17719
|
+
alignItems: "center",
|
|
17720
|
+
gap: 1.5,
|
|
17721
|
+
p: 1.5,
|
|
17722
|
+
borderRadius: 2,
|
|
17723
|
+
border: "1px solid hsl(var(--border))",
|
|
17724
|
+
bgcolor: "hsl(var(--card))"
|
|
17725
|
+
},
|
|
17726
|
+
children: [
|
|
17727
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: { flex: 1, minWidth: 0 }, children: [
|
|
17728
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Typography, { sx: { fontSize: "0.85rem", fontWeight: 600, color: "hsl(var(--foreground))", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: selectedAgentWorkflow.name }),
|
|
17729
|
+
selectedAgentWorkflow.description && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Typography, { sx: { fontSize: "0.75rem", color: "hsl(var(--muted-foreground))", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: selectedAgentWorkflow.description })
|
|
17730
|
+
] }),
|
|
17731
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
17732
|
+
import_material17.Button,
|
|
17733
|
+
{
|
|
17734
|
+
size: "small",
|
|
17735
|
+
startIcon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react8.Pencil, { size: 14 }),
|
|
17736
|
+
onClick: openEditPrompt,
|
|
17737
|
+
sx: {
|
|
17738
|
+
height: 36,
|
|
17739
|
+
border: "1px solid hsl(var(--border))",
|
|
17740
|
+
borderRadius: 1.5,
|
|
17741
|
+
color: "hsl(var(--foreground))",
|
|
17742
|
+
textTransform: "none",
|
|
17743
|
+
fontSize: "0.8rem",
|
|
17744
|
+
px: 1.5,
|
|
17745
|
+
"&:hover": { bgcolor: "hsl(var(--muted))" }
|
|
17746
|
+
},
|
|
17747
|
+
children: "Edit"
|
|
17748
|
+
}
|
|
17749
|
+
),
|
|
17750
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
17751
|
+
import_material17.Button,
|
|
17752
|
+
{
|
|
17753
|
+
size: "small",
|
|
17754
|
+
startIcon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react8.StopCircle, { size: 14 }),
|
|
17755
|
+
onClick: () => setStopOpen(true),
|
|
17756
|
+
sx: {
|
|
17757
|
+
height: 36,
|
|
17758
|
+
border: "1px solid hsl(var(--severity-critical, 0 72% 55%) / 0.4)",
|
|
17759
|
+
borderRadius: 1.5,
|
|
17760
|
+
color: "hsl(var(--severity-critical, 0 72% 55%))",
|
|
17761
|
+
textTransform: "none",
|
|
17762
|
+
fontSize: "0.8rem",
|
|
17763
|
+
px: 1.5,
|
|
17764
|
+
"&:hover": { bgcolor: "hsla(var(--severity-critical, 0 72% 55%) / 0.08)" }
|
|
17765
|
+
},
|
|
17766
|
+
children: "Stop schedule"
|
|
17767
|
+
}
|
|
17768
|
+
)
|
|
17769
|
+
]
|
|
17770
|
+
}
|
|
17771
|
+
),
|
|
17772
|
+
isLoading && runs.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Box, { sx: { display: "flex", justifyContent: "center", py: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.CircularProgress, { size: 28, sx: { color: "hsl(var(--primary))" } }) }) : error ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: { display: "flex", flexDirection: "column", alignItems: "center", textAlign: "center", py: 6 }, children: [
|
|
17773
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.AlertCircle, { size: 28, style: { color: "hsl(var(--severity-critical, 0 72% 55%))", marginBottom: 8 } }),
|
|
17774
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
17775
|
+
import_material17.Typography,
|
|
17776
|
+
{
|
|
17777
|
+
sx: { color: "hsl(var(--severity-critical, 0 72% 55%))", fontSize: "0.9rem", mb: 1 },
|
|
17778
|
+
children: error
|
|
17779
|
+
}
|
|
17780
|
+
),
|
|
17781
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
17782
|
+
import_material17.Button,
|
|
17783
|
+
{
|
|
17784
|
+
size: "small",
|
|
17785
|
+
onClick: () => fetchRuns(false, ""),
|
|
17786
|
+
sx: { color: "hsl(var(--primary))", textTransform: "none" },
|
|
17787
|
+
children: "Try again"
|
|
17788
|
+
}
|
|
17789
|
+
)
|
|
17790
|
+
] }) : filteredRuns.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: { display: "flex", flexDirection: "column", alignItems: "center", textAlign: "center", py: 8 }, children: [
|
|
17791
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
17792
|
+
import_lucide_react7.Activity,
|
|
17793
|
+
{
|
|
17794
|
+
size: 40,
|
|
17795
|
+
style: { color: "hsl(var(--muted-foreground))", marginBottom: 12 }
|
|
17796
|
+
}
|
|
17797
|
+
),
|
|
17798
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Typography, { sx: { color: "hsl(var(--muted-foreground))", fontSize: "0.9rem" }, children: emptyTitle }),
|
|
17799
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
17800
|
+
import_material17.Typography,
|
|
17801
|
+
{
|
|
17802
|
+
sx: { color: "hsl(var(--muted-foreground))", fontSize: "0.8rem", mt: 0.5, opacity: 0.7 },
|
|
17803
|
+
children: emptySubtitle
|
|
17804
|
+
}
|
|
17805
|
+
)
|
|
17806
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Box, { sx: { display: "flex", flexDirection: "column", gap: 1.5 }, children: [
|
|
17807
|
+
filteredRuns.map((run2, idx) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
17808
|
+
AgentRunRow,
|
|
17809
|
+
{
|
|
17810
|
+
run: run2,
|
|
17811
|
+
onClick: () => {
|
|
17812
|
+
setLastOpenedAgentRun(run2.execution_id);
|
|
17813
|
+
setLastOpenedRunId(run2.execution_id || null);
|
|
17814
|
+
onRunClick?.(run2);
|
|
17815
|
+
},
|
|
17816
|
+
sx: [
|
|
17817
|
+
...Array.isArray(rowSx) ? rowSx : rowSx ? [rowSx] : [],
|
|
17818
|
+
...run2.execution_id && run2.execution_id === lastOpenedRunId ? [{
|
|
17819
|
+
borderColor: "hsl(var(--primary) / 0.55)",
|
|
17820
|
+
bgcolor: "hsl(var(--primary) / 0.06)"
|
|
17821
|
+
}] : []
|
|
17822
|
+
],
|
|
17823
|
+
appIcons,
|
|
17824
|
+
onAppClick: (app2) => setAppDrawer(app2),
|
|
17825
|
+
apiKey,
|
|
17826
|
+
apiBaseUrl,
|
|
17827
|
+
orgId,
|
|
17828
|
+
abortingIds,
|
|
17829
|
+
onAbort: handleAbort
|
|
17830
|
+
},
|
|
17831
|
+
run2.execution_id || idx
|
|
17832
|
+
)),
|
|
17833
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Box, { sx: { display: "flex", justifyContent: "center", mt: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
17834
|
+
import_material17.Button,
|
|
17835
|
+
{
|
|
17836
|
+
size: "small",
|
|
17837
|
+
onClick: loadMore,
|
|
17838
|
+
disabled: isLoading,
|
|
17839
|
+
sx: { color: "hsl(var(--primary))", textTransform: "none", fontSize: "0.8rem" },
|
|
17840
|
+
children: [
|
|
17841
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.CircularProgress, { size: 14, sx: { mr: 1 } }) : null,
|
|
17842
|
+
"Load more"
|
|
17843
|
+
]
|
|
17844
|
+
}
|
|
17845
|
+
) })
|
|
17846
|
+
] }),
|
|
17847
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
17848
|
+
import_material17.Dialog,
|
|
17849
|
+
{
|
|
17850
|
+
open: stopOpen,
|
|
17851
|
+
onClose: () => stopLoading ? null : setStopOpen(false),
|
|
17852
|
+
slotProps: { paper: { sx: { bgcolor: "hsl(var(--card))", color: "hsl(var(--foreground))", border: "1px solid hsl(var(--border))" } } },
|
|
17853
|
+
children: [
|
|
17854
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.DialogTitle, { sx: { fontSize: "1rem", fontWeight: 600 }, children: "Stop schedule?" }),
|
|
17855
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.DialogContent, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.Typography, { sx: { fontSize: "0.85rem", color: "hsl(var(--muted-foreground))" }, children: [
|
|
17856
|
+
'This will stop "',
|
|
17857
|
+
selectedAgentWorkflow?.name,
|
|
17858
|
+
'" and delete the scheduled workflow. Past executions remain visible. This cannot be undone.'
|
|
17859
|
+
] }) }),
|
|
17860
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_material17.DialogActions, { sx: { px: 3, pb: 2 }, children: [
|
|
17861
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.Button, { onClick: () => setStopOpen(false), disabled: stopLoading, sx: { textTransform: "none", color: "hsl(var(--muted-foreground))" }, children: "Cancel" }),
|
|
17862
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
17863
|
+
import_material17.Button,
|
|
17864
|
+
{
|
|
17865
|
+
onClick: confirmStop,
|
|
17866
|
+
disabled: stopLoading,
|
|
17867
|
+
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)" } },
|
|
17868
|
+
children: [
|
|
17869
|
+
stopLoading ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_material17.CircularProgress, { size: 16, sx: { color: "hsl(var(--primary-foreground))", mr: 1 } }) : null,
|
|
17870
|
+
"Stop schedule"
|
|
17871
|
+
]
|
|
17872
|
+
}
|
|
17873
|
+
)
|
|
17874
|
+
] })
|
|
17875
|
+
]
|
|
17876
|
+
}
|
|
17877
|
+
),
|
|
17878
|
+
renderAppDetailDrawer ? renderAppDetailDrawer({
|
|
17879
|
+
open: !!appDrawer,
|
|
17880
|
+
onClose: () => setAppDrawer(null),
|
|
17881
|
+
appName: appDrawer?.name || null,
|
|
17882
|
+
appId: appDrawer?.id || null,
|
|
17883
|
+
activeOrgId: orgId || null,
|
|
17884
|
+
globalUrl: globalUrl || apiBaseUrl,
|
|
17885
|
+
theme,
|
|
17886
|
+
colorMode
|
|
17887
|
+
}) : null
|
|
17888
|
+
]
|
|
17850
17889
|
}
|
|
17851
|
-
|
|
17852
|
-
renderedDocs.push({
|
|
17853
|
-
title,
|
|
17854
|
-
slug: activeDoc?.slug,
|
|
17855
|
-
content: sanitizeDocMarkdown(rawMarkdown, 3e4),
|
|
17856
|
-
isCurrent: true
|
|
17857
|
-
});
|
|
17858
|
-
}
|
|
17859
|
-
let docSection = "";
|
|
17860
|
-
if (renderedDocs.length > 0) {
|
|
17861
|
-
const docBlocks = renderedDocs.filter((d) => d.content && d.content.trim().length > 0).map((d) => {
|
|
17862
|
-
const marker = d.isCurrent ? " (Currently Viewed)" : "";
|
|
17863
|
-
return `## Document: ${d.title}${marker}
|
|
17864
|
-
|
|
17865
|
-
${d.content}`;
|
|
17866
|
-
}).join("\n\n---\n\n");
|
|
17867
|
-
docSection = `# Documentation Category: ${categoryName}
|
|
17868
|
-
|
|
17869
|
-
${docBlocks}`;
|
|
17870
|
-
}
|
|
17871
|
-
const markedExcerpt = (activeDoc?.selectedText || getActiveDocSelectedText() || "").trim();
|
|
17872
|
-
const excerptBlock = markedExcerpt ? `
|
|
17873
|
-
|
|
17874
|
-
## User-Marked Excerpt (Focus of Question)
|
|
17875
|
-
The user highlighted the following text in "${title}":
|
|
17876
|
-
"""
|
|
17877
|
-
${markedExcerpt.slice(0, 4e3)}
|
|
17878
|
-
"""` : "";
|
|
17879
|
-
if (docSection) {
|
|
17880
|
-
return `Answer the users question about Shuffle ${categoryName} based on the following complete documentation for the ${categoryName} category:
|
|
17881
|
-
${DOC_PROMPT_DELIMITER_START}
|
|
17882
|
-
${docSection}${excerptBlock}
|
|
17883
|
-
${DOC_PROMPT_DELIMITER_END}
|
|
17884
|
-
|
|
17885
|
-
User Question:
|
|
17886
|
-
${trimmedQuestion}`;
|
|
17887
|
-
}
|
|
17888
|
-
return `Answer the users question about Shuffle ${categoryName}:${excerptBlock}
|
|
17889
|
-
|
|
17890
|
-
${trimmedQuestion}`;
|
|
17890
|
+
);
|
|
17891
17891
|
};
|
|
17892
|
+
var AgentActivityList_default = AgentActivityList;
|
|
17892
17893
|
|
|
17893
17894
|
// agentChat.ts
|
|
17894
17895
|
var AGENT_WORKFLOW_CHANGED_EVENT = "shuffle:agent_workflow_changed";
|