@getcatalystiq/agent-plane-ui 0.1.15 → 0.1.16
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.cjs +440 -0
- package/dist/index.d.cts +63 -1
- package/dist/index.d.ts +63 -1
- package/dist/index.js +439 -1
- package/package.json +6 -1
package/dist/index.cjs
CHANGED
|
@@ -7,6 +7,7 @@ var swr = require('swr');
|
|
|
7
7
|
var ReactMarkdown = require('react-markdown');
|
|
8
8
|
var cmdk = require('cmdk');
|
|
9
9
|
var Popover = require('@radix-ui/react-popover');
|
|
10
|
+
var remarkGfm = require('remark-gfm');
|
|
10
11
|
|
|
11
12
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
13
|
|
|
@@ -31,6 +32,7 @@ function _interopNamespace(e) {
|
|
|
31
32
|
var React3__namespace = /*#__PURE__*/_interopNamespace(React3);
|
|
32
33
|
var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
|
|
33
34
|
var Popover__namespace = /*#__PURE__*/_interopNamespace(Popover);
|
|
35
|
+
var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
|
|
34
36
|
|
|
35
37
|
function Select({ className = "", ...props }) {
|
|
36
38
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
|
|
@@ -4386,6 +4388,443 @@ function ScheduleCard({
|
|
|
4386
4388
|
] })
|
|
4387
4389
|
] });
|
|
4388
4390
|
}
|
|
4391
|
+
function MarkdownContent({ children }) {
|
|
4392
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-foreground [&_p]:my-1 [&_h1]:text-lg [&_h1]:font-bold [&_h1]:my-2 [&_h2]:text-base [&_h2]:font-semibold [&_h2]:my-2 [&_h3]:text-sm [&_h3]:font-semibold [&_h3]:my-1 [&_ul]:list-disc [&_ul]:pl-5 [&_ul]:my-1 [&_ol]:list-decimal [&_ol]:pl-5 [&_ol]:my-1 [&_li]:my-0.5 [&_pre]:bg-muted [&_pre]:rounded-md [&_pre]:p-3 [&_pre]:overflow-x-auto [&_pre]:text-xs [&_pre]:my-2 [&_code:not(pre_code)]:bg-muted [&_code:not(pre_code)]:px-1 [&_code:not(pre_code)]:py-0.5 [&_code:not(pre_code)]:rounded [&_code:not(pre_code)]:text-xs [&_code:not(pre_code)]:font-mono [&_a]:text-blue-400 [&_a]:underline [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground [&_blockquote]:my-2 [&_hr]:border-border [&_hr]:my-3 [&_table]:border-collapse [&_table]:text-xs [&_table]:w-full [&_th]:border [&_th]:border-border [&_th]:px-2 [&_th]:py-1 [&_th]:text-left [&_th]:font-semibold [&_th]:bg-muted [&_td]:border [&_td]:border-border [&_td]:px-2 [&_td]:py-1 [&_strong]:font-semibold [&_em]:italic", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4393
|
+
ReactMarkdown__default.default,
|
|
4394
|
+
{
|
|
4395
|
+
remarkPlugins: [remarkGfm__default.default],
|
|
4396
|
+
components: {
|
|
4397
|
+
a: ({ href, children: linkChildren }) => /* @__PURE__ */ jsxRuntime.jsx("a", { href, target: "_blank", rel: "noopener noreferrer", children: linkChildren })
|
|
4398
|
+
},
|
|
4399
|
+
children
|
|
4400
|
+
}
|
|
4401
|
+
) });
|
|
4402
|
+
}
|
|
4403
|
+
function CollapsibleJson({ data, maxHeight = "12rem" }) {
|
|
4404
|
+
const [expanded, setExpanded] = React3.useState(false);
|
|
4405
|
+
const json = typeof data === "string" ? data : JSON.stringify(data, null, 2);
|
|
4406
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
4407
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4408
|
+
"pre",
|
|
4409
|
+
{
|
|
4410
|
+
className: `text-xs font-mono text-muted-foreground bg-muted/50 rounded-md p-2 overflow-x-auto whitespace-pre-wrap break-all ${expanded ? "" : "overflow-hidden"}`,
|
|
4411
|
+
style: expanded ? void 0 : { maxHeight },
|
|
4412
|
+
children: json
|
|
4413
|
+
}
|
|
4414
|
+
),
|
|
4415
|
+
json.length > 200 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4416
|
+
"button",
|
|
4417
|
+
{
|
|
4418
|
+
onClick: () => setExpanded(!expanded),
|
|
4419
|
+
className: "text-xs text-blue-400 hover:text-blue-300 mt-1",
|
|
4420
|
+
children: expanded ? "Collapse" : "Expand"
|
|
4421
|
+
}
|
|
4422
|
+
)
|
|
4423
|
+
] });
|
|
4424
|
+
}
|
|
4425
|
+
function renderEvent(event, idx) {
|
|
4426
|
+
if (event.type === "heartbeat") return null;
|
|
4427
|
+
if (event.type === "text_delta") return null;
|
|
4428
|
+
if (event.type === "session_created") return null;
|
|
4429
|
+
if (event.type === "session_info") return null;
|
|
4430
|
+
if (event.type === "mcp_status") return null;
|
|
4431
|
+
if (event.type === "rate_limit_event") return null;
|
|
4432
|
+
if (event.type === "user_message") {
|
|
4433
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-border pt-3 mt-1", children: [
|
|
4434
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-emerald-400 uppercase", children: "You" }),
|
|
4435
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-foreground mt-1 whitespace-pre-wrap", children: String(event.text) })
|
|
4436
|
+
] }, idx);
|
|
4437
|
+
}
|
|
4438
|
+
if (event.type === "assistant") {
|
|
4439
|
+
const content = event.message;
|
|
4440
|
+
const blocks = content?.content ?? [];
|
|
4441
|
+
const textBlocks = blocks.filter((c) => c.type === "text").map((c) => c.text).join("");
|
|
4442
|
+
const toolUseBlocks = blocks.filter((c) => c.type === "tool_use");
|
|
4443
|
+
if (!textBlocks && toolUseBlocks.length === 0) return null;
|
|
4444
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
4445
|
+
textBlocks && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
4446
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-blue-400 uppercase", children: "Assistant" }),
|
|
4447
|
+
/* @__PURE__ */ jsxRuntime.jsx(MarkdownContent, { children: textBlocks })
|
|
4448
|
+
] }),
|
|
4449
|
+
toolUseBlocks.map((tool, ti) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1 ml-3 pl-3 border-l-2 border-yellow-800/50", children: [
|
|
4450
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-semibold text-yellow-400 uppercase", children: [
|
|
4451
|
+
"Tool Call: ",
|
|
4452
|
+
tool.name ?? "unknown"
|
|
4453
|
+
] }),
|
|
4454
|
+
tool.id && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground ml-2 font-mono", children: String(tool.id) }),
|
|
4455
|
+
tool.input != null && /* @__PURE__ */ jsxRuntime.jsx(CollapsibleJson, { data: tool.input })
|
|
4456
|
+
] }, ti))
|
|
4457
|
+
] }, idx);
|
|
4458
|
+
}
|
|
4459
|
+
if (event.type === "tool_use") {
|
|
4460
|
+
const toolName = String(event.tool_name ?? event.name ?? "unknown");
|
|
4461
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1 ml-3 pl-3 border-l-2 border-yellow-800/50", children: [
|
|
4462
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4463
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-yellow-400 uppercase", children: "Tool Call" }),
|
|
4464
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-mono text-yellow-400/80", children: toolName }),
|
|
4465
|
+
event.tool_use_id ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground font-mono", children: String(event.tool_use_id) }) : null
|
|
4466
|
+
] }),
|
|
4467
|
+
event.input != null ? /* @__PURE__ */ jsxRuntime.jsx(CollapsibleJson, { data: event.input }) : null
|
|
4468
|
+
] }, idx);
|
|
4469
|
+
}
|
|
4470
|
+
if (event.type === "tool_result") {
|
|
4471
|
+
const isError = event.is_error === true || event.error === true;
|
|
4472
|
+
const content = event.output ?? event.content ?? "";
|
|
4473
|
+
const contentStr = typeof content === "string" ? content : JSON.stringify(content, null, 2);
|
|
4474
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `space-y-1 ml-3 pl-3 border-l-2 ${isError ? "border-red-800/50" : "border-green-800/50"}`, children: [
|
|
4475
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4476
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-xs font-semibold uppercase ${isError ? "text-red-400" : "text-green-400"}`, children: isError ? "Tool Error" : "Tool Result" }),
|
|
4477
|
+
event.tool_name ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-mono text-muted-foreground", children: String(event.tool_name) }) : null,
|
|
4478
|
+
event.tool_use_id ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground font-mono", children: String(event.tool_use_id) }) : null
|
|
4479
|
+
] }),
|
|
4480
|
+
contentStr ? /* @__PURE__ */ jsxRuntime.jsx(CollapsibleJson, { data: contentStr }) : null
|
|
4481
|
+
] }, idx);
|
|
4482
|
+
}
|
|
4483
|
+
if (event.type === "result") {
|
|
4484
|
+
const success = event.subtype === "success";
|
|
4485
|
+
const costUsd = event.cost_usd ?? event.total_cost_usd;
|
|
4486
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `rounded-md px-3 py-2 flex items-center gap-3 ${success ? "bg-green-950 border border-green-900" : "bg-red-950 border border-red-900"}`, children: [
|
|
4487
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-xs font-semibold ${success ? "text-green-400" : "text-red-400"}`, children: success ? "Completed" : "Failed" }),
|
|
4488
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-3 text-xs text-zinc-400", children: [
|
|
4489
|
+
event.num_turns != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
4490
|
+
String(event.num_turns),
|
|
4491
|
+
" turns"
|
|
4492
|
+
] }),
|
|
4493
|
+
costUsd != null && Number(costUsd) > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
4494
|
+
"$",
|
|
4495
|
+
Number(costUsd).toFixed(4)
|
|
4496
|
+
] }),
|
|
4497
|
+
event.duration_ms != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
4498
|
+
(Number(event.duration_ms) / 1e3).toFixed(1),
|
|
4499
|
+
"s"
|
|
4500
|
+
] }),
|
|
4501
|
+
event.duration_api_ms != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
4502
|
+
"API: ",
|
|
4503
|
+
(Number(event.duration_api_ms) / 1e3).toFixed(1),
|
|
4504
|
+
"s"
|
|
4505
|
+
] })
|
|
4506
|
+
] })
|
|
4507
|
+
] }, idx);
|
|
4508
|
+
}
|
|
4509
|
+
if (event.type === "error") {
|
|
4510
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-md p-3 bg-red-950 border border-red-800", children: [
|
|
4511
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4512
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-red-400", children: "Error" }),
|
|
4513
|
+
event.code ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-mono text-red-400/70", children: String(event.code) }) : null
|
|
4514
|
+
] }),
|
|
4515
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-foreground mt-1", children: String(event.error ?? "Unknown error") })
|
|
4516
|
+
] }, idx);
|
|
4517
|
+
}
|
|
4518
|
+
if (event.type === "stream_detached") {
|
|
4519
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground italic border-t border-border pt-2", children: [
|
|
4520
|
+
"Stream detached at ",
|
|
4521
|
+
event.timestamp ? new Date(String(event.timestamp)).toLocaleTimeString() : "unknown",
|
|
4522
|
+
" \u2014 run continues in background"
|
|
4523
|
+
] }, idx);
|
|
4524
|
+
}
|
|
4525
|
+
if (event.type === "queued") {
|
|
4526
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground", children: "Queued\u2026" }, idx);
|
|
4527
|
+
}
|
|
4528
|
+
if (event.type === "sandbox_starting") {
|
|
4529
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground", children: "Starting sandbox\u2026" }, idx);
|
|
4530
|
+
}
|
|
4531
|
+
if (event.type === "run_started") {
|
|
4532
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground", children: [
|
|
4533
|
+
"Agent started",
|
|
4534
|
+
event.model ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-2 font-mono text-foreground/60", children: String(event.model) }) : null,
|
|
4535
|
+
event.mcp_server_count != null && Number(event.mcp_server_count) > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-2", children: [
|
|
4536
|
+
String(event.mcp_server_count),
|
|
4537
|
+
" MCP server",
|
|
4538
|
+
Number(event.mcp_server_count) !== 1 ? "s" : ""
|
|
4539
|
+
] })
|
|
4540
|
+
] }, idx);
|
|
4541
|
+
}
|
|
4542
|
+
if (event.type === "system") {
|
|
4543
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground italic", children: String(event.message ?? JSON.stringify(event)) }, idx);
|
|
4544
|
+
}
|
|
4545
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
4546
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-purple-400 uppercase", children: event.type }),
|
|
4547
|
+
/* @__PURE__ */ jsxRuntime.jsx(CollapsibleJson, { data: event, maxHeight: "8rem" })
|
|
4548
|
+
] }, idx);
|
|
4549
|
+
}
|
|
4550
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled", "timed_out"]);
|
|
4551
|
+
function PlaygroundPage({ agentId }) {
|
|
4552
|
+
const client = chunk4XBBDUSZ_cjs.useAgentPlaneClient();
|
|
4553
|
+
const { LinkComponent, basePath } = chunk4XBBDUSZ_cjs.useNavigation();
|
|
4554
|
+
const { data: agent, error: agentError, isLoading } = chunk4XBBDUSZ_cjs.useApi(
|
|
4555
|
+
`agent-${agentId}`,
|
|
4556
|
+
(c) => c.agents.get(agentId)
|
|
4557
|
+
);
|
|
4558
|
+
const [prompt, setPrompt] = React3.useState("");
|
|
4559
|
+
const [events, setEvents] = React3.useState([]);
|
|
4560
|
+
const [streamingText, setStreamingText] = React3.useState("");
|
|
4561
|
+
const [running, setRunning] = React3.useState(false);
|
|
4562
|
+
const [polling, setPolling] = React3.useState(false);
|
|
4563
|
+
const [error, setError] = React3.useState(null);
|
|
4564
|
+
const [sessionId, setSessionId] = React3.useState(null);
|
|
4565
|
+
const sessionIdRef = React3.useRef(null);
|
|
4566
|
+
const abortRef = React3.useRef(null);
|
|
4567
|
+
const runIdRef = React3.useRef(null);
|
|
4568
|
+
const streamRef = React3.useRef(null);
|
|
4569
|
+
const scrollRef = React3.useRef(null);
|
|
4570
|
+
const textareaRef = React3.useRef(null);
|
|
4571
|
+
React3.useEffect(() => {
|
|
4572
|
+
const el = scrollRef.current;
|
|
4573
|
+
if (el) el.scrollTop = el.scrollHeight;
|
|
4574
|
+
}, [events, streamingText]);
|
|
4575
|
+
React3.useEffect(() => {
|
|
4576
|
+
return () => {
|
|
4577
|
+
abortRef.current?.abort();
|
|
4578
|
+
streamRef.current?.abort();
|
|
4579
|
+
};
|
|
4580
|
+
}, []);
|
|
4581
|
+
const pollForFinalResult = React3.useCallback(async (runId) => {
|
|
4582
|
+
setPolling(true);
|
|
4583
|
+
let delay = 3e3;
|
|
4584
|
+
const maxDelay = 1e4;
|
|
4585
|
+
try {
|
|
4586
|
+
while (true) {
|
|
4587
|
+
if (abortRef.current?.signal.aborted) break;
|
|
4588
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
4589
|
+
if (abortRef.current?.signal.aborted) break;
|
|
4590
|
+
try {
|
|
4591
|
+
const run = await client.runs.get(runId);
|
|
4592
|
+
if (TERMINAL_STATUSES.has(run.status)) {
|
|
4593
|
+
try {
|
|
4594
|
+
const transcriptEvents = await client.runs.transcriptArray(runId);
|
|
4595
|
+
if (transcriptEvents.length > 0) {
|
|
4596
|
+
const detachIdx = transcriptEvents.findIndex((ev) => ev.type === "stream_detached");
|
|
4597
|
+
const eventsAfterDetach = detachIdx >= 0 ? transcriptEvents.slice(detachIdx + 1) : [];
|
|
4598
|
+
const newEvents = eventsAfterDetach.filter(
|
|
4599
|
+
(ev) => ev.type !== "heartbeat" && ev.type !== "text_delta" && ev.type !== "run_started" && ev.type !== "queued" && ev.type !== "sandbox_starting"
|
|
4600
|
+
);
|
|
4601
|
+
if (newEvents.length > 0) {
|
|
4602
|
+
setEvents((prev) => [...prev, ...newEvents]);
|
|
4603
|
+
}
|
|
4604
|
+
}
|
|
4605
|
+
} catch {
|
|
4606
|
+
}
|
|
4607
|
+
setEvents((prev) => {
|
|
4608
|
+
if (prev.some((ev) => ev.type === "result")) return prev;
|
|
4609
|
+
const syntheticResult = {
|
|
4610
|
+
type: "result",
|
|
4611
|
+
subtype: run.status === "completed" ? "success" : "failed",
|
|
4612
|
+
cost_usd: run.cost_usd,
|
|
4613
|
+
num_turns: run.num_turns,
|
|
4614
|
+
duration_ms: run.duration_ms
|
|
4615
|
+
};
|
|
4616
|
+
if (run.error_type) {
|
|
4617
|
+
syntheticResult.result = run.error_type;
|
|
4618
|
+
}
|
|
4619
|
+
return [...prev, syntheticResult];
|
|
4620
|
+
});
|
|
4621
|
+
break;
|
|
4622
|
+
}
|
|
4623
|
+
delay = Math.min(delay * 2, maxDelay);
|
|
4624
|
+
} catch (err) {
|
|
4625
|
+
if (err?.name === "AbortError") break;
|
|
4626
|
+
delay = Math.min(delay * 2, maxDelay);
|
|
4627
|
+
}
|
|
4628
|
+
}
|
|
4629
|
+
} finally {
|
|
4630
|
+
setPolling(false);
|
|
4631
|
+
setRunning(false);
|
|
4632
|
+
abortRef.current = null;
|
|
4633
|
+
runIdRef.current = null;
|
|
4634
|
+
streamRef.current = null;
|
|
4635
|
+
}
|
|
4636
|
+
}, [client]);
|
|
4637
|
+
const consumeStream = React3.useCallback(async (stream) => {
|
|
4638
|
+
streamRef.current = stream;
|
|
4639
|
+
let handedOffToPoll = false;
|
|
4640
|
+
try {
|
|
4641
|
+
for await (const event of stream) {
|
|
4642
|
+
const ev = event;
|
|
4643
|
+
if (ev.type === "session_created" && ev.session_id) {
|
|
4644
|
+
const sid = ev.session_id;
|
|
4645
|
+
sessionIdRef.current = sid;
|
|
4646
|
+
setSessionId(sid);
|
|
4647
|
+
}
|
|
4648
|
+
if (ev.type === "run_started" && ev.run_id) {
|
|
4649
|
+
runIdRef.current = ev.run_id;
|
|
4650
|
+
}
|
|
4651
|
+
if (ev.type === "text_delta") {
|
|
4652
|
+
setStreamingText((prev) => prev + (ev.text ?? ""));
|
|
4653
|
+
} else if (ev.type === "stream_detached") {
|
|
4654
|
+
setStreamingText("");
|
|
4655
|
+
setEvents((prev) => [...prev, ev]);
|
|
4656
|
+
if (runIdRef.current) {
|
|
4657
|
+
handedOffToPoll = true;
|
|
4658
|
+
pollForFinalResult(runIdRef.current);
|
|
4659
|
+
return;
|
|
4660
|
+
}
|
|
4661
|
+
} else {
|
|
4662
|
+
if (ev.type === "assistant") setStreamingText("");
|
|
4663
|
+
setEvents((prev) => [...prev, ev]);
|
|
4664
|
+
}
|
|
4665
|
+
}
|
|
4666
|
+
} finally {
|
|
4667
|
+
if (!handedOffToPoll) {
|
|
4668
|
+
setRunning(false);
|
|
4669
|
+
abortRef.current = null;
|
|
4670
|
+
runIdRef.current = null;
|
|
4671
|
+
streamRef.current = null;
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4674
|
+
}, [pollForFinalResult]);
|
|
4675
|
+
const handleSend = React3.useCallback(async () => {
|
|
4676
|
+
if (!prompt.trim() || running) return;
|
|
4677
|
+
const messageText = prompt.trim();
|
|
4678
|
+
setPrompt("");
|
|
4679
|
+
setRunning(true);
|
|
4680
|
+
setStreamingText("");
|
|
4681
|
+
setError(null);
|
|
4682
|
+
setPolling(false);
|
|
4683
|
+
setEvents((prev) => [...prev, { type: "user_message", text: messageText }]);
|
|
4684
|
+
const abort = new AbortController();
|
|
4685
|
+
abortRef.current = abort;
|
|
4686
|
+
try {
|
|
4687
|
+
let stream;
|
|
4688
|
+
const currentSessionId = sessionIdRef.current;
|
|
4689
|
+
if (currentSessionId) {
|
|
4690
|
+
stream = await client.sessions.sendMessage(
|
|
4691
|
+
currentSessionId,
|
|
4692
|
+
{ prompt: messageText },
|
|
4693
|
+
{ signal: abort.signal }
|
|
4694
|
+
);
|
|
4695
|
+
} else {
|
|
4696
|
+
stream = await client.sessions.create(
|
|
4697
|
+
{ agent_id: agentId, prompt: messageText },
|
|
4698
|
+
{ signal: abort.signal }
|
|
4699
|
+
);
|
|
4700
|
+
}
|
|
4701
|
+
await consumeStream(stream);
|
|
4702
|
+
} catch (err) {
|
|
4703
|
+
if (err?.name !== "AbortError") {
|
|
4704
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
4705
|
+
setError(msg);
|
|
4706
|
+
}
|
|
4707
|
+
setRunning(false);
|
|
4708
|
+
abortRef.current = null;
|
|
4709
|
+
runIdRef.current = null;
|
|
4710
|
+
streamRef.current = null;
|
|
4711
|
+
}
|
|
4712
|
+
}, [prompt, running, agentId, client, consumeStream]);
|
|
4713
|
+
function handleNewChat() {
|
|
4714
|
+
abortRef.current?.abort();
|
|
4715
|
+
streamRef.current?.abort();
|
|
4716
|
+
if (sessionId) {
|
|
4717
|
+
client.sessions.stop(sessionId).catch(() => {
|
|
4718
|
+
});
|
|
4719
|
+
}
|
|
4720
|
+
sessionIdRef.current = null;
|
|
4721
|
+
setSessionId(null);
|
|
4722
|
+
setEvents([]);
|
|
4723
|
+
setStreamingText("");
|
|
4724
|
+
setRunning(false);
|
|
4725
|
+
setPolling(false);
|
|
4726
|
+
setError(null);
|
|
4727
|
+
setPrompt("");
|
|
4728
|
+
runIdRef.current = null;
|
|
4729
|
+
abortRef.current = null;
|
|
4730
|
+
streamRef.current = null;
|
|
4731
|
+
textareaRef.current?.focus();
|
|
4732
|
+
}
|
|
4733
|
+
function handleStop() {
|
|
4734
|
+
abortRef.current?.abort();
|
|
4735
|
+
streamRef.current?.abort();
|
|
4736
|
+
const id = runIdRef.current;
|
|
4737
|
+
if (id) {
|
|
4738
|
+
runIdRef.current = null;
|
|
4739
|
+
client.runs.cancel(id).catch(() => {
|
|
4740
|
+
});
|
|
4741
|
+
}
|
|
4742
|
+
}
|
|
4743
|
+
if (isLoading) {
|
|
4744
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col h-[calc(100vh-6rem)]", children: [
|
|
4745
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
|
|
4746
|
+
/* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Skeleton, { className: "h-8 w-24" }),
|
|
4747
|
+
/* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Skeleton, { className: "h-4 w-40" })
|
|
4748
|
+
] }),
|
|
4749
|
+
/* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Skeleton, { className: "flex-1 rounded-lg" }),
|
|
4750
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 space-y-2", children: [
|
|
4751
|
+
/* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Skeleton, { className: "h-24 w-full rounded-lg" }),
|
|
4752
|
+
/* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Skeleton, { className: "h-8 w-20" })
|
|
4753
|
+
] })
|
|
4754
|
+
] });
|
|
4755
|
+
}
|
|
4756
|
+
if (agentError || !agent) {
|
|
4757
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center h-[calc(100vh-6rem)] gap-3", children: [
|
|
4758
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-destructive text-sm", children: agentError?.status === 404 ? "Agent not found." : `Failed to load agent: ${agentError?.message ?? "Unknown error"}` }),
|
|
4759
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4760
|
+
LinkComponent,
|
|
4761
|
+
{
|
|
4762
|
+
href: `${basePath}/agents`,
|
|
4763
|
+
className: "text-sm text-muted-foreground hover:text-foreground",
|
|
4764
|
+
children: "\u2190 Back to agents"
|
|
4765
|
+
}
|
|
4766
|
+
)
|
|
4767
|
+
] });
|
|
4768
|
+
}
|
|
4769
|
+
const hasContent = events.length > 0 || running;
|
|
4770
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col h-[calc(100vh-6rem)]", children: [
|
|
4771
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
|
|
4772
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4773
|
+
LinkComponent,
|
|
4774
|
+
{
|
|
4775
|
+
href: `${basePath}/agents/${agentId}`,
|
|
4776
|
+
className: "text-sm text-muted-foreground hover:text-foreground",
|
|
4777
|
+
children: [
|
|
4778
|
+
"\u2190 ",
|
|
4779
|
+
agent.name
|
|
4780
|
+
]
|
|
4781
|
+
}
|
|
4782
|
+
),
|
|
4783
|
+
(sessionId || events.length > 0) && /* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Button, { onClick: handleNewChat, variant: "outline", size: "sm", disabled: running, children: "New Chat" }),
|
|
4784
|
+
sessionId && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground font-mono", children: [
|
|
4785
|
+
"Session: ",
|
|
4786
|
+
sessionId.slice(0, 12),
|
|
4787
|
+
"\u2026"
|
|
4788
|
+
] })
|
|
4789
|
+
] }),
|
|
4790
|
+
hasContent && /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: scrollRef, className: "flex-1 min-h-0 overflow-y-auto rounded-lg border border-border bg-muted/20 p-4 space-y-4 mb-4", children: [
|
|
4791
|
+
events.map((ev, i) => renderEvent(ev, i)),
|
|
4792
|
+
streamingText && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
4793
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-blue-400 uppercase", children: "Assistant" }),
|
|
4794
|
+
/* @__PURE__ */ jsxRuntime.jsx(MarkdownContent, { children: streamingText }),
|
|
4795
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block w-0.5 h-4 bg-foreground animate-pulse align-text-bottom" })
|
|
4796
|
+
] }),
|
|
4797
|
+
running && !streamingText && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
4798
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "animate-pulse", children: "\u25CF" }),
|
|
4799
|
+
" ",
|
|
4800
|
+
polling ? "Reconnected, streaming updates\u2026" : "Running\u2026"
|
|
4801
|
+
] })
|
|
4802
|
+
] }),
|
|
4803
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 shrink-0", children: [
|
|
4804
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: error }),
|
|
4805
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4806
|
+
Textarea,
|
|
4807
|
+
{
|
|
4808
|
+
ref: textareaRef,
|
|
4809
|
+
placeholder: sessionId ? "Send a follow-up message\u2026" : "Enter your prompt\u2026",
|
|
4810
|
+
value: prompt,
|
|
4811
|
+
onChange: (e) => setPrompt(e.target.value),
|
|
4812
|
+
rows: hasContent ? 3 : 12,
|
|
4813
|
+
disabled: running,
|
|
4814
|
+
className: "font-mono text-sm resize-none",
|
|
4815
|
+
onKeyDown: (e) => {
|
|
4816
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) handleSend();
|
|
4817
|
+
}
|
|
4818
|
+
}
|
|
4819
|
+
),
|
|
4820
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4821
|
+
/* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Button, { onClick: handleSend, disabled: running || !prompt.trim(), size: "sm", children: running ? "Running\u2026" : sessionId ? "Send" : "Run" }),
|
|
4822
|
+
running && /* @__PURE__ */ jsxRuntime.jsx(chunk4XBBDUSZ_cjs.Button, { onClick: handleStop, variant: "outline", size: "sm", children: "Stop" }),
|
|
4823
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground ml-1", children: "\u2318+Enter to send" })
|
|
4824
|
+
] })
|
|
4825
|
+
] })
|
|
4826
|
+
] });
|
|
4827
|
+
}
|
|
4389
4828
|
|
|
4390
4829
|
Object.defineProperty(exports, "AgentPlaneProvider", {
|
|
4391
4830
|
enumerable: true,
|
|
@@ -4486,6 +4925,7 @@ exports.McpServerListPage = McpServerListPage;
|
|
|
4486
4925
|
exports.MetricCard = MetricCard;
|
|
4487
4926
|
exports.ModelSelector = ModelSelector;
|
|
4488
4927
|
exports.PaginationBar = PaginationBar;
|
|
4928
|
+
exports.PlaygroundPage = PlaygroundPage;
|
|
4489
4929
|
exports.PluginDetailPage = PluginDetailPage;
|
|
4490
4930
|
exports.PluginMarketplaceDetailPage = PluginMarketplaceDetailPage;
|
|
4491
4931
|
exports.PluginMarketplaceListPage = PluginMarketplaceListPage;
|
package/dist/index.d.cts
CHANGED
|
@@ -7,6 +7,52 @@ import * as class_variance_authority_types from 'class-variance-authority/types'
|
|
|
7
7
|
import { VariantProps } from 'class-variance-authority';
|
|
8
8
|
import { DailyAgentStat } from './charts.cjs';
|
|
9
9
|
|
|
10
|
+
/** Minimal stream event types used by the playground UI. */
|
|
11
|
+
interface PlaygroundTextDeltaEvent {
|
|
12
|
+
type: "text_delta";
|
|
13
|
+
text: string;
|
|
14
|
+
}
|
|
15
|
+
interface PlaygroundRunStartedEvent {
|
|
16
|
+
type: "run_started";
|
|
17
|
+
run_id: string;
|
|
18
|
+
agent_id: string;
|
|
19
|
+
model: string;
|
|
20
|
+
timestamp: string;
|
|
21
|
+
}
|
|
22
|
+
interface PlaygroundToolUseEvent {
|
|
23
|
+
type: "tool_use";
|
|
24
|
+
name?: string;
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
interface PlaygroundToolResultEvent {
|
|
28
|
+
type: "tool_result";
|
|
29
|
+
[key: string]: unknown;
|
|
30
|
+
}
|
|
31
|
+
interface PlaygroundResultEvent {
|
|
32
|
+
type: "result";
|
|
33
|
+
subtype: string;
|
|
34
|
+
total_cost_usd?: number;
|
|
35
|
+
num_turns?: number;
|
|
36
|
+
duration_ms?: number;
|
|
37
|
+
}
|
|
38
|
+
interface PlaygroundErrorEvent {
|
|
39
|
+
type: "error";
|
|
40
|
+
error: string;
|
|
41
|
+
code?: string;
|
|
42
|
+
}
|
|
43
|
+
interface PlaygroundSessionCreatedEvent {
|
|
44
|
+
type: "session_created";
|
|
45
|
+
session_id: string;
|
|
46
|
+
}
|
|
47
|
+
type PlaygroundStreamEvent = PlaygroundTextDeltaEvent | PlaygroundRunStartedEvent | PlaygroundToolUseEvent | PlaygroundToolResultEvent | PlaygroundResultEvent | PlaygroundErrorEvent | PlaygroundSessionCreatedEvent | {
|
|
48
|
+
type: string;
|
|
49
|
+
[key: string]: unknown;
|
|
50
|
+
};
|
|
51
|
+
/** Async iterable stream of events (compatible with SDK RunStream). */
|
|
52
|
+
interface PlaygroundStream extends AsyncIterable<PlaygroundStreamEvent> {
|
|
53
|
+
run_id: string | null;
|
|
54
|
+
abort(reason?: unknown): void;
|
|
55
|
+
}
|
|
10
56
|
/**
|
|
11
57
|
* Structural interface for the AgentPlane SDK client.
|
|
12
58
|
* Declares all methods the UI actually uses, avoiding a hard compile-time
|
|
@@ -47,6 +93,17 @@ interface AgentPlaneClient {
|
|
|
47
93
|
list(params?: Record<string, unknown>): Promise<unknown>;
|
|
48
94
|
get(sessionId: string): Promise<unknown>;
|
|
49
95
|
stop(sessionId: string): Promise<unknown>;
|
|
96
|
+
create(params: {
|
|
97
|
+
agent_id: string;
|
|
98
|
+
prompt?: string;
|
|
99
|
+
}, options?: {
|
|
100
|
+
signal?: AbortSignal;
|
|
101
|
+
}): Promise<unknown | PlaygroundStream>;
|
|
102
|
+
sendMessage(sessionId: string, params: {
|
|
103
|
+
prompt: string;
|
|
104
|
+
}, options?: {
|
|
105
|
+
signal?: AbortSignal;
|
|
106
|
+
}): Promise<PlaygroundStream>;
|
|
50
107
|
};
|
|
51
108
|
connectors: {
|
|
52
109
|
list(agentId: string): Promise<unknown[]>;
|
|
@@ -632,6 +689,11 @@ interface Props$1 {
|
|
|
632
689
|
}
|
|
633
690
|
declare function AgentA2aInfo({ agentId, tenantSlug, agentSlug, baseUrl, initialEnabled, initialTags, onChanged, }: Props$1): react_jsx_runtime.JSX.Element;
|
|
634
691
|
|
|
692
|
+
interface PlaygroundPageProps {
|
|
693
|
+
agentId: string;
|
|
694
|
+
}
|
|
695
|
+
declare function PlaygroundPage({ agentId }: PlaygroundPageProps): react_jsx_runtime.JSX.Element;
|
|
696
|
+
|
|
635
697
|
interface ModelSelectorProps {
|
|
636
698
|
value: string;
|
|
637
699
|
onChange: (modelId: string) => void;
|
|
@@ -645,4 +707,4 @@ interface Props {
|
|
|
645
707
|
}
|
|
646
708
|
declare function ToolkitMultiselect({ value, onChange }: Props): react_jsx_runtime.JSX.Element;
|
|
647
709
|
|
|
648
|
-
export { AdminTable, AdminTableHead, AdminTableRow, AgentA2aInfo, AgentConnectorsManager, AgentDetailPage, AgentEditForm, AgentListPage, type AgentPlaneClient, AgentPlaneProvider, type AgentPlaneProviderProps, AgentPluginManager, AgentRuns, AgentScheduleForm, AgentSkillManager, Badge, type BadgeProps, Button, type ButtonProps, Card, CardContent, CardDescription, CardHeader, CardTitle, ConfirmDialog, CopyButton, DashboardPage, type DashboardPageProps, DetailPageHeader, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, EmptyRow, FormError, FormField, Input, type LinkComponentProps, LocalDate, McpServerListPage, type McpServerListPageProps, MetricCard, ModelSelector, type NavigationProps, PaginationBar, PluginDetailPage, type PluginDetailPageProps, PluginMarketplaceDetailPage, type PluginMarketplaceDetailPageProps, PluginMarketplaceListPage, type PluginMarketplaceListPageProps, RunDetailPage, type RunDetailPageProps, RunListPage, type RunListPageProps, RunSourceBadge, RunStatusBadge, SectionHeader, Select, SettingsPage, type SettingsPageProps, Skeleton, Tabs, Textarea, type TextareaProps, Th, ToolkitMultiselect, TranscriptViewer, badgeVariants, buttonVariants, cn, parsePaginationParams, useAgentPlaneClient, useApi, useAuthError, useNavigation };
|
|
710
|
+
export { AdminTable, AdminTableHead, AdminTableRow, AgentA2aInfo, AgentConnectorsManager, AgentDetailPage, AgentEditForm, AgentListPage, type AgentPlaneClient, AgentPlaneProvider, type AgentPlaneProviderProps, AgentPluginManager, AgentRuns, AgentScheduleForm, AgentSkillManager, Badge, type BadgeProps, Button, type ButtonProps, Card, CardContent, CardDescription, CardHeader, CardTitle, ConfirmDialog, CopyButton, DashboardPage, type DashboardPageProps, DetailPageHeader, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, EmptyRow, FormError, FormField, Input, type LinkComponentProps, LocalDate, McpServerListPage, type McpServerListPageProps, MetricCard, ModelSelector, type NavigationProps, PaginationBar, PlaygroundPage, type PlaygroundPageProps, type PlaygroundStream, type PlaygroundStreamEvent, PluginDetailPage, type PluginDetailPageProps, PluginMarketplaceDetailPage, type PluginMarketplaceDetailPageProps, PluginMarketplaceListPage, type PluginMarketplaceListPageProps, RunDetailPage, type RunDetailPageProps, RunListPage, type RunListPageProps, RunSourceBadge, RunStatusBadge, SectionHeader, Select, SettingsPage, type SettingsPageProps, Skeleton, Tabs, Textarea, type TextareaProps, Th, ToolkitMultiselect, TranscriptViewer, badgeVariants, buttonVariants, cn, parsePaginationParams, useAgentPlaneClient, useApi, useAuthError, useNavigation };
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,52 @@ import * as class_variance_authority_types from 'class-variance-authority/types'
|
|
|
7
7
|
import { VariantProps } from 'class-variance-authority';
|
|
8
8
|
import { DailyAgentStat } from './charts.js';
|
|
9
9
|
|
|
10
|
+
/** Minimal stream event types used by the playground UI. */
|
|
11
|
+
interface PlaygroundTextDeltaEvent {
|
|
12
|
+
type: "text_delta";
|
|
13
|
+
text: string;
|
|
14
|
+
}
|
|
15
|
+
interface PlaygroundRunStartedEvent {
|
|
16
|
+
type: "run_started";
|
|
17
|
+
run_id: string;
|
|
18
|
+
agent_id: string;
|
|
19
|
+
model: string;
|
|
20
|
+
timestamp: string;
|
|
21
|
+
}
|
|
22
|
+
interface PlaygroundToolUseEvent {
|
|
23
|
+
type: "tool_use";
|
|
24
|
+
name?: string;
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
interface PlaygroundToolResultEvent {
|
|
28
|
+
type: "tool_result";
|
|
29
|
+
[key: string]: unknown;
|
|
30
|
+
}
|
|
31
|
+
interface PlaygroundResultEvent {
|
|
32
|
+
type: "result";
|
|
33
|
+
subtype: string;
|
|
34
|
+
total_cost_usd?: number;
|
|
35
|
+
num_turns?: number;
|
|
36
|
+
duration_ms?: number;
|
|
37
|
+
}
|
|
38
|
+
interface PlaygroundErrorEvent {
|
|
39
|
+
type: "error";
|
|
40
|
+
error: string;
|
|
41
|
+
code?: string;
|
|
42
|
+
}
|
|
43
|
+
interface PlaygroundSessionCreatedEvent {
|
|
44
|
+
type: "session_created";
|
|
45
|
+
session_id: string;
|
|
46
|
+
}
|
|
47
|
+
type PlaygroundStreamEvent = PlaygroundTextDeltaEvent | PlaygroundRunStartedEvent | PlaygroundToolUseEvent | PlaygroundToolResultEvent | PlaygroundResultEvent | PlaygroundErrorEvent | PlaygroundSessionCreatedEvent | {
|
|
48
|
+
type: string;
|
|
49
|
+
[key: string]: unknown;
|
|
50
|
+
};
|
|
51
|
+
/** Async iterable stream of events (compatible with SDK RunStream). */
|
|
52
|
+
interface PlaygroundStream extends AsyncIterable<PlaygroundStreamEvent> {
|
|
53
|
+
run_id: string | null;
|
|
54
|
+
abort(reason?: unknown): void;
|
|
55
|
+
}
|
|
10
56
|
/**
|
|
11
57
|
* Structural interface for the AgentPlane SDK client.
|
|
12
58
|
* Declares all methods the UI actually uses, avoiding a hard compile-time
|
|
@@ -47,6 +93,17 @@ interface AgentPlaneClient {
|
|
|
47
93
|
list(params?: Record<string, unknown>): Promise<unknown>;
|
|
48
94
|
get(sessionId: string): Promise<unknown>;
|
|
49
95
|
stop(sessionId: string): Promise<unknown>;
|
|
96
|
+
create(params: {
|
|
97
|
+
agent_id: string;
|
|
98
|
+
prompt?: string;
|
|
99
|
+
}, options?: {
|
|
100
|
+
signal?: AbortSignal;
|
|
101
|
+
}): Promise<unknown | PlaygroundStream>;
|
|
102
|
+
sendMessage(sessionId: string, params: {
|
|
103
|
+
prompt: string;
|
|
104
|
+
}, options?: {
|
|
105
|
+
signal?: AbortSignal;
|
|
106
|
+
}): Promise<PlaygroundStream>;
|
|
50
107
|
};
|
|
51
108
|
connectors: {
|
|
52
109
|
list(agentId: string): Promise<unknown[]>;
|
|
@@ -632,6 +689,11 @@ interface Props$1 {
|
|
|
632
689
|
}
|
|
633
690
|
declare function AgentA2aInfo({ agentId, tenantSlug, agentSlug, baseUrl, initialEnabled, initialTags, onChanged, }: Props$1): react_jsx_runtime.JSX.Element;
|
|
634
691
|
|
|
692
|
+
interface PlaygroundPageProps {
|
|
693
|
+
agentId: string;
|
|
694
|
+
}
|
|
695
|
+
declare function PlaygroundPage({ agentId }: PlaygroundPageProps): react_jsx_runtime.JSX.Element;
|
|
696
|
+
|
|
635
697
|
interface ModelSelectorProps {
|
|
636
698
|
value: string;
|
|
637
699
|
onChange: (modelId: string) => void;
|
|
@@ -645,4 +707,4 @@ interface Props {
|
|
|
645
707
|
}
|
|
646
708
|
declare function ToolkitMultiselect({ value, onChange }: Props): react_jsx_runtime.JSX.Element;
|
|
647
709
|
|
|
648
|
-
export { AdminTable, AdminTableHead, AdminTableRow, AgentA2aInfo, AgentConnectorsManager, AgentDetailPage, AgentEditForm, AgentListPage, type AgentPlaneClient, AgentPlaneProvider, type AgentPlaneProviderProps, AgentPluginManager, AgentRuns, AgentScheduleForm, AgentSkillManager, Badge, type BadgeProps, Button, type ButtonProps, Card, CardContent, CardDescription, CardHeader, CardTitle, ConfirmDialog, CopyButton, DashboardPage, type DashboardPageProps, DetailPageHeader, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, EmptyRow, FormError, FormField, Input, type LinkComponentProps, LocalDate, McpServerListPage, type McpServerListPageProps, MetricCard, ModelSelector, type NavigationProps, PaginationBar, PluginDetailPage, type PluginDetailPageProps, PluginMarketplaceDetailPage, type PluginMarketplaceDetailPageProps, PluginMarketplaceListPage, type PluginMarketplaceListPageProps, RunDetailPage, type RunDetailPageProps, RunListPage, type RunListPageProps, RunSourceBadge, RunStatusBadge, SectionHeader, Select, SettingsPage, type SettingsPageProps, Skeleton, Tabs, Textarea, type TextareaProps, Th, ToolkitMultiselect, TranscriptViewer, badgeVariants, buttonVariants, cn, parsePaginationParams, useAgentPlaneClient, useApi, useAuthError, useNavigation };
|
|
710
|
+
export { AdminTable, AdminTableHead, AdminTableRow, AgentA2aInfo, AgentConnectorsManager, AgentDetailPage, AgentEditForm, AgentListPage, type AgentPlaneClient, AgentPlaneProvider, type AgentPlaneProviderProps, AgentPluginManager, AgentRuns, AgentScheduleForm, AgentSkillManager, Badge, type BadgeProps, Button, type ButtonProps, Card, CardContent, CardDescription, CardHeader, CardTitle, ConfirmDialog, CopyButton, DashboardPage, type DashboardPageProps, DetailPageHeader, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, EmptyRow, FormError, FormField, Input, type LinkComponentProps, LocalDate, McpServerListPage, type McpServerListPageProps, MetricCard, ModelSelector, type NavigationProps, PaginationBar, PlaygroundPage, type PlaygroundPageProps, type PlaygroundStream, type PlaygroundStreamEvent, PluginDetailPage, type PluginDetailPageProps, PluginMarketplaceDetailPage, type PluginMarketplaceDetailPageProps, PluginMarketplaceListPage, type PluginMarketplaceListPageProps, RunDetailPage, type RunDetailPageProps, RunListPage, type RunListPageProps, RunSourceBadge, RunStatusBadge, SectionHeader, Select, SettingsPage, type SettingsPageProps, Skeleton, Tabs, Textarea, type TextareaProps, Th, ToolkitMultiselect, TranscriptViewer, badgeVariants, buttonVariants, cn, parsePaginationParams, useAgentPlaneClient, useApi, useAuthError, useNavigation };
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { useSWRConfig } from 'swr';
|
|
|
7
7
|
import ReactMarkdown from 'react-markdown';
|
|
8
8
|
import { Command } from 'cmdk';
|
|
9
9
|
import * as Popover from '@radix-ui/react-popover';
|
|
10
|
+
import remarkGfm from 'remark-gfm';
|
|
10
11
|
|
|
11
12
|
function Select({ className = "", ...props }) {
|
|
12
13
|
return /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
|
|
@@ -4362,5 +4363,442 @@ function ScheduleCard({
|
|
|
4362
4363
|
] })
|
|
4363
4364
|
] });
|
|
4364
4365
|
}
|
|
4366
|
+
function MarkdownContent({ children }) {
|
|
4367
|
+
return /* @__PURE__ */ jsx("div", { className: "text-sm text-foreground [&_p]:my-1 [&_h1]:text-lg [&_h1]:font-bold [&_h1]:my-2 [&_h2]:text-base [&_h2]:font-semibold [&_h2]:my-2 [&_h3]:text-sm [&_h3]:font-semibold [&_h3]:my-1 [&_ul]:list-disc [&_ul]:pl-5 [&_ul]:my-1 [&_ol]:list-decimal [&_ol]:pl-5 [&_ol]:my-1 [&_li]:my-0.5 [&_pre]:bg-muted [&_pre]:rounded-md [&_pre]:p-3 [&_pre]:overflow-x-auto [&_pre]:text-xs [&_pre]:my-2 [&_code:not(pre_code)]:bg-muted [&_code:not(pre_code)]:px-1 [&_code:not(pre_code)]:py-0.5 [&_code:not(pre_code)]:rounded [&_code:not(pre_code)]:text-xs [&_code:not(pre_code)]:font-mono [&_a]:text-blue-400 [&_a]:underline [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground [&_blockquote]:my-2 [&_hr]:border-border [&_hr]:my-3 [&_table]:border-collapse [&_table]:text-xs [&_table]:w-full [&_th]:border [&_th]:border-border [&_th]:px-2 [&_th]:py-1 [&_th]:text-left [&_th]:font-semibold [&_th]:bg-muted [&_td]:border [&_td]:border-border [&_td]:px-2 [&_td]:py-1 [&_strong]:font-semibold [&_em]:italic", children: /* @__PURE__ */ jsx(
|
|
4368
|
+
ReactMarkdown,
|
|
4369
|
+
{
|
|
4370
|
+
remarkPlugins: [remarkGfm],
|
|
4371
|
+
components: {
|
|
4372
|
+
a: ({ href, children: linkChildren }) => /* @__PURE__ */ jsx("a", { href, target: "_blank", rel: "noopener noreferrer", children: linkChildren })
|
|
4373
|
+
},
|
|
4374
|
+
children
|
|
4375
|
+
}
|
|
4376
|
+
) });
|
|
4377
|
+
}
|
|
4378
|
+
function CollapsibleJson({ data, maxHeight = "12rem" }) {
|
|
4379
|
+
const [expanded, setExpanded] = useState(false);
|
|
4380
|
+
const json = typeof data === "string" ? data : JSON.stringify(data, null, 2);
|
|
4381
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
4382
|
+
/* @__PURE__ */ jsx(
|
|
4383
|
+
"pre",
|
|
4384
|
+
{
|
|
4385
|
+
className: `text-xs font-mono text-muted-foreground bg-muted/50 rounded-md p-2 overflow-x-auto whitespace-pre-wrap break-all ${expanded ? "" : "overflow-hidden"}`,
|
|
4386
|
+
style: expanded ? void 0 : { maxHeight },
|
|
4387
|
+
children: json
|
|
4388
|
+
}
|
|
4389
|
+
),
|
|
4390
|
+
json.length > 200 && /* @__PURE__ */ jsx(
|
|
4391
|
+
"button",
|
|
4392
|
+
{
|
|
4393
|
+
onClick: () => setExpanded(!expanded),
|
|
4394
|
+
className: "text-xs text-blue-400 hover:text-blue-300 mt-1",
|
|
4395
|
+
children: expanded ? "Collapse" : "Expand"
|
|
4396
|
+
}
|
|
4397
|
+
)
|
|
4398
|
+
] });
|
|
4399
|
+
}
|
|
4400
|
+
function renderEvent(event, idx) {
|
|
4401
|
+
if (event.type === "heartbeat") return null;
|
|
4402
|
+
if (event.type === "text_delta") return null;
|
|
4403
|
+
if (event.type === "session_created") return null;
|
|
4404
|
+
if (event.type === "session_info") return null;
|
|
4405
|
+
if (event.type === "mcp_status") return null;
|
|
4406
|
+
if (event.type === "rate_limit_event") return null;
|
|
4407
|
+
if (event.type === "user_message") {
|
|
4408
|
+
return /* @__PURE__ */ jsxs("div", { className: "border-t border-border pt-3 mt-1", children: [
|
|
4409
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-emerald-400 uppercase", children: "You" }),
|
|
4410
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-foreground mt-1 whitespace-pre-wrap", children: String(event.text) })
|
|
4411
|
+
] }, idx);
|
|
4412
|
+
}
|
|
4413
|
+
if (event.type === "assistant") {
|
|
4414
|
+
const content = event.message;
|
|
4415
|
+
const blocks = content?.content ?? [];
|
|
4416
|
+
const textBlocks = blocks.filter((c) => c.type === "text").map((c) => c.text).join("");
|
|
4417
|
+
const toolUseBlocks = blocks.filter((c) => c.type === "tool_use");
|
|
4418
|
+
if (!textBlocks && toolUseBlocks.length === 0) return null;
|
|
4419
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
4420
|
+
textBlocks && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
4421
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-blue-400 uppercase", children: "Assistant" }),
|
|
4422
|
+
/* @__PURE__ */ jsx(MarkdownContent, { children: textBlocks })
|
|
4423
|
+
] }),
|
|
4424
|
+
toolUseBlocks.map((tool, ti) => /* @__PURE__ */ jsxs("div", { className: "space-y-1 ml-3 pl-3 border-l-2 border-yellow-800/50", children: [
|
|
4425
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-semibold text-yellow-400 uppercase", children: [
|
|
4426
|
+
"Tool Call: ",
|
|
4427
|
+
tool.name ?? "unknown"
|
|
4428
|
+
] }),
|
|
4429
|
+
tool.id && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground ml-2 font-mono", children: String(tool.id) }),
|
|
4430
|
+
tool.input != null && /* @__PURE__ */ jsx(CollapsibleJson, { data: tool.input })
|
|
4431
|
+
] }, ti))
|
|
4432
|
+
] }, idx);
|
|
4433
|
+
}
|
|
4434
|
+
if (event.type === "tool_use") {
|
|
4435
|
+
const toolName = String(event.tool_name ?? event.name ?? "unknown");
|
|
4436
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1 ml-3 pl-3 border-l-2 border-yellow-800/50", children: [
|
|
4437
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4438
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-yellow-400 uppercase", children: "Tool Call" }),
|
|
4439
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-mono text-yellow-400/80", children: toolName }),
|
|
4440
|
+
event.tool_use_id ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground font-mono", children: String(event.tool_use_id) }) : null
|
|
4441
|
+
] }),
|
|
4442
|
+
event.input != null ? /* @__PURE__ */ jsx(CollapsibleJson, { data: event.input }) : null
|
|
4443
|
+
] }, idx);
|
|
4444
|
+
}
|
|
4445
|
+
if (event.type === "tool_result") {
|
|
4446
|
+
const isError = event.is_error === true || event.error === true;
|
|
4447
|
+
const content = event.output ?? event.content ?? "";
|
|
4448
|
+
const contentStr = typeof content === "string" ? content : JSON.stringify(content, null, 2);
|
|
4449
|
+
return /* @__PURE__ */ jsxs("div", { className: `space-y-1 ml-3 pl-3 border-l-2 ${isError ? "border-red-800/50" : "border-green-800/50"}`, children: [
|
|
4450
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4451
|
+
/* @__PURE__ */ jsx("span", { className: `text-xs font-semibold uppercase ${isError ? "text-red-400" : "text-green-400"}`, children: isError ? "Tool Error" : "Tool Result" }),
|
|
4452
|
+
event.tool_name ? /* @__PURE__ */ jsx("span", { className: "text-xs font-mono text-muted-foreground", children: String(event.tool_name) }) : null,
|
|
4453
|
+
event.tool_use_id ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground font-mono", children: String(event.tool_use_id) }) : null
|
|
4454
|
+
] }),
|
|
4455
|
+
contentStr ? /* @__PURE__ */ jsx(CollapsibleJson, { data: contentStr }) : null
|
|
4456
|
+
] }, idx);
|
|
4457
|
+
}
|
|
4458
|
+
if (event.type === "result") {
|
|
4459
|
+
const success = event.subtype === "success";
|
|
4460
|
+
const costUsd = event.cost_usd ?? event.total_cost_usd;
|
|
4461
|
+
return /* @__PURE__ */ jsxs("div", { className: `rounded-md px-3 py-2 flex items-center gap-3 ${success ? "bg-green-950 border border-green-900" : "bg-red-950 border border-red-900"}`, children: [
|
|
4462
|
+
/* @__PURE__ */ jsx("span", { className: `text-xs font-semibold ${success ? "text-green-400" : "text-red-400"}`, children: success ? "Completed" : "Failed" }),
|
|
4463
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-3 text-xs text-zinc-400", children: [
|
|
4464
|
+
event.num_turns != null && /* @__PURE__ */ jsxs("span", { children: [
|
|
4465
|
+
String(event.num_turns),
|
|
4466
|
+
" turns"
|
|
4467
|
+
] }),
|
|
4468
|
+
costUsd != null && Number(costUsd) > 0 && /* @__PURE__ */ jsxs("span", { children: [
|
|
4469
|
+
"$",
|
|
4470
|
+
Number(costUsd).toFixed(4)
|
|
4471
|
+
] }),
|
|
4472
|
+
event.duration_ms != null && /* @__PURE__ */ jsxs("span", { children: [
|
|
4473
|
+
(Number(event.duration_ms) / 1e3).toFixed(1),
|
|
4474
|
+
"s"
|
|
4475
|
+
] }),
|
|
4476
|
+
event.duration_api_ms != null && /* @__PURE__ */ jsxs("span", { children: [
|
|
4477
|
+
"API: ",
|
|
4478
|
+
(Number(event.duration_api_ms) / 1e3).toFixed(1),
|
|
4479
|
+
"s"
|
|
4480
|
+
] })
|
|
4481
|
+
] })
|
|
4482
|
+
] }, idx);
|
|
4483
|
+
}
|
|
4484
|
+
if (event.type === "error") {
|
|
4485
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-md p-3 bg-red-950 border border-red-800", children: [
|
|
4486
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4487
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-red-400", children: "Error" }),
|
|
4488
|
+
event.code ? /* @__PURE__ */ jsx("span", { className: "text-xs font-mono text-red-400/70", children: String(event.code) }) : null
|
|
4489
|
+
] }),
|
|
4490
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-foreground mt-1", children: String(event.error ?? "Unknown error") })
|
|
4491
|
+
] }, idx);
|
|
4492
|
+
}
|
|
4493
|
+
if (event.type === "stream_detached") {
|
|
4494
|
+
return /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground italic border-t border-border pt-2", children: [
|
|
4495
|
+
"Stream detached at ",
|
|
4496
|
+
event.timestamp ? new Date(String(event.timestamp)).toLocaleTimeString() : "unknown",
|
|
4497
|
+
" \u2014 run continues in background"
|
|
4498
|
+
] }, idx);
|
|
4499
|
+
}
|
|
4500
|
+
if (event.type === "queued") {
|
|
4501
|
+
return /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: "Queued\u2026" }, idx);
|
|
4502
|
+
}
|
|
4503
|
+
if (event.type === "sandbox_starting") {
|
|
4504
|
+
return /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: "Starting sandbox\u2026" }, idx);
|
|
4505
|
+
}
|
|
4506
|
+
if (event.type === "run_started") {
|
|
4507
|
+
return /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground", children: [
|
|
4508
|
+
"Agent started",
|
|
4509
|
+
event.model ? /* @__PURE__ */ jsx("span", { className: "ml-2 font-mono text-foreground/60", children: String(event.model) }) : null,
|
|
4510
|
+
event.mcp_server_count != null && Number(event.mcp_server_count) > 0 && /* @__PURE__ */ jsxs("span", { className: "ml-2", children: [
|
|
4511
|
+
String(event.mcp_server_count),
|
|
4512
|
+
" MCP server",
|
|
4513
|
+
Number(event.mcp_server_count) !== 1 ? "s" : ""
|
|
4514
|
+
] })
|
|
4515
|
+
] }, idx);
|
|
4516
|
+
}
|
|
4517
|
+
if (event.type === "system") {
|
|
4518
|
+
return /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground italic", children: String(event.message ?? JSON.stringify(event)) }, idx);
|
|
4519
|
+
}
|
|
4520
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
4521
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-purple-400 uppercase", children: event.type }),
|
|
4522
|
+
/* @__PURE__ */ jsx(CollapsibleJson, { data: event, maxHeight: "8rem" })
|
|
4523
|
+
] }, idx);
|
|
4524
|
+
}
|
|
4525
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled", "timed_out"]);
|
|
4526
|
+
function PlaygroundPage({ agentId }) {
|
|
4527
|
+
const client = useAgentPlaneClient();
|
|
4528
|
+
const { LinkComponent, basePath } = useNavigation();
|
|
4529
|
+
const { data: agent, error: agentError, isLoading } = useApi(
|
|
4530
|
+
`agent-${agentId}`,
|
|
4531
|
+
(c) => c.agents.get(agentId)
|
|
4532
|
+
);
|
|
4533
|
+
const [prompt, setPrompt] = useState("");
|
|
4534
|
+
const [events, setEvents] = useState([]);
|
|
4535
|
+
const [streamingText, setStreamingText] = useState("");
|
|
4536
|
+
const [running, setRunning] = useState(false);
|
|
4537
|
+
const [polling, setPolling] = useState(false);
|
|
4538
|
+
const [error, setError] = useState(null);
|
|
4539
|
+
const [sessionId, setSessionId] = useState(null);
|
|
4540
|
+
const sessionIdRef = useRef(null);
|
|
4541
|
+
const abortRef = useRef(null);
|
|
4542
|
+
const runIdRef = useRef(null);
|
|
4543
|
+
const streamRef = useRef(null);
|
|
4544
|
+
const scrollRef = useRef(null);
|
|
4545
|
+
const textareaRef = useRef(null);
|
|
4546
|
+
useEffect(() => {
|
|
4547
|
+
const el = scrollRef.current;
|
|
4548
|
+
if (el) el.scrollTop = el.scrollHeight;
|
|
4549
|
+
}, [events, streamingText]);
|
|
4550
|
+
useEffect(() => {
|
|
4551
|
+
return () => {
|
|
4552
|
+
abortRef.current?.abort();
|
|
4553
|
+
streamRef.current?.abort();
|
|
4554
|
+
};
|
|
4555
|
+
}, []);
|
|
4556
|
+
const pollForFinalResult = useCallback(async (runId) => {
|
|
4557
|
+
setPolling(true);
|
|
4558
|
+
let delay = 3e3;
|
|
4559
|
+
const maxDelay = 1e4;
|
|
4560
|
+
try {
|
|
4561
|
+
while (true) {
|
|
4562
|
+
if (abortRef.current?.signal.aborted) break;
|
|
4563
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
4564
|
+
if (abortRef.current?.signal.aborted) break;
|
|
4565
|
+
try {
|
|
4566
|
+
const run = await client.runs.get(runId);
|
|
4567
|
+
if (TERMINAL_STATUSES.has(run.status)) {
|
|
4568
|
+
try {
|
|
4569
|
+
const transcriptEvents = await client.runs.transcriptArray(runId);
|
|
4570
|
+
if (transcriptEvents.length > 0) {
|
|
4571
|
+
const detachIdx = transcriptEvents.findIndex((ev) => ev.type === "stream_detached");
|
|
4572
|
+
const eventsAfterDetach = detachIdx >= 0 ? transcriptEvents.slice(detachIdx + 1) : [];
|
|
4573
|
+
const newEvents = eventsAfterDetach.filter(
|
|
4574
|
+
(ev) => ev.type !== "heartbeat" && ev.type !== "text_delta" && ev.type !== "run_started" && ev.type !== "queued" && ev.type !== "sandbox_starting"
|
|
4575
|
+
);
|
|
4576
|
+
if (newEvents.length > 0) {
|
|
4577
|
+
setEvents((prev) => [...prev, ...newEvents]);
|
|
4578
|
+
}
|
|
4579
|
+
}
|
|
4580
|
+
} catch {
|
|
4581
|
+
}
|
|
4582
|
+
setEvents((prev) => {
|
|
4583
|
+
if (prev.some((ev) => ev.type === "result")) return prev;
|
|
4584
|
+
const syntheticResult = {
|
|
4585
|
+
type: "result",
|
|
4586
|
+
subtype: run.status === "completed" ? "success" : "failed",
|
|
4587
|
+
cost_usd: run.cost_usd,
|
|
4588
|
+
num_turns: run.num_turns,
|
|
4589
|
+
duration_ms: run.duration_ms
|
|
4590
|
+
};
|
|
4591
|
+
if (run.error_type) {
|
|
4592
|
+
syntheticResult.result = run.error_type;
|
|
4593
|
+
}
|
|
4594
|
+
return [...prev, syntheticResult];
|
|
4595
|
+
});
|
|
4596
|
+
break;
|
|
4597
|
+
}
|
|
4598
|
+
delay = Math.min(delay * 2, maxDelay);
|
|
4599
|
+
} catch (err) {
|
|
4600
|
+
if (err?.name === "AbortError") break;
|
|
4601
|
+
delay = Math.min(delay * 2, maxDelay);
|
|
4602
|
+
}
|
|
4603
|
+
}
|
|
4604
|
+
} finally {
|
|
4605
|
+
setPolling(false);
|
|
4606
|
+
setRunning(false);
|
|
4607
|
+
abortRef.current = null;
|
|
4608
|
+
runIdRef.current = null;
|
|
4609
|
+
streamRef.current = null;
|
|
4610
|
+
}
|
|
4611
|
+
}, [client]);
|
|
4612
|
+
const consumeStream = useCallback(async (stream) => {
|
|
4613
|
+
streamRef.current = stream;
|
|
4614
|
+
let handedOffToPoll = false;
|
|
4615
|
+
try {
|
|
4616
|
+
for await (const event of stream) {
|
|
4617
|
+
const ev = event;
|
|
4618
|
+
if (ev.type === "session_created" && ev.session_id) {
|
|
4619
|
+
const sid = ev.session_id;
|
|
4620
|
+
sessionIdRef.current = sid;
|
|
4621
|
+
setSessionId(sid);
|
|
4622
|
+
}
|
|
4623
|
+
if (ev.type === "run_started" && ev.run_id) {
|
|
4624
|
+
runIdRef.current = ev.run_id;
|
|
4625
|
+
}
|
|
4626
|
+
if (ev.type === "text_delta") {
|
|
4627
|
+
setStreamingText((prev) => prev + (ev.text ?? ""));
|
|
4628
|
+
} else if (ev.type === "stream_detached") {
|
|
4629
|
+
setStreamingText("");
|
|
4630
|
+
setEvents((prev) => [...prev, ev]);
|
|
4631
|
+
if (runIdRef.current) {
|
|
4632
|
+
handedOffToPoll = true;
|
|
4633
|
+
pollForFinalResult(runIdRef.current);
|
|
4634
|
+
return;
|
|
4635
|
+
}
|
|
4636
|
+
} else {
|
|
4637
|
+
if (ev.type === "assistant") setStreamingText("");
|
|
4638
|
+
setEvents((prev) => [...prev, ev]);
|
|
4639
|
+
}
|
|
4640
|
+
}
|
|
4641
|
+
} finally {
|
|
4642
|
+
if (!handedOffToPoll) {
|
|
4643
|
+
setRunning(false);
|
|
4644
|
+
abortRef.current = null;
|
|
4645
|
+
runIdRef.current = null;
|
|
4646
|
+
streamRef.current = null;
|
|
4647
|
+
}
|
|
4648
|
+
}
|
|
4649
|
+
}, [pollForFinalResult]);
|
|
4650
|
+
const handleSend = useCallback(async () => {
|
|
4651
|
+
if (!prompt.trim() || running) return;
|
|
4652
|
+
const messageText = prompt.trim();
|
|
4653
|
+
setPrompt("");
|
|
4654
|
+
setRunning(true);
|
|
4655
|
+
setStreamingText("");
|
|
4656
|
+
setError(null);
|
|
4657
|
+
setPolling(false);
|
|
4658
|
+
setEvents((prev) => [...prev, { type: "user_message", text: messageText }]);
|
|
4659
|
+
const abort = new AbortController();
|
|
4660
|
+
abortRef.current = abort;
|
|
4661
|
+
try {
|
|
4662
|
+
let stream;
|
|
4663
|
+
const currentSessionId = sessionIdRef.current;
|
|
4664
|
+
if (currentSessionId) {
|
|
4665
|
+
stream = await client.sessions.sendMessage(
|
|
4666
|
+
currentSessionId,
|
|
4667
|
+
{ prompt: messageText },
|
|
4668
|
+
{ signal: abort.signal }
|
|
4669
|
+
);
|
|
4670
|
+
} else {
|
|
4671
|
+
stream = await client.sessions.create(
|
|
4672
|
+
{ agent_id: agentId, prompt: messageText },
|
|
4673
|
+
{ signal: abort.signal }
|
|
4674
|
+
);
|
|
4675
|
+
}
|
|
4676
|
+
await consumeStream(stream);
|
|
4677
|
+
} catch (err) {
|
|
4678
|
+
if (err?.name !== "AbortError") {
|
|
4679
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
4680
|
+
setError(msg);
|
|
4681
|
+
}
|
|
4682
|
+
setRunning(false);
|
|
4683
|
+
abortRef.current = null;
|
|
4684
|
+
runIdRef.current = null;
|
|
4685
|
+
streamRef.current = null;
|
|
4686
|
+
}
|
|
4687
|
+
}, [prompt, running, agentId, client, consumeStream]);
|
|
4688
|
+
function handleNewChat() {
|
|
4689
|
+
abortRef.current?.abort();
|
|
4690
|
+
streamRef.current?.abort();
|
|
4691
|
+
if (sessionId) {
|
|
4692
|
+
client.sessions.stop(sessionId).catch(() => {
|
|
4693
|
+
});
|
|
4694
|
+
}
|
|
4695
|
+
sessionIdRef.current = null;
|
|
4696
|
+
setSessionId(null);
|
|
4697
|
+
setEvents([]);
|
|
4698
|
+
setStreamingText("");
|
|
4699
|
+
setRunning(false);
|
|
4700
|
+
setPolling(false);
|
|
4701
|
+
setError(null);
|
|
4702
|
+
setPrompt("");
|
|
4703
|
+
runIdRef.current = null;
|
|
4704
|
+
abortRef.current = null;
|
|
4705
|
+
streamRef.current = null;
|
|
4706
|
+
textareaRef.current?.focus();
|
|
4707
|
+
}
|
|
4708
|
+
function handleStop() {
|
|
4709
|
+
abortRef.current?.abort();
|
|
4710
|
+
streamRef.current?.abort();
|
|
4711
|
+
const id = runIdRef.current;
|
|
4712
|
+
if (id) {
|
|
4713
|
+
runIdRef.current = null;
|
|
4714
|
+
client.runs.cancel(id).catch(() => {
|
|
4715
|
+
});
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
if (isLoading) {
|
|
4719
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-[calc(100vh-6rem)]", children: [
|
|
4720
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
|
|
4721
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-24" }),
|
|
4722
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-40" })
|
|
4723
|
+
] }),
|
|
4724
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "flex-1 rounded-lg" }),
|
|
4725
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-2", children: [
|
|
4726
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-24 w-full rounded-lg" }),
|
|
4727
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-20" })
|
|
4728
|
+
] })
|
|
4729
|
+
] });
|
|
4730
|
+
}
|
|
4731
|
+
if (agentError || !agent) {
|
|
4732
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center h-[calc(100vh-6rem)] gap-3", children: [
|
|
4733
|
+
/* @__PURE__ */ jsx("p", { className: "text-destructive text-sm", children: agentError?.status === 404 ? "Agent not found." : `Failed to load agent: ${agentError?.message ?? "Unknown error"}` }),
|
|
4734
|
+
/* @__PURE__ */ jsx(
|
|
4735
|
+
LinkComponent,
|
|
4736
|
+
{
|
|
4737
|
+
href: `${basePath}/agents`,
|
|
4738
|
+
className: "text-sm text-muted-foreground hover:text-foreground",
|
|
4739
|
+
children: "\u2190 Back to agents"
|
|
4740
|
+
}
|
|
4741
|
+
)
|
|
4742
|
+
] });
|
|
4743
|
+
}
|
|
4744
|
+
const hasContent = events.length > 0 || running;
|
|
4745
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-[calc(100vh-6rem)]", children: [
|
|
4746
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
|
|
4747
|
+
/* @__PURE__ */ jsxs(
|
|
4748
|
+
LinkComponent,
|
|
4749
|
+
{
|
|
4750
|
+
href: `${basePath}/agents/${agentId}`,
|
|
4751
|
+
className: "text-sm text-muted-foreground hover:text-foreground",
|
|
4752
|
+
children: [
|
|
4753
|
+
"\u2190 ",
|
|
4754
|
+
agent.name
|
|
4755
|
+
]
|
|
4756
|
+
}
|
|
4757
|
+
),
|
|
4758
|
+
(sessionId || events.length > 0) && /* @__PURE__ */ jsx(Button, { onClick: handleNewChat, variant: "outline", size: "sm", disabled: running, children: "New Chat" }),
|
|
4759
|
+
sessionId && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground font-mono", children: [
|
|
4760
|
+
"Session: ",
|
|
4761
|
+
sessionId.slice(0, 12),
|
|
4762
|
+
"\u2026"
|
|
4763
|
+
] })
|
|
4764
|
+
] }),
|
|
4765
|
+
hasContent && /* @__PURE__ */ jsxs("div", { ref: scrollRef, className: "flex-1 min-h-0 overflow-y-auto rounded-lg border border-border bg-muted/20 p-4 space-y-4 mb-4", children: [
|
|
4766
|
+
events.map((ev, i) => renderEvent(ev, i)),
|
|
4767
|
+
streamingText && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
4768
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-blue-400 uppercase", children: "Assistant" }),
|
|
4769
|
+
/* @__PURE__ */ jsx(MarkdownContent, { children: streamingText }),
|
|
4770
|
+
/* @__PURE__ */ jsx("span", { className: "inline-block w-0.5 h-4 bg-foreground animate-pulse align-text-bottom" })
|
|
4771
|
+
] }),
|
|
4772
|
+
running && !streamingText && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
4773
|
+
/* @__PURE__ */ jsx("span", { className: "animate-pulse", children: "\u25CF" }),
|
|
4774
|
+
" ",
|
|
4775
|
+
polling ? "Reconnected, streaming updates\u2026" : "Running\u2026"
|
|
4776
|
+
] })
|
|
4777
|
+
] }),
|
|
4778
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2 shrink-0", children: [
|
|
4779
|
+
error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error }),
|
|
4780
|
+
/* @__PURE__ */ jsx(
|
|
4781
|
+
Textarea,
|
|
4782
|
+
{
|
|
4783
|
+
ref: textareaRef,
|
|
4784
|
+
placeholder: sessionId ? "Send a follow-up message\u2026" : "Enter your prompt\u2026",
|
|
4785
|
+
value: prompt,
|
|
4786
|
+
onChange: (e) => setPrompt(e.target.value),
|
|
4787
|
+
rows: hasContent ? 3 : 12,
|
|
4788
|
+
disabled: running,
|
|
4789
|
+
className: "font-mono text-sm resize-none",
|
|
4790
|
+
onKeyDown: (e) => {
|
|
4791
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) handleSend();
|
|
4792
|
+
}
|
|
4793
|
+
}
|
|
4794
|
+
),
|
|
4795
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4796
|
+
/* @__PURE__ */ jsx(Button, { onClick: handleSend, disabled: running || !prompt.trim(), size: "sm", children: running ? "Running\u2026" : sessionId ? "Send" : "Run" }),
|
|
4797
|
+
running && /* @__PURE__ */ jsx(Button, { onClick: handleStop, variant: "outline", size: "sm", children: "Stop" }),
|
|
4798
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground ml-1", children: "\u2318+Enter to send" })
|
|
4799
|
+
] })
|
|
4800
|
+
] })
|
|
4801
|
+
] });
|
|
4802
|
+
}
|
|
4365
4803
|
|
|
4366
|
-
export { AdminTable, AdminTableHead, AdminTableRow, AgentA2aInfo, AgentConnectorsManager, AgentDetailPage, AgentEditForm, AgentListPage, AgentPluginManager, AgentRuns, AgentScheduleForm, AgentSkillManager, ConfirmDialog, CopyButton, DashboardPage, DetailPageHeader, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, EmptyRow, FormError, FormField, LocalDate, McpServerListPage, MetricCard, ModelSelector, PaginationBar, PluginDetailPage, PluginMarketplaceDetailPage, PluginMarketplaceListPage, RunDetailPage, RunListPage, RunSourceBadge, RunStatusBadge, SectionHeader, Select, SettingsPage, Tabs, Textarea, Th, ToolkitMultiselect, TranscriptViewer, parsePaginationParams };
|
|
4804
|
+
export { AdminTable, AdminTableHead, AdminTableRow, AgentA2aInfo, AgentConnectorsManager, AgentDetailPage, AgentEditForm, AgentListPage, AgentPluginManager, AgentRuns, AgentScheduleForm, AgentSkillManager, ConfirmDialog, CopyButton, DashboardPage, DetailPageHeader, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, EmptyRow, FormError, FormField, LocalDate, McpServerListPage, MetricCard, ModelSelector, PaginationBar, PlaygroundPage, PluginDetailPage, PluginMarketplaceDetailPage, PluginMarketplaceListPage, RunDetailPage, RunListPage, RunSourceBadge, RunStatusBadge, SectionHeader, Select, SettingsPage, Tabs, Textarea, Th, ToolkitMultiselect, TranscriptViewer, parsePaginationParams };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getcatalystiq/agent-plane-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"description": "Embeddable React component library for AgentPlane",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -61,12 +61,16 @@
|
|
|
61
61
|
"react-dom": "^18.0.0 || ^19.0.0",
|
|
62
62
|
"react-markdown": "^9.0.0",
|
|
63
63
|
"recharts": "^2.0.0",
|
|
64
|
+
"remark-gfm": "^4.0.0",
|
|
64
65
|
"swr": "^2.0.0"
|
|
65
66
|
},
|
|
66
67
|
"peerDependenciesMeta": {
|
|
67
68
|
"react-markdown": {
|
|
68
69
|
"optional": true
|
|
69
70
|
},
|
|
71
|
+
"remark-gfm": {
|
|
72
|
+
"optional": true
|
|
73
|
+
},
|
|
70
74
|
"recharts": {
|
|
71
75
|
"optional": true
|
|
72
76
|
},
|
|
@@ -96,6 +100,7 @@
|
|
|
96
100
|
"devDependencies": {
|
|
97
101
|
"@types/react": "^19",
|
|
98
102
|
"@types/react-dom": "^19",
|
|
103
|
+
"remark-gfm": "^4.0.1",
|
|
99
104
|
"swr": "^2.0.0",
|
|
100
105
|
"tsup": "^8",
|
|
101
106
|
"typescript": "^5"
|