@robota-sdk/agent-web-ui 3.0.0-beta.64

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.
@@ -0,0 +1,676 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
+ get: ((k) => from[k]).bind(null, key),
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
+ value: mod,
21
+ enumerable: true
22
+ }) : target, mod));
23
+ //#endregion
24
+ let react = require("react");
25
+ react = __toESM(react, 1);
26
+ let react_markdown = require("react-markdown");
27
+ react_markdown = __toESM(react_markdown);
28
+ let remark_gfm = require("remark-gfm");
29
+ remark_gfm = __toESM(remark_gfm);
30
+ let react_jsx_runtime = require("react/jsx-runtime");
31
+ //#region src/client/ws-session-client.ts
32
+ const RECONNECT_DELAY_MS = 2e3;
33
+ const MAX_RECONNECT_ATTEMPTS = 10;
34
+ function createWsSessionClient(url, callbacks) {
35
+ let ws = null;
36
+ let currentStatus = "disconnected";
37
+ let reconnectTimer = null;
38
+ let reconnectAttempts = 0;
39
+ let intentionalDisconnect = false;
40
+ function setStatus(s) {
41
+ currentStatus = s;
42
+ callbacks.onStatusChange(s);
43
+ }
44
+ function scheduleReconnect() {
45
+ if (intentionalDisconnect || reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) return;
46
+ reconnectTimer = setTimeout(() => {
47
+ reconnectAttempts++;
48
+ doConnect();
49
+ }, RECONNECT_DELAY_MS);
50
+ }
51
+ function doConnect() {
52
+ setStatus("connecting");
53
+ ws = new WebSocket(url);
54
+ ws.onopen = () => {
55
+ reconnectAttempts = 0;
56
+ setStatus("connected");
57
+ send({ type: "get-messages" });
58
+ };
59
+ ws.onmessage = (event) => {
60
+ const data = event.data;
61
+ if (typeof data !== "string") return;
62
+ const msg = JSON.parse(data);
63
+ callbacks.onMessage(msg);
64
+ };
65
+ ws.onclose = () => {
66
+ ws = null;
67
+ if (!intentionalDisconnect) {
68
+ setStatus("disconnected");
69
+ scheduleReconnect();
70
+ } else setStatus("disconnected");
71
+ };
72
+ ws.onerror = () => {
73
+ setStatus("error");
74
+ };
75
+ }
76
+ function connect() {
77
+ intentionalDisconnect = false;
78
+ if (reconnectTimer !== null) {
79
+ clearTimeout(reconnectTimer);
80
+ reconnectTimer = null;
81
+ }
82
+ doConnect();
83
+ }
84
+ function disconnect() {
85
+ intentionalDisconnect = true;
86
+ if (reconnectTimer !== null) {
87
+ clearTimeout(reconnectTimer);
88
+ reconnectTimer = null;
89
+ }
90
+ ws?.close();
91
+ ws = null;
92
+ setStatus("disconnected");
93
+ }
94
+ function send(msg) {
95
+ if (ws?.readyState === WebSocket.OPEN) ws.send(JSON.stringify(msg));
96
+ }
97
+ return {
98
+ connect,
99
+ disconnect,
100
+ send,
101
+ status: () => currentStatus
102
+ };
103
+ }
104
+ //#endregion
105
+ //#region src/hooks/useWsSession.ts
106
+ /**
107
+ * React hook that connects to an agent-cli sidecar WebSocket and
108
+ * reconstructs conversation state from TServerMessage events.
109
+ */
110
+ let msgCounter = 0;
111
+ function nextId() {
112
+ return `msg_${++msgCounter}_${Date.now()}`;
113
+ }
114
+ function useWsSession(url) {
115
+ const [status, setStatus] = (0, react.useState)("disconnected");
116
+ const [messages, setMessages] = (0, react.useState)([]);
117
+ const [activeTools, setActiveTools] = (0, react.useState)([]);
118
+ const [streamingText, setStreamingText] = (0, react.useState)("");
119
+ const [isThinking, setIsThinking] = (0, react.useState)(false);
120
+ const [executionWorkspace, setExecutionWorkspace] = (0, react.useState)(null);
121
+ const clientRef = (0, react.useRef)(null);
122
+ const streamingIdRef = (0, react.useRef)(null);
123
+ const streamingTextRef = (0, react.useRef)("");
124
+ const handleMessage = (0, react.useCallback)((msg) => {
125
+ switch (msg.type) {
126
+ case "messages":
127
+ setMessages(msg.messages.flatMap((m) => {
128
+ if (m.role !== "user" && m.role !== "assistant") return [];
129
+ const content = m.content ?? "";
130
+ return [{
131
+ id: nextId(),
132
+ role: m.role,
133
+ content
134
+ }];
135
+ }));
136
+ break;
137
+ case "user_message":
138
+ setMessages((prev) => [...prev, {
139
+ id: nextId(),
140
+ role: "user",
141
+ content: msg.content
142
+ }]);
143
+ break;
144
+ case "text_delta":
145
+ setStreamingText((prev) => {
146
+ const next = prev + msg.delta;
147
+ streamingTextRef.current = next;
148
+ if (streamingIdRef.current === null) streamingIdRef.current = nextId();
149
+ return next;
150
+ });
151
+ break;
152
+ case "thinking":
153
+ setIsThinking(msg.isThinking);
154
+ break;
155
+ case "tool_start": {
156
+ const { state } = msg;
157
+ const toolId = nextId();
158
+ setActiveTools((prev) => [...prev, {
159
+ id: toolId,
160
+ name: state.toolName,
161
+ status: "running",
162
+ input: state.firstArg
163
+ }]);
164
+ break;
165
+ }
166
+ case "tool_end": {
167
+ const { state } = msg;
168
+ setActiveTools((prev) => prev.map((t) => t.name === state.toolName && t.status === "running" ? {
169
+ ...t,
170
+ status: state.isRunning ? "running" : "done",
171
+ result: state.result
172
+ } : t));
173
+ break;
174
+ }
175
+ case "execution_workspace_event":
176
+ setExecutionWorkspace(msg.snapshot);
177
+ break;
178
+ case "complete":
179
+ case "interrupted": {
180
+ const finalText = streamingTextRef.current;
181
+ const sid = streamingIdRef.current;
182
+ streamingTextRef.current = "";
183
+ streamingIdRef.current = null;
184
+ setStreamingText("");
185
+ setIsThinking(false);
186
+ setActiveTools([]);
187
+ if (finalText) setMessages((prev) => [...prev, {
188
+ id: sid ?? nextId(),
189
+ role: "assistant",
190
+ content: finalText
191
+ }]);
192
+ break;
193
+ }
194
+ }
195
+ }, []);
196
+ const send = (0, react.useCallback)((msg) => {
197
+ clientRef.current?.send(msg);
198
+ }, []);
199
+ (0, react.useEffect)(() => {
200
+ const client = createWsSessionClient(url, {
201
+ onMessage: handleMessage,
202
+ onStatusChange: setStatus
203
+ });
204
+ clientRef.current = client;
205
+ client.connect();
206
+ return () => {
207
+ client.disconnect();
208
+ clientRef.current = null;
209
+ };
210
+ }, [url, handleMessage]);
211
+ return {
212
+ status,
213
+ messages,
214
+ activeTools,
215
+ streamingText,
216
+ isThinking,
217
+ executionWorkspace,
218
+ send
219
+ };
220
+ }
221
+ //#endregion
222
+ //#region src/components/ConversationView.tsx
223
+ function AgentMarkdown({ children }) {
224
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_markdown.default, {
225
+ remarkPlugins: [remark_gfm.default],
226
+ components: {
227
+ h1: ({ children: c }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", {
228
+ className: "text-base font-bold mt-4 mb-2 text-foreground border-b border-border/50 pb-1.5",
229
+ children: c
230
+ }),
231
+ h2: ({ children: c }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
232
+ className: "text-sm font-semibold mt-3 mb-1.5 text-foreground",
233
+ children: c
234
+ }),
235
+ h3: ({ children: c }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
236
+ className: "text-sm font-medium mt-2 mb-1 text-muted-foreground",
237
+ children: c
238
+ }),
239
+ p: ({ children: c }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
240
+ className: "mb-2 last:mb-0 leading-relaxed text-sm",
241
+ children: c
242
+ }),
243
+ pre: ({ children: c }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
244
+ className: "bg-black/30 border border-border/40 rounded-lg p-3 my-2 overflow-x-auto text-xs font-mono leading-relaxed",
245
+ children: c
246
+ }),
247
+ code: ({ className, children: c }) => {
248
+ return Boolean(className) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
249
+ className: `font-mono leading-relaxed ${className ?? ""}`,
250
+ children: c
251
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
252
+ className: "font-mono text-[11px] bg-black/25 px-1.5 py-0.5 rounded text-amber-300/80 border border-border/30",
253
+ children: c
254
+ });
255
+ },
256
+ ul: ({ children: c }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
257
+ className: "list-disc list-outside ml-4 mb-2 space-y-0.5 text-sm",
258
+ children: c
259
+ }),
260
+ ol: ({ children: c }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", {
261
+ className: "list-decimal list-outside ml-4 mb-2 space-y-0.5 text-sm",
262
+ children: c
263
+ }),
264
+ li: ({ children: c }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
265
+ className: "leading-relaxed",
266
+ children: c
267
+ }),
268
+ strong: ({ children: c }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
269
+ className: "font-semibold text-foreground",
270
+ children: c
271
+ }),
272
+ em: ({ children: c }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("em", {
273
+ className: "italic text-muted-foreground/80",
274
+ children: c
275
+ }),
276
+ a: ({ href, children: c }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
277
+ href,
278
+ className: "text-primary underline underline-offset-2 hover:opacity-75 transition-opacity",
279
+ target: "_blank",
280
+ rel: "noopener noreferrer",
281
+ children: c
282
+ }),
283
+ table: ({ children: c }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
284
+ className: "overflow-x-auto my-2 rounded-lg border border-border/50",
285
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("table", {
286
+ className: "w-full text-xs border-collapse",
287
+ children: c
288
+ })
289
+ }),
290
+ thead: ({ children: c }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", {
291
+ className: "bg-muted/40",
292
+ children: c
293
+ }),
294
+ th: ({ children: c }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
295
+ className: "px-3 py-2 text-left font-medium text-muted-foreground border-b border-border/50 text-[11px] tracking-wide",
296
+ children: c
297
+ }),
298
+ td: ({ children: c }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
299
+ className: "px-3 py-2 border-b border-border/30 text-xs last-of-type:border-0",
300
+ children: c
301
+ }),
302
+ blockquote: ({ children: c }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("blockquote", {
303
+ className: "border-l-2 border-primary/40 pl-3 my-2 text-muted-foreground text-sm italic",
304
+ children: c
305
+ }),
306
+ hr: () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("hr", { className: "border-border/50 my-3" })
307
+ },
308
+ children
309
+ });
310
+ }
311
+ function UserBlock({ content }) {
312
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
313
+ className: "flex flex-col gap-1",
314
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
315
+ className: "text-[10px] font-mono tracking-[0.14em] uppercase text-primary/50 px-1",
316
+ children: "You"
317
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
318
+ className: "rounded-xl border border-primary/20 bg-primary/8 px-4 py-3 text-sm leading-relaxed text-foreground",
319
+ children: content
320
+ })]
321
+ });
322
+ }
323
+ function AgentBlock({ content, isStreaming = false }) {
324
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
325
+ className: "flex flex-col gap-1",
326
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
327
+ className: "text-[10px] font-mono tracking-[0.14em] uppercase text-muted-foreground/50 px-1",
328
+ children: "Agent"
329
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
330
+ className: "rounded-xl border border-border/60 bg-card px-4 py-3 text-card-foreground",
331
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AgentMarkdown, { children: content }), isStreaming && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "inline-block w-[2px] h-[14px] bg-primary/60 ml-0.5 align-middle animate-pulse" })]
332
+ })]
333
+ });
334
+ }
335
+ function ToolCard({ tool }) {
336
+ const running = tool.status === "running";
337
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
338
+ className: `flex items-center gap-2.5 rounded-lg border px-3.5 py-2 text-[11px] font-mono transition-colors ${running ? "bg-amber-500/5 border-amber-500/20 text-amber-300/80" : "bg-emerald-500/5 border-emerald-500/20 text-emerald-300/80"}`,
339
+ children: [
340
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: `h-1.5 w-1.5 rounded-full flex-shrink-0 ${running ? "bg-amber-400 animate-pulse" : "bg-emerald-400"}` }),
341
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
342
+ className: "opacity-50",
343
+ children: running ? "▶" : "✓"
344
+ }),
345
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
346
+ className: "font-medium tracking-wide",
347
+ children: tool.name
348
+ }),
349
+ typeof tool.input === "string" && tool.input && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
350
+ className: "opacity-35 truncate max-w-[200px] text-[10px]",
351
+ children: tool.input
352
+ }),
353
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
354
+ className: "ml-auto opacity-35 text-[10px]",
355
+ children: running ? "running…" : "done"
356
+ })
357
+ ]
358
+ });
359
+ }
360
+ function ThinkingIndicator() {
361
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
362
+ className: "flex items-center gap-2.5 px-1",
363
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
364
+ className: "text-[10px] font-mono tracking-[0.14em] uppercase text-muted-foreground/50",
365
+ children: "Agent"
366
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
367
+ className: "flex gap-1 items-center",
368
+ children: [
369
+ 0,
370
+ 1,
371
+ 2
372
+ ].map((i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
373
+ className: "w-1.5 h-1.5 rounded-full bg-emerald-400/60 animate-bounce",
374
+ style: { animationDelay: `${i * 150}ms` }
375
+ }, i))
376
+ })]
377
+ });
378
+ }
379
+ function ConversationView({ messages, activeTools, streamingText, isThinking }) {
380
+ const bottomRef = (0, react.useRef)(null);
381
+ (0, react.useEffect)(() => {
382
+ bottomRef.current?.scrollIntoView({ behavior: "smooth" });
383
+ }, [
384
+ messages.length,
385
+ streamingText,
386
+ isThinking,
387
+ activeTools.length
388
+ ]);
389
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
390
+ className: "flex flex-col gap-3 p-4 overflow-y-auto h-full",
391
+ children: [
392
+ messages.length === 0 && !isThinking && activeTools.length === 0 && !streamingText && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
393
+ className: "flex h-full items-center justify-center",
394
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
395
+ className: "text-xs font-mono text-muted-foreground/50 tracking-widest uppercase",
396
+ children: "No messages yet"
397
+ })
398
+ }),
399
+ messages.map((msg) => msg.role === "user" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UserBlock, { content: msg.content }, msg.id) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AgentBlock, { content: msg.content }, msg.id)),
400
+ isThinking && !streamingText && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThinkingIndicator, {}),
401
+ activeTools.map((tool) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCard, { tool }, tool.id)),
402
+ streamingText && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AgentBlock, {
403
+ content: streamingText,
404
+ isStreaming: true
405
+ }),
406
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { ref: bottomRef })
407
+ ]
408
+ });
409
+ }
410
+ //#endregion
411
+ //#region src/components/AgentActivityPanel.tsx
412
+ function AgentActivityPanel({ tasks, className }) {
413
+ const runningCount = tasks.filter((t) => t.status === "running").length;
414
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
415
+ className: `flex flex-col overflow-hidden bg-card/10 ${className ?? ""}`,
416
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
417
+ className: "px-3 py-2 border-b border-border/50 flex items-center gap-2 flex-shrink-0",
418
+ children: [
419
+ runningCount > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
420
+ className: "relative flex h-1.5 w-1.5 flex-shrink-0",
421
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-amber-400 opacity-60" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "relative inline-flex h-1.5 w-1.5 rounded-full bg-amber-500" })]
422
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "h-1.5 w-1.5 rounded-full bg-zinc-600 flex-shrink-0" }),
423
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
424
+ className: "text-[10px] font-mono font-semibold tracking-[0.14em] uppercase text-muted-foreground",
425
+ children: "Agents"
426
+ }),
427
+ runningCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
428
+ className: "ml-auto text-[10px] font-mono text-amber-400/70 tabular-nums",
429
+ children: [runningCount, " running"]
430
+ })
431
+ ]
432
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
433
+ className: "flex-1 overflow-y-auto px-2 py-2 space-y-1.5",
434
+ children: tasks.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AgentCard, { entry }, entry.id))
435
+ })]
436
+ });
437
+ }
438
+ function AgentCard({ entry }) {
439
+ const tokens = getStatusTokens(entry.status, entry.attention);
440
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
441
+ className: `relative rounded-md overflow-hidden border transition-opacity duration-500 ${tokens.cardCls} ${entry.status === "completed" ? "opacity-40" : ""}`,
442
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: `absolute left-0 top-0 bottom-0 w-0.5 ${tokens.barCls}` }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
443
+ className: "pl-3.5 pr-3 py-2",
444
+ children: [
445
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
446
+ className: "flex items-center gap-2",
447
+ children: [
448
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusDot, {
449
+ status: entry.status,
450
+ attention: entry.attention
451
+ }),
452
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
453
+ className: "text-[11px] font-mono font-medium text-foreground/90 truncate flex-1 leading-none",
454
+ children: entry.title
455
+ }),
456
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AttentionTag, {
457
+ attention: entry.attention,
458
+ status: entry.status
459
+ })
460
+ ]
461
+ }),
462
+ entry.currentAction && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
463
+ className: "text-[10px] text-foreground/50 leading-snug mt-1.5 truncate",
464
+ children: entry.currentAction
465
+ }),
466
+ entry.preview && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
467
+ className: "text-[10px] font-mono text-muted-foreground/40 leading-snug mt-0.5 truncate",
468
+ children: [
469
+ "“",
470
+ entry.preview,
471
+ "”"
472
+ ]
473
+ })
474
+ ]
475
+ })]
476
+ });
477
+ }
478
+ function StatusDot({ status, attention }) {
479
+ if (attention === "permission" || status === "waiting_permission") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
480
+ className: "relative flex h-1.5 w-1.5 flex-shrink-0",
481
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-rose-400 opacity-70" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "relative inline-flex h-1.5 w-1.5 rounded-full bg-rose-500" })]
482
+ });
483
+ if (attention === "failed" || status === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "h-1.5 w-1.5 rounded-full bg-rose-500 flex-shrink-0" });
484
+ if (status === "running") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
485
+ className: "relative flex h-1.5 w-1.5 flex-shrink-0",
486
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-amber-400 opacity-55" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "relative inline-flex h-1.5 w-1.5 rounded-full bg-amber-400" })]
487
+ });
488
+ if (status === "completed") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "h-1.5 w-1.5 rounded-full bg-emerald-400/60 flex-shrink-0" });
489
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "h-1.5 w-1.5 rounded-full bg-zinc-600 flex-shrink-0" });
490
+ }
491
+ function AttentionTag({ attention, status }) {
492
+ if (attention === "permission" || status === "waiting_permission") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
493
+ className: "text-[9px] font-mono text-rose-400 bg-rose-500/10 border border-rose-500/25 px-1.5 py-px rounded flex-shrink-0",
494
+ children: "perm"
495
+ });
496
+ if (attention === "failed" || status === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
497
+ className: "text-[9px] font-mono text-rose-400 bg-rose-500/10 border border-rose-500/25 px-1.5 py-px rounded flex-shrink-0",
498
+ children: "err"
499
+ });
500
+ if (status === "completed") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
501
+ className: "text-[9px] font-mono text-emerald-400/50 flex-shrink-0",
502
+ children: "done"
503
+ });
504
+ if (status === "queued") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
505
+ className: "text-[9px] font-mono text-zinc-500 flex-shrink-0",
506
+ children: "queued"
507
+ });
508
+ return null;
509
+ }
510
+ function getStatusTokens(status, attention) {
511
+ if (attention === "permission" || status === "waiting_permission") return {
512
+ cardCls: "border-rose-500/40 bg-rose-950/10",
513
+ barCls: "bg-rose-400"
514
+ };
515
+ if (attention === "failed" || status === "failed") return {
516
+ cardCls: "border-rose-500/30 bg-rose-950/15",
517
+ barCls: "bg-rose-500"
518
+ };
519
+ if (status === "running") return {
520
+ cardCls: "border-amber-500/20 bg-card/30",
521
+ barCls: "bg-amber-400"
522
+ };
523
+ if (status === "completed") return {
524
+ cardCls: "border-border/20 bg-card/20",
525
+ barCls: "bg-emerald-400/50"
526
+ };
527
+ return {
528
+ cardCls: "border-border/30 bg-card/25",
529
+ barCls: "bg-zinc-600/50"
530
+ };
531
+ }
532
+ //#endregion
533
+ //#region src/components/SessionMonitor.tsx
534
+ const STATUS_CONFIG = {
535
+ connected: {
536
+ label: "Connected",
537
+ dot: "bg-emerald-400",
538
+ text: "text-emerald-400",
539
+ glow: "shadow-[0_0_7px_1px_rgba(52,211,153,0.55)]"
540
+ },
541
+ connecting: {
542
+ label: "Connecting…",
543
+ dot: "bg-amber-400 animate-pulse",
544
+ text: "text-amber-400",
545
+ glow: ""
546
+ },
547
+ disconnected: {
548
+ label: "Disconnected",
549
+ dot: "bg-zinc-600",
550
+ text: "text-zinc-500",
551
+ glow: ""
552
+ },
553
+ error: {
554
+ label: "Error",
555
+ dot: "bg-rose-500",
556
+ text: "text-rose-400",
557
+ glow: ""
558
+ }
559
+ };
560
+ function SessionMonitor({ wsUrl, className }) {
561
+ const [url, setUrl] = (0, react.useState)(wsUrl);
562
+ const [inputUrl, setInputUrl] = (0, react.useState)(wsUrl);
563
+ const { status, messages, activeTools, streamingText, isThinking, executionWorkspace, send } = useWsSession(url);
564
+ const cfg = STATUS_CONFIG[status] ?? STATUS_CONFIG.disconnected;
565
+ const backgroundTasks = executionWorkspace?.entries.filter((e) => e.kind === "background_task" && e.visibility !== "collapsed") ?? [];
566
+ const hasAgents = backgroundTasks.length > 0;
567
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
568
+ className: `flex flex-col h-full overflow-hidden bg-background ${className ?? ""}`,
569
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
570
+ className: "flex items-center gap-3 border-b border-border/50 px-4 py-2.5 bg-card/30 flex-shrink-0",
571
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
572
+ className: "flex items-center gap-2.5",
573
+ children: [
574
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: `h-2 w-2 rounded-full flex-shrink-0 ${cfg.dot} ${cfg.glow}` }),
575
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
576
+ className: "text-[11px] font-mono font-semibold tracking-[0.14em] uppercase text-foreground/70",
577
+ children: "CLI Monitor"
578
+ }),
579
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
580
+ className: "text-border/60 font-mono text-xs",
581
+ children: "·"
582
+ }),
583
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
584
+ className: `text-[11px] font-mono ${cfg.text}`,
585
+ children: cfg.label
586
+ })
587
+ ]
588
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
589
+ className: "ml-auto flex items-center gap-2",
590
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
591
+ className: "h-7 rounded-lg border border-border/60 bg-background/60 px-2.5 text-[11px] font-mono text-foreground/70 placeholder:text-muted-foreground/35 focus:outline-none focus:border-primary/50 focus:ring-1 focus:ring-primary/15 w-52 transition-all",
592
+ value: inputUrl,
593
+ onChange: (e) => setInputUrl(e.target.value),
594
+ onKeyDown: (e) => {
595
+ if (e.key === "Enter") setUrl(inputUrl);
596
+ },
597
+ placeholder: "ws://localhost:7070"
598
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
599
+ className: "h-7 rounded-lg border border-border/60 px-3 text-[11px] font-mono text-muted-foreground hover:text-foreground hover:border-primary/40 hover:bg-primary/5 transition-all",
600
+ onClick: () => setUrl(inputUrl),
601
+ children: "Connect"
602
+ })]
603
+ })]
604
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
605
+ className: "flex flex-1 overflow-hidden",
606
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
607
+ className: `flex flex-col overflow-hidden min-w-0 ${hasAgents ? "flex-[2]" : "flex-1"}`,
608
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
609
+ className: "flex-1 overflow-hidden",
610
+ children: status === "disconnected" || status === "connecting" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
611
+ className: "flex h-full items-center justify-center",
612
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
613
+ className: "flex flex-col items-center gap-3 text-center px-8",
614
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
615
+ className: "h-9 w-9 rounded-full border border-border/50 flex items-center justify-center",
616
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: `h-2.5 w-2.5 rounded-full ${cfg.dot}` })
617
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
618
+ className: "text-xs font-mono text-muted-foreground max-w-[260px] leading-relaxed",
619
+ children: status === "connecting" ? `Connecting to ${url}…` : `Run robota to start the CLI (WS transport starts automatically).`
620
+ })]
621
+ })
622
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConversationView, {
623
+ messages,
624
+ activeTools,
625
+ streamingText,
626
+ isThinking
627
+ })
628
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionInput, {
629
+ enabled: status === "connected",
630
+ onSubmit: (prompt) => send({
631
+ type: "submit",
632
+ prompt
633
+ })
634
+ })]
635
+ }), hasAgents && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AgentActivityPanel, {
636
+ tasks: backgroundTasks,
637
+ className: "flex-1 border-l border-border/50"
638
+ })]
639
+ })]
640
+ });
641
+ }
642
+ function SessionInput({ enabled, onSubmit }) {
643
+ const [value, setValue] = (0, react.useState)("");
644
+ const handleSubmit = () => {
645
+ const trimmed = value.trim();
646
+ if (!trimmed) return;
647
+ onSubmit(trimmed);
648
+ setValue("");
649
+ };
650
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
651
+ className: "border-t border-border/50 px-3 py-2.5 flex gap-2 items-end bg-card/20 flex-shrink-0",
652
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
653
+ className: "flex-1 resize-none rounded-xl border border-border/60 bg-background/50 px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground/35 focus:outline-none focus:border-primary/45 focus:ring-1 focus:ring-primary/15 min-h-[36px] max-h-[120px] transition-all font-[inherit] leading-relaxed",
654
+ rows: 1,
655
+ placeholder: enabled ? "Send a message…" : "Connect to send messages",
656
+ disabled: !enabled,
657
+ value,
658
+ onChange: (e) => setValue(e.target.value),
659
+ onKeyDown: (e) => {
660
+ if (e.key === "Enter" && !e.shiftKey) {
661
+ e.preventDefault();
662
+ handleSubmit();
663
+ }
664
+ }
665
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
666
+ className: "h-9 rounded-xl border border-border/60 px-3.5 text-[11px] font-mono text-muted-foreground hover:text-foreground hover:border-primary/40 hover:bg-primary/5 transition-all disabled:opacity-25 disabled:cursor-not-allowed",
667
+ disabled: !enabled || !value.trim(),
668
+ onClick: handleSubmit,
669
+ children: "Send"
670
+ })]
671
+ });
672
+ }
673
+ //#endregion
674
+ exports.ConversationView = ConversationView;
675
+ exports.SessionMonitor = SessionMonitor;
676
+ exports.useWsSession = useWsSession;