agent-state-machine 2.2.1 → 2.3.0
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/bin/cli.js +33 -5
- package/lib/file-tree.js +1 -1
- package/lib/runtime/agent.js +6 -2
- package/lib/runtime/interaction.js +2 -1
- package/lib/runtime/prompt.js +37 -1
- package/lib/runtime/runtime.js +67 -5
- package/lib/setup.js +4 -4
- package/package.json +1 -1
- package/templates/project-builder/agents/code-fixer.md +50 -0
- package/templates/project-builder/agents/code-writer.md +3 -0
- package/templates/project-builder/agents/sanity-checker.md +6 -0
- package/templates/project-builder/agents/test-planner.md +3 -1
- package/templates/project-builder/config.js +4 -4
- package/templates/project-builder/scripts/workflow-helpers.js +104 -2
- package/templates/project-builder/workflow.js +151 -14
- package/templates/starter/config.js +1 -1
- package/vercel-server/api/submit/[token].js +0 -11
- package/vercel-server/local-server.js +0 -19
- package/vercel-server/public/remote/assets/index-BTLc1QSv.js +168 -0
- package/vercel-server/public/remote/assets/index-DLa4X08t.css +1 -0
- package/vercel-server/public/remote/index.html +2 -2
- package/vercel-server/ui/src/App.jsx +53 -18
- package/vercel-server/ui/src/components/ChoiceInteraction.jsx +69 -18
- package/vercel-server/ui/src/components/ConfirmInteraction.jsx +7 -7
- package/vercel-server/ui/src/components/ContentCard.jsx +607 -103
- package/vercel-server/ui/src/components/EventsLog.jsx +20 -13
- package/vercel-server/ui/src/components/Footer.jsx +9 -4
- package/vercel-server/ui/src/components/Header.jsx +12 -3
- package/vercel-server/ui/src/components/SendingCard.jsx +33 -0
- package/vercel-server/ui/src/components/TextInteraction.jsx +8 -8
- package/vercel-server/ui/src/index.css +82 -10
- package/vercel-server/public/remote/assets/index-CbgeVnKw.js +0 -148
- package/vercel-server/public/remote/assets/index-DHL_iHQW.css +0 -1
|
@@ -1,5 +1,36 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
1
2
|
import CopyButton from "./CopyButton.jsx";
|
|
2
|
-
import { Bot, Brain, ChevronRight } from "lucide-react";
|
|
3
|
+
import { Bot, Brain, ChevronRight, Search, X } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6
|
+
|
|
7
|
+
const highlightText = (text, query) => {
|
|
8
|
+
const source = String(text ?? "");
|
|
9
|
+
const q = (query || "").trim();
|
|
10
|
+
if (!q) return source;
|
|
11
|
+
const regex = new RegExp(escapeRegExp(q), "gi");
|
|
12
|
+
const matches = source.match(regex);
|
|
13
|
+
if (!matches) return source;
|
|
14
|
+
|
|
15
|
+
const parts = source.split(regex);
|
|
16
|
+
const nodes = [];
|
|
17
|
+
|
|
18
|
+
parts.forEach((part, index) => {
|
|
19
|
+
nodes.push(part);
|
|
20
|
+
if (index < matches.length) {
|
|
21
|
+
nodes.push(
|
|
22
|
+
<mark
|
|
23
|
+
key={`mark-${index}`}
|
|
24
|
+
className="rounded bg-black/10 dark:bg-white/20 px-1"
|
|
25
|
+
>
|
|
26
|
+
{matches[index]}
|
|
27
|
+
</mark>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return nodes;
|
|
33
|
+
};
|
|
3
34
|
|
|
4
35
|
function AgentStartedIcon({ className = "" }) {
|
|
5
36
|
return (
|
|
@@ -12,6 +43,106 @@ function AgentStartedIcon({ className = "" }) {
|
|
|
12
43
|
);
|
|
13
44
|
}
|
|
14
45
|
|
|
46
|
+
function MonoBlock({ children }) {
|
|
47
|
+
return (
|
|
48
|
+
<pre className="text-xs sm:text-sm font-mono leading-relaxed whitespace-pre-wrap break-words overflow-auto max-h-[60vh] p-6 rounded-[24px] border border-black bg-black text-white/90 dark:border-white dark:bg-white dark:text-black/90">
|
|
49
|
+
{children}
|
|
50
|
+
</pre>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function TextBlock({ children }) {
|
|
55
|
+
return (
|
|
56
|
+
<div className="rounded-[24px] border border-black/10 dark:border-white/10 bg-black/[0.02] dark:bg-white/[0.04] p-6 text-base sm:text-lg leading-relaxed whitespace-pre-wrap break-words text-black/80 dark:text-white/80">
|
|
57
|
+
{children}
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function PanelBody({ as: Component = "div", className = "", children }) {
|
|
63
|
+
return (
|
|
64
|
+
<Component
|
|
65
|
+
className={`border-t border-black/10 dark:border-white/10 p-6 ${className}`}
|
|
66
|
+
>
|
|
67
|
+
{children}
|
|
68
|
+
</Component>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function RawToggle({ open, onToggle }) {
|
|
73
|
+
return (
|
|
74
|
+
<button
|
|
75
|
+
type="button"
|
|
76
|
+
onClick={onToggle}
|
|
77
|
+
className="px-3 py-1 rounded-full text-[10px] font-bold tracking-[0.2em] uppercase border border-black/20 dark:border-white/20 text-black/60 dark:text-white/60 hover:text-black dark:hover:text-white hover:border-black/40 dark:hover:border-white/40 transition-colors"
|
|
78
|
+
aria-pressed={open}
|
|
79
|
+
aria-label={open ? "Hide raw event JSON" : "Show raw event JSON"}
|
|
80
|
+
>
|
|
81
|
+
Raw
|
|
82
|
+
</button>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const renderInlineValue = (value) => {
|
|
87
|
+
if (value === null || value === undefined) {
|
|
88
|
+
return <span className="italic">—</span>;
|
|
89
|
+
}
|
|
90
|
+
if (typeof value === "string") {
|
|
91
|
+
if (value.length > 140 || value.includes("\n")) {
|
|
92
|
+
return <TextBlock>{value}</TextBlock>;
|
|
93
|
+
}
|
|
94
|
+
return (
|
|
95
|
+
<span className="text-lg sm:text-xl font-semibold whitespace-pre-wrap break-words">
|
|
96
|
+
{value}
|
|
97
|
+
</span>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
if (typeof value === "object") {
|
|
101
|
+
return <pre className="raw-json-block raw-json-block--full">{renderJsonWithHighlight(value)}</pre>;
|
|
102
|
+
}
|
|
103
|
+
return <span className="text-lg sm:text-xl font-semibold break-words">{String(value)}</span>;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
function renderJsonWithHighlight(value) {
|
|
107
|
+
const raw = JSON.stringify(value, null, 2);
|
|
108
|
+
const regex =
|
|
109
|
+
/("(?:\\u[a-fA-F0-9]{4}|\\[^u]|[^\\"])*"(?:\s*:)?|\btrue\b|\bfalse\b|\bnull\b|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g;
|
|
110
|
+
const nodes = [];
|
|
111
|
+
let lastIndex = 0;
|
|
112
|
+
let match;
|
|
113
|
+
|
|
114
|
+
while ((match = regex.exec(raw)) !== null) {
|
|
115
|
+
const { index } = match;
|
|
116
|
+
if (index > lastIndex) {
|
|
117
|
+
nodes.push(raw.slice(lastIndex, index));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const token = match[0];
|
|
121
|
+
let className = "json-token-number";
|
|
122
|
+
if (token.startsWith('"')) {
|
|
123
|
+
className = token.endsWith(":") ? "json-token-key" : "json-token-string";
|
|
124
|
+
} else if (token === "true" || token === "false") {
|
|
125
|
+
className = "json-token-boolean";
|
|
126
|
+
} else if (token === "null") {
|
|
127
|
+
className = "json-token-null";
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
nodes.push(
|
|
131
|
+
<span key={`json-${index}`} className={className}>
|
|
132
|
+
{token}
|
|
133
|
+
</span>
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
lastIndex = index + token.length;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (lastIndex < raw.length) {
|
|
140
|
+
nodes.push(raw.slice(lastIndex));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return nodes;
|
|
144
|
+
}
|
|
145
|
+
|
|
15
146
|
export default function ContentCard({ item }) {
|
|
16
147
|
if (!item) return null;
|
|
17
148
|
|
|
@@ -21,10 +152,42 @@ export default function ContentCard({ item }) {
|
|
|
21
152
|
second: "2-digit",
|
|
22
153
|
});
|
|
23
154
|
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
155
|
+
const [promptQuery, setPromptQuery] = useState("");
|
|
156
|
+
const [showRaw, setShowRaw] = useState(false);
|
|
157
|
+
const [showPromptSearch, setShowPromptSearch] = useState(false);
|
|
158
|
+
|
|
159
|
+
// Full-auto countdown logic (must be at top level for hooks rules)
|
|
160
|
+
const isInteractionEvent = item.event === "PROMPT_REQUESTED" || item.event === "INTERACTION_REQUESTED";
|
|
161
|
+
const isFullAuto = isInteractionEvent && !!item.fullAuto;
|
|
162
|
+
const interactionType = item.type || "text";
|
|
163
|
+
const interactionOptions = item.options || [];
|
|
164
|
+
const autoSelectDelay = item.autoSelectDelay ?? 20;
|
|
165
|
+
const shouldShowCountdown = isFullAuto && interactionType === "choice" && interactionOptions.length > 0;
|
|
166
|
+
|
|
167
|
+
// Calculate countdown directly - triggers re-render via tick state
|
|
168
|
+
const [tick, setTick] = useState(0);
|
|
169
|
+
|
|
170
|
+
const countdown = (() => {
|
|
171
|
+
if (!shouldShowCountdown) return null;
|
|
172
|
+
const eventTime = new Date(item.timestamp).getTime();
|
|
173
|
+
const elapsed = Math.floor((Date.now() - eventTime) / 1000);
|
|
174
|
+
return autoSelectDelay - elapsed;
|
|
175
|
+
})();
|
|
176
|
+
|
|
177
|
+
useEffect(() => {
|
|
178
|
+
if (!shouldShowCountdown) return;
|
|
179
|
+
|
|
180
|
+
const timer = setInterval(() => {
|
|
181
|
+
setTick(t => t + 1); // Force re-render to recalculate countdown
|
|
182
|
+
}, 1000);
|
|
183
|
+
|
|
184
|
+
return () => clearInterval(timer);
|
|
185
|
+
}, [shouldShowCountdown, item.timestamp]);
|
|
186
|
+
|
|
187
|
+
const formatKey = (key) => {
|
|
188
|
+
const spaced = key.replace(/_/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
189
|
+
return spaced.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
190
|
+
};
|
|
28
191
|
|
|
29
192
|
const tryParseJson = (raw) => {
|
|
30
193
|
try {
|
|
@@ -109,11 +272,73 @@ export default function ContentCard({ item }) {
|
|
|
109
272
|
};
|
|
110
273
|
};
|
|
111
274
|
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
275
|
+
const splitPromptSections = (promptText) => {
|
|
276
|
+
if (typeof promptText !== "string" || !promptText.trim()) return [];
|
|
277
|
+
const lines = promptText.split(/\r?\n/);
|
|
278
|
+
const sections = [];
|
|
279
|
+
let current = null;
|
|
280
|
+
let inFence = false;
|
|
281
|
+
|
|
282
|
+
const pushSection = () => {
|
|
283
|
+
if (!current) return;
|
|
284
|
+
const content = current.content.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
285
|
+
if (!content) {
|
|
286
|
+
current = null;
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
sections.push({ title: current.title || "Prompt", content });
|
|
290
|
+
current = null;
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
lines.forEach((line) => {
|
|
294
|
+
const trimmed = line.trim();
|
|
295
|
+
if (/^```/.test(trimmed)) {
|
|
296
|
+
inFence = !inFence;
|
|
297
|
+
}
|
|
298
|
+
if (!trimmed) {
|
|
299
|
+
if (current) current.content.push("");
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (!inFence && /^---+$/.test(trimmed)) {
|
|
303
|
+
pushSection();
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const headingMatch = !inFence ? /^(#{1,6})\s+(.+)$/.exec(trimmed) : null;
|
|
307
|
+
if (headingMatch) {
|
|
308
|
+
pushSection();
|
|
309
|
+
current = { title: headingMatch[2].trim(), content: [] };
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (!current) current = { title: "Prompt", content: [] };
|
|
313
|
+
current.content.push(line);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
pushSection();
|
|
317
|
+
return sections;
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
const renderPromptSectionContent = (content, query) => {
|
|
321
|
+
if (!content) {
|
|
322
|
+
return (
|
|
323
|
+
<PanelBody className="italic text-black/40 dark:text-white/40">—</PanelBody>
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
if (content.includes("```")) {
|
|
327
|
+
return (
|
|
328
|
+
<PanelBody
|
|
329
|
+
as="pre"
|
|
330
|
+
className="text-xs sm:text-sm font-mono leading-relaxed whitespace-pre-wrap break-words overflow-auto max-h-[60vh] text-black/90 dark:text-white/90"
|
|
331
|
+
>
|
|
332
|
+
{highlightText(content, query)}
|
|
333
|
+
</PanelBody>
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
return (
|
|
337
|
+
<PanelBody className="text-base sm:text-lg leading-relaxed whitespace-pre-wrap break-words text-black/80 dark:text-white/80">
|
|
338
|
+
{highlightText(content, query)}
|
|
339
|
+
</PanelBody>
|
|
340
|
+
);
|
|
341
|
+
};
|
|
117
342
|
|
|
118
343
|
const renderValue = (value) => {
|
|
119
344
|
if (value === null || value === undefined) {
|
|
@@ -122,7 +347,7 @@ export default function ContentCard({ item }) {
|
|
|
122
347
|
|
|
123
348
|
if (typeof value === "boolean") {
|
|
124
349
|
const base =
|
|
125
|
-
"inline-flex items-center rounded-full px-3 py-1 text-
|
|
350
|
+
"inline-flex items-center rounded-full px-3 py-1 text-[11px] font-semibold tracking-[0.16em] uppercase border";
|
|
126
351
|
return value ? (
|
|
127
352
|
<span className={`${base} bg-black text-white border-black dark:bg-white dark:text-black dark:border-white`}>
|
|
128
353
|
Yes
|
|
@@ -137,15 +362,15 @@ export default function ContentCard({ item }) {
|
|
|
137
362
|
if (typeof value === "string") {
|
|
138
363
|
const parsedJson = extractJsonFromString(value);
|
|
139
364
|
if (parsedJson !== null) {
|
|
140
|
-
return
|
|
365
|
+
return renderStructured(parsedJson);
|
|
141
366
|
}
|
|
142
367
|
|
|
143
368
|
if (value.length > 140 || value.includes("\n")) {
|
|
144
|
-
return <
|
|
369
|
+
return <TextBlock>{value}</TextBlock>;
|
|
145
370
|
}
|
|
146
371
|
|
|
147
372
|
return (
|
|
148
|
-
<span className="text-
|
|
373
|
+
<span className="text-lg sm:text-xl font-semibold whitespace-pre-wrap break-words">
|
|
149
374
|
{value}
|
|
150
375
|
</span>
|
|
151
376
|
);
|
|
@@ -155,7 +380,7 @@ export default function ContentCard({ item }) {
|
|
|
155
380
|
return <MonoBlock>{JSON.stringify(value, null, 2)}</MonoBlock>;
|
|
156
381
|
}
|
|
157
382
|
|
|
158
|
-
return <span className="text-
|
|
383
|
+
return <span className="text-lg sm:text-xl font-semibold break-words">{String(value)}</span>;
|
|
159
384
|
};
|
|
160
385
|
|
|
161
386
|
const MAX_STRUCT_DEPTH = 6;
|
|
@@ -181,9 +406,9 @@ export default function ContentCard({ item }) {
|
|
|
181
406
|
|
|
182
407
|
if (isSimple) {
|
|
183
408
|
return (
|
|
184
|
-
<div className="space-y-
|
|
409
|
+
<div className="space-y-3">
|
|
185
410
|
{items.map((entry, index) => (
|
|
186
|
-
<div key={`item-${index}`} className="text-base">
|
|
411
|
+
<div key={`item-${index}`} className="text-base sm:text-lg">
|
|
187
412
|
{renderValue(entry)}
|
|
188
413
|
</div>
|
|
189
414
|
))}
|
|
@@ -197,7 +422,7 @@ export default function ContentCard({ item }) {
|
|
|
197
422
|
{items.map((entry, index) => (
|
|
198
423
|
<div
|
|
199
424
|
key={`item-${index}`}
|
|
200
|
-
className="rounded-[20px] border border-black
|
|
425
|
+
className="rounded-[20px] border border-black/10 dark:border-white/10 bg-black/[0.02] dark:bg-white/[0.04] p-4"
|
|
201
426
|
>
|
|
202
427
|
{renderKeyValueGrid(Object.entries(entry), depth)}
|
|
203
428
|
</div>
|
|
@@ -211,9 +436,9 @@ export default function ContentCard({ item }) {
|
|
|
211
436
|
{items.map((entry, index) => (
|
|
212
437
|
<div
|
|
213
438
|
key={`item-${index}`}
|
|
214
|
-
className="space-y-4 rounded-[24px] border border-black
|
|
439
|
+
className="space-y-4 rounded-[24px] border border-black/10 dark:border-white/10 bg-black/[0.02] dark:bg-white/[0.04] p-5"
|
|
215
440
|
>
|
|
216
|
-
<div className="text-[
|
|
441
|
+
<div className="text-[11px] font-semibold tracking-[0.24em] uppercase text-black/50 dark:text-white/50">
|
|
217
442
|
Item {index + 1}
|
|
218
443
|
</div>
|
|
219
444
|
{renderStructured(entry, depth + 1)}
|
|
@@ -245,13 +470,15 @@ export default function ContentCard({ item }) {
|
|
|
245
470
|
|
|
246
471
|
function renderKeyValueGrid(entries, depth = 0) {
|
|
247
472
|
return (
|
|
248
|
-
<div className="space-y-
|
|
473
|
+
<div className="space-y-6">
|
|
249
474
|
{entries.map(([key, value]) => (
|
|
250
|
-
<div key={key} className="
|
|
251
|
-
<div className="text-
|
|
475
|
+
<div key={key} className="grid gap-2 sm:grid-cols-[160px_1fr] sm:items-start min-w-0">
|
|
476
|
+
<div className="text-xs font-semibold text-black/50 dark:text-white/50 break-words">
|
|
252
477
|
{formatKey(key)}
|
|
253
478
|
</div>
|
|
254
|
-
<div className="text-base
|
|
479
|
+
<div className="text-base sm:text-lg leading-relaxed break-words min-w-0">
|
|
480
|
+
{renderStructured(value, depth + 1)}
|
|
481
|
+
</div>
|
|
255
482
|
</div>
|
|
256
483
|
))}
|
|
257
484
|
</div>
|
|
@@ -264,136 +491,390 @@ export default function ContentCard({ item }) {
|
|
|
264
491
|
const { cleanedPrompt, contextTitle, contextData, contextRaw } = extractContextFromPrompt(
|
|
265
492
|
item.prompt || ""
|
|
266
493
|
);
|
|
494
|
+
const promptSections = splitPromptSections(cleanedPrompt);
|
|
495
|
+
const trimmedPromptQuery = promptQuery.trim();
|
|
496
|
+
const normalizedPromptQuery = trimmedPromptQuery.toLowerCase();
|
|
497
|
+
const filteredPromptSections = trimmedPromptQuery
|
|
498
|
+
? promptSections.filter((section) =>
|
|
499
|
+
`${section.title}\n${section.content}`.toLowerCase().includes(normalizedPromptQuery)
|
|
500
|
+
)
|
|
501
|
+
: [];
|
|
502
|
+
const promptCountLabel = promptSections.length ? `${promptSections.length} sections` : null;
|
|
267
503
|
|
|
268
504
|
const contextTokenCount = estimateTokensFromText(contextRaw || "");
|
|
269
505
|
const contextMeta = contextRaw ? `~${formatTokenCount(contextTokenCount)}` : "";
|
|
270
506
|
|
|
271
507
|
content = (
|
|
272
|
-
<div className="space-y-
|
|
273
|
-
<div className="space-y-
|
|
508
|
+
<div className="space-y-10 py-6">
|
|
509
|
+
<div className="space-y-3 text-center">
|
|
274
510
|
<AgentStartedIcon />
|
|
275
|
-
<div className="space-y-
|
|
276
|
-
<
|
|
277
|
-
|
|
511
|
+
<div className="space-y-1">
|
|
512
|
+
<div className="text-[11px] font-semibold tracking-[0.28em] uppercase text-black/50 dark:text-white/50">
|
|
513
|
+
Agent started
|
|
514
|
+
</div>
|
|
515
|
+
<h2 className="text-3xl sm:text-4xl font-black tracking-tight">{item.agent}</h2>
|
|
278
516
|
</div>
|
|
279
517
|
</div>
|
|
280
518
|
|
|
281
519
|
{contextTitle && contextData !== null && (
|
|
282
|
-
<details className="group rounded-[24px] border border-black dark:border-white">
|
|
520
|
+
<details className="group rounded-[24px] border border-black/10 dark:border-white/10 bg-black/[0.02] dark:bg-white/[0.04]">
|
|
283
521
|
<summary className="cursor-pointer select-none px-6 py-5 flex items-center justify-between gap-6 [&::-webkit-details-marker]:hidden">
|
|
284
522
|
<div className="flex items-center gap-3">
|
|
285
523
|
<ChevronRight className="w-4 h-4 transition-transform group-open:rotate-90" />
|
|
286
524
|
{contextTitle === "Current Context" ? (
|
|
287
525
|
<Brain className="w-4 h-4" aria-hidden="true" />
|
|
288
526
|
) : null}
|
|
289
|
-
<div className="text-
|
|
527
|
+
<div className="text-[11px] font-semibold tracking-[0.24em] uppercase text-black/60 dark:text-white/60">
|
|
290
528
|
{contextTitle}
|
|
291
529
|
</div>
|
|
292
530
|
</div>
|
|
293
|
-
{contextMeta ? <div className="text-xs font-mono">{contextMeta}</div> : null}
|
|
531
|
+
{contextMeta ? <div className="text-xs font-mono text-black/50 dark:text-white/50">{contextMeta}</div> : null}
|
|
294
532
|
</summary>
|
|
295
533
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
)}
|
|
302
|
-
|
|
534
|
+
{typeof contextData === "string" ? (
|
|
535
|
+
<PanelBody className="text-base sm:text-lg leading-relaxed whitespace-pre-wrap break-words text-black/80 dark:text-white/80">
|
|
536
|
+
{contextData}
|
|
537
|
+
</PanelBody>
|
|
538
|
+
) : (
|
|
539
|
+
<PanelBody>{renderStructured(contextData)}</PanelBody>
|
|
540
|
+
)}
|
|
303
541
|
</details>
|
|
304
542
|
)}
|
|
305
543
|
|
|
306
|
-
<div className="
|
|
307
|
-
|
|
544
|
+
<div className="space-y-4">
|
|
545
|
+
<div className="flex flex-wrap items-center justify-between gap-3">
|
|
546
|
+
<div className="text-[11px] font-semibold tracking-[0.24em] uppercase text-black/50 dark:text-white/50">
|
|
547
|
+
Prompt
|
|
548
|
+
</div>
|
|
549
|
+
{promptCountLabel ? (
|
|
550
|
+
<div className="text-xs font-mono text-black/40 dark:text-white/40">
|
|
551
|
+
{promptCountLabel}
|
|
552
|
+
</div>
|
|
553
|
+
) : null}
|
|
554
|
+
</div>
|
|
555
|
+
|
|
556
|
+
<div className="space-y-4">
|
|
557
|
+
{promptSections.length > 0 ? (
|
|
558
|
+
promptSections.map((section, index) => {
|
|
559
|
+
const isInstructions = /(^| )instructions$/i.test(section.title.trim());
|
|
560
|
+
const shouldOpen = index === 0 || isInstructions;
|
|
561
|
+
const lineCount = section.content
|
|
562
|
+
? section.content.split(/\r?\n/).length
|
|
563
|
+
: 0;
|
|
564
|
+
return (
|
|
565
|
+
<details
|
|
566
|
+
key={`${section.title}-${index}`}
|
|
567
|
+
open={shouldOpen}
|
|
568
|
+
className="group rounded-[24px] border border-black/10 dark:border-white/10 bg-black/[0.02] dark:bg-white/[0.04]"
|
|
569
|
+
>
|
|
570
|
+
<summary className="cursor-pointer select-none px-6 py-5 flex items-center justify-between gap-6 [&::-webkit-details-marker]:hidden">
|
|
571
|
+
<div className="flex items-center gap-3 min-w-0">
|
|
572
|
+
<ChevronRight className="w-4 h-4 transition-transform group-open:rotate-90" />
|
|
573
|
+
<div className="text-[11px] font-semibold tracking-[0.24em] uppercase text-black/60 dark:text-white/60 break-words">
|
|
574
|
+
{section.title || "Prompt"}
|
|
575
|
+
</div>
|
|
576
|
+
</div>
|
|
577
|
+
{lineCount ? (
|
|
578
|
+
<div className="text-xs font-mono text-black/40 dark:text-white/40">
|
|
579
|
+
{lineCount} lines
|
|
580
|
+
</div>
|
|
581
|
+
) : null}
|
|
582
|
+
</summary>
|
|
583
|
+
{renderPromptSectionContent(section.content, "")}
|
|
584
|
+
</details>
|
|
585
|
+
);
|
|
586
|
+
})
|
|
587
|
+
) : (
|
|
588
|
+
<TextBlock>{cleanedPrompt}</TextBlock>
|
|
589
|
+
)}
|
|
590
|
+
</div>
|
|
308
591
|
</div>
|
|
592
|
+
|
|
593
|
+
{showPromptSearch ? (
|
|
594
|
+
<div className="fixed inset-0 z-40 bg-white text-black dark:bg-black dark:text-white">
|
|
595
|
+
<div className="h-full w-full overflow-y-auto custom-scroll px-6 sm:px-10 py-10">
|
|
596
|
+
<div className="max-w-3xl mx-auto space-y-8">
|
|
597
|
+
<div className="flex flex-wrap items-center justify-between gap-4">
|
|
598
|
+
<div className="text-[11px] font-semibold tracking-[0.32em] uppercase text-black/50 dark:text-white/50">
|
|
599
|
+
Prompt search
|
|
600
|
+
</div>
|
|
601
|
+
<button
|
|
602
|
+
type="button"
|
|
603
|
+
onClick={() => setShowPromptSearch(false)}
|
|
604
|
+
className="text-[10px] font-bold tracking-[0.2em] uppercase text-black/60 hover:text-black dark:text-white/60 dark:hover:text-white"
|
|
605
|
+
>
|
|
606
|
+
Close
|
|
607
|
+
</button>
|
|
608
|
+
</div>
|
|
609
|
+
|
|
610
|
+
<div className="relative">
|
|
611
|
+
<Search className="w-4 h-4 text-black/50 dark:text-white/60 absolute left-4 top-1/2 -translate-y-1/2" />
|
|
612
|
+
<input
|
|
613
|
+
value={promptQuery}
|
|
614
|
+
onChange={(event) => setPromptQuery(event.target.value)}
|
|
615
|
+
placeholder="Search the prompt..."
|
|
616
|
+
className="w-full rounded-full border border-black/20 dark:border-white/20 bg-transparent py-4 pl-11 pr-10 text-base text-black dark:text-white placeholder:text-black/40 dark:placeholder:text-white/40 focus:outline-none focus:ring-1 focus:ring-black/30 dark:focus:ring-white/40"
|
|
617
|
+
autoFocus
|
|
618
|
+
/>
|
|
619
|
+
{trimmedPromptQuery ? (
|
|
620
|
+
<button
|
|
621
|
+
type="button"
|
|
622
|
+
onClick={() => setPromptQuery("")}
|
|
623
|
+
aria-label="Clear prompt search"
|
|
624
|
+
className="absolute right-3 top-1/2 -translate-y-1/2 rounded-full p-1 text-black/60 hover:text-black dark:text-white/60 dark:hover:text-white"
|
|
625
|
+
>
|
|
626
|
+
<X className="w-3.5 h-3.5" />
|
|
627
|
+
</button>
|
|
628
|
+
) : null}
|
|
629
|
+
</div>
|
|
630
|
+
|
|
631
|
+
{trimmedPromptQuery ? (
|
|
632
|
+
filteredPromptSections.length > 0 ? (
|
|
633
|
+
<div className="space-y-4">
|
|
634
|
+
{filteredPromptSections.map((section, index) => {
|
|
635
|
+
const lineCount = section.content
|
|
636
|
+
? section.content.split(/\r?\n/).length
|
|
637
|
+
: 0;
|
|
638
|
+
return (
|
|
639
|
+
<div
|
|
640
|
+
key={`${section.title}-${index}`}
|
|
641
|
+
className="rounded-[22px] border border-black/10 dark:border-white/15 bg-black/[0.02] dark:bg-white/5 overflow-hidden"
|
|
642
|
+
>
|
|
643
|
+
<div className="px-5 py-4 flex items-center justify-between gap-4">
|
|
644
|
+
<div className="text-[11px] font-semibold tracking-[0.24em] uppercase text-black/70 dark:text-white/70 break-words">
|
|
645
|
+
{highlightText(section.title || "Prompt", trimmedPromptQuery)}
|
|
646
|
+
</div>
|
|
647
|
+
{lineCount ? (
|
|
648
|
+
<div className="text-xs font-mono text-black/50 dark:text-white/50">
|
|
649
|
+
{lineCount} lines
|
|
650
|
+
</div>
|
|
651
|
+
) : null}
|
|
652
|
+
</div>
|
|
653
|
+
<div className="border-t border-black/10 dark:border-white/10 p-5">
|
|
654
|
+
<pre className="m-0 text-xs sm:text-sm font-mono leading-relaxed whitespace-pre-wrap break-words text-black/85 dark:text-white/85">
|
|
655
|
+
{highlightText(section.content || "", trimmedPromptQuery)}
|
|
656
|
+
</pre>
|
|
657
|
+
</div>
|
|
658
|
+
</div>
|
|
659
|
+
);
|
|
660
|
+
})}
|
|
661
|
+
</div>
|
|
662
|
+
) : (
|
|
663
|
+
<div className="rounded-[20px] border border-black/10 dark:border-white/10 bg-black/[0.02] dark:bg-white/5 p-5 text-sm text-black/60 dark:text-white/60">
|
|
664
|
+
No prompt sections match "{trimmedPromptQuery}".
|
|
665
|
+
</div>
|
|
666
|
+
)
|
|
667
|
+
) : (
|
|
668
|
+
<div className="text-sm text-black/50 dark:text-white/50">
|
|
669
|
+
Start typing to filter prompt sections.
|
|
670
|
+
</div>
|
|
671
|
+
)}
|
|
672
|
+
</div>
|
|
673
|
+
</div>
|
|
674
|
+
</div>
|
|
675
|
+
) : null}
|
|
309
676
|
</div>
|
|
310
677
|
);
|
|
311
678
|
} else if (item.event && item.event.startsWith("WORKFLOW_")) {
|
|
312
679
|
const step = item.event.replace("WORKFLOW_", "");
|
|
313
680
|
content = (
|
|
314
|
-
<div className="min-h-[50vh] flex flex-col items-center justify-center text-center space-y-8 py-
|
|
315
|
-
<div className="text-[10px] font-
|
|
681
|
+
<div className="min-h-[50vh] flex flex-col items-center justify-center text-center space-y-8 py-16">
|
|
682
|
+
<div className="text-[10px] font-semibold tracking-[0.4em] uppercase text-black/50 dark:text-white/50">
|
|
316
683
|
Lifecycle status
|
|
317
684
|
</div>
|
|
318
685
|
|
|
319
|
-
<div className="w-full max-w-5xl rounded-[28px] border border-black
|
|
320
|
-
<h2 className="text-
|
|
686
|
+
<div className="w-full max-w-5xl rounded-[28px] border border-black/10 dark:border-white/10 bg-black/[0.02] dark:bg-white/[0.04] p-10">
|
|
687
|
+
<h2 className="text-4xl sm:text-6xl md:text-7xl font-black tracking-tighter leading-none uppercase">
|
|
321
688
|
{step}
|
|
322
689
|
</h2>
|
|
323
690
|
|
|
324
691
|
{item.error && (
|
|
325
|
-
<
|
|
326
|
-
{item.error}
|
|
327
|
-
</
|
|
692
|
+
<div className="mt-6">
|
|
693
|
+
<MonoBlock>{item.error}</MonoBlock>
|
|
694
|
+
</div>
|
|
328
695
|
)}
|
|
329
696
|
</div>
|
|
330
697
|
</div>
|
|
331
698
|
);
|
|
332
699
|
} else if (item.event === "PROMPT_REQUESTED" || item.event === "INTERACTION_REQUESTED") {
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
].filter((entry) => entry[1] !== undefined);
|
|
700
|
+
const isInteraction = item.event === "INTERACTION_REQUESTED";
|
|
701
|
+
const label = isInteraction ? "Interaction needed" : "Question asked";
|
|
702
|
+
const headline = item.question || item.prompt || "Prompt";
|
|
703
|
+
const agentName = item.agent || item.slug || "Agent";
|
|
338
704
|
|
|
339
705
|
content = (
|
|
340
|
-
<div className="
|
|
341
|
-
<div className="text-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
706
|
+
<div className="py-10 sm:py-14 space-y-8">
|
|
707
|
+
<div className="text-center space-y-4">
|
|
708
|
+
<AgentStartedIcon className="w-12 h-12" />
|
|
709
|
+
<div className="text-[11px] font-semibold tracking-[0.32em] uppercase text-black/50 dark:text-white/50">
|
|
710
|
+
{label}
|
|
711
|
+
</div>
|
|
712
|
+
<div className="text-lg sm:text-xl font-semibold text-black/70 dark:text-white/70">
|
|
713
|
+
{agentName} needs your input
|
|
714
|
+
</div>
|
|
715
|
+
</div>
|
|
716
|
+
<div className="text-3xl sm:text-4xl md:text-5xl font-black tracking-tight leading-tight text-center text-balance">
|
|
717
|
+
{headline}
|
|
345
718
|
</div>
|
|
346
719
|
|
|
347
|
-
{
|
|
348
|
-
<div className="text-
|
|
720
|
+
{shouldShowCountdown && countdown !== null && (
|
|
721
|
+
<div className="text-center">
|
|
722
|
+
<div className={`inline-flex items-center gap-2 px-4 py-2 rounded-full ${
|
|
723
|
+
countdown > 0
|
|
724
|
+
? "bg-black/5 dark:bg-white/10"
|
|
725
|
+
: "bg-black text-white dark:bg-white dark:text-black"
|
|
726
|
+
}`}>
|
|
727
|
+
<span className="text-xl">⚡</span>
|
|
728
|
+
<span className={`text-sm font-semibold ${
|
|
729
|
+
countdown > 0 ? "text-black/70 dark:text-white/70" : ""
|
|
730
|
+
}`}>
|
|
731
|
+
{countdown > 0
|
|
732
|
+
? `Agent deciding in ${countdown}s...`
|
|
733
|
+
: "Auto-selected"}
|
|
734
|
+
</span>
|
|
735
|
+
</div>
|
|
736
|
+
</div>
|
|
349
737
|
)}
|
|
350
738
|
|
|
351
|
-
{
|
|
739
|
+
{interactionType === "choice" && interactionOptions.length > 0 && (
|
|
740
|
+
<div className="space-y-3 max-w-2xl mx-auto">
|
|
741
|
+
<div className="text-[11px] font-semibold tracking-[0.24em] uppercase text-black/50 dark:text-white/50 text-center">
|
|
742
|
+
Options
|
|
743
|
+
</div>
|
|
744
|
+
<div className="space-y-2">
|
|
745
|
+
{interactionOptions.map((opt, index) => (
|
|
746
|
+
<div
|
|
747
|
+
key={opt.key || index}
|
|
748
|
+
className={`p-4 rounded-2xl border transition-all ${
|
|
749
|
+
index === 0
|
|
750
|
+
? "border-black bg-black text-white dark:border-white dark:bg-white dark:text-black"
|
|
751
|
+
: "border-black/10 dark:border-white/10 bg-black/[0.02] dark:bg-white/[0.03]"
|
|
752
|
+
}`}
|
|
753
|
+
>
|
|
754
|
+
<div className="flex items-center gap-3">
|
|
755
|
+
<div className={`font-bold ${index === 0 ? "" : "text-black dark:text-white"}`}>
|
|
756
|
+
{opt.label || opt.key}
|
|
757
|
+
{index === 0 && (
|
|
758
|
+
<span className={`ml-2 text-xs font-medium px-2 py-0.5 rounded-full ${
|
|
759
|
+
"bg-white/20 dark:bg-black/20"
|
|
760
|
+
}`}>
|
|
761
|
+
Recommended
|
|
762
|
+
</span>
|
|
763
|
+
)}
|
|
764
|
+
</div>
|
|
765
|
+
</div>
|
|
766
|
+
{opt.description && (
|
|
767
|
+
<div className={`text-sm mt-1 ${
|
|
768
|
+
index === 0 ? "text-white/70 dark:text-black/70" : "text-black/50 dark:text-white/50"
|
|
769
|
+
}`}>
|
|
770
|
+
{opt.description}
|
|
771
|
+
</div>
|
|
772
|
+
)}
|
|
773
|
+
</div>
|
|
774
|
+
))}
|
|
775
|
+
</div>
|
|
776
|
+
{item.allowCustom && (
|
|
777
|
+
<div className="text-xs text-center text-black/40 dark:text-white/40 mt-2">
|
|
778
|
+
Custom response allowed
|
|
779
|
+
</div>
|
|
780
|
+
)}
|
|
781
|
+
</div>
|
|
782
|
+
)}
|
|
352
783
|
</div>
|
|
353
784
|
);
|
|
354
|
-
} else if (item.event === "
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
const details = [
|
|
358
|
-
["slug", item.slug],
|
|
359
|
-
["targetKey", item.targetKey],
|
|
360
|
-
].filter((entry) => entry[1] !== undefined);
|
|
785
|
+
} else if (item.event === "INTERACTION_AUTO_RESOLVED" || item.event === "PROMPT_AUTO_ANSWERED") {
|
|
786
|
+
const autoSelected = item.autoSelected || "Unknown";
|
|
787
|
+
const agentName = item.agent || item.slug || "Agent";
|
|
361
788
|
|
|
362
789
|
content = (
|
|
363
|
-
<div className="space-y-
|
|
364
|
-
<div className="
|
|
790
|
+
<div className="space-y-8 py-6">
|
|
791
|
+
<div className="space-y-4 text-center">
|
|
792
|
+
<div className="mx-auto w-14 h-14 rounded-full border-2 border-black dark:border-white bg-black dark:bg-white flex items-center justify-center">
|
|
793
|
+
<span className="text-2xl">⚡</span>
|
|
794
|
+
</div>
|
|
795
|
+
<div className="text-[11px] font-semibold tracking-[0.24em] uppercase text-black/50 dark:text-white/50">
|
|
796
|
+
Auto-selected
|
|
797
|
+
</div>
|
|
798
|
+
<div className="text-lg sm:text-xl font-semibold text-black/70 dark:text-white/70">
|
|
799
|
+
{agentName} decided automatically
|
|
800
|
+
</div>
|
|
801
|
+
</div>
|
|
802
|
+
<div className="rounded-[24px] border-2 border-black bg-black text-white dark:border-white dark:bg-white dark:text-black p-6 text-center max-w-md mx-auto">
|
|
803
|
+
<div className="text-2xl sm:text-3xl font-black tracking-tight">
|
|
804
|
+
{autoSelected}
|
|
805
|
+
</div>
|
|
806
|
+
</div>
|
|
807
|
+
</div>
|
|
808
|
+
);
|
|
809
|
+
} else if (item.event === "INTERACTION_RESOLVED") {
|
|
810
|
+
const resolvedCopy =
|
|
811
|
+
item.source === "remote" ? "Remote response received." : "Response captured.";
|
|
812
|
+
const sourceCopy =
|
|
813
|
+
item.source === "remote" ? "Received from remote" : item.source ? `Received from ${item.source}` : "Received";
|
|
814
|
+
const agentName = item.agent || item.slug || "Agent";
|
|
365
815
|
|
|
366
|
-
|
|
367
|
-
|
|
816
|
+
content = (
|
|
817
|
+
<div className="space-y-8 py-6">
|
|
818
|
+
<div className="space-y-4 text-center">
|
|
819
|
+
<AgentStartedIcon className="w-12 h-12" />
|
|
820
|
+
<div className="text-[11px] font-semibold tracking-[0.24em] uppercase text-black/50 dark:text-white/50">
|
|
821
|
+
Interaction resolved
|
|
822
|
+
</div>
|
|
823
|
+
<div className="text-lg sm:text-xl font-semibold text-black/70 dark:text-white/70">
|
|
824
|
+
{agentName} received your response
|
|
825
|
+
</div>
|
|
368
826
|
</div>
|
|
827
|
+
<div className="rounded-[24px] border border-black/10 dark:border-white/10 bg-black/[0.02] dark:bg-white/[0.04] p-6 text-center">
|
|
828
|
+
<div className="text-xl sm:text-2xl font-semibold leading-relaxed text-balance">
|
|
829
|
+
{resolvedCopy}
|
|
830
|
+
</div>
|
|
831
|
+
<div className="mt-3 text-sm text-black/60 dark:text-white/60">
|
|
832
|
+
{sourceCopy}
|
|
833
|
+
</div>
|
|
834
|
+
</div>
|
|
835
|
+
</div>
|
|
836
|
+
);
|
|
837
|
+
} else if (item.event === "PROMPT_ANSWERED" || item.event === "INTERACTION_SUBMITTED") {
|
|
838
|
+
const responseValue = item.answer !== undefined ? item.answer : item.response;
|
|
369
839
|
|
|
370
|
-
|
|
840
|
+
content = (
|
|
841
|
+
<div className="space-y-8 py-6">
|
|
842
|
+
<div className="space-y-3">
|
|
843
|
+
<div className="text-[11px] font-semibold tracking-[0.24em] uppercase text-black/50 dark:text-white/50">
|
|
844
|
+
Response sent
|
|
845
|
+
</div>
|
|
846
|
+
{renderInlineValue(responseValue)}
|
|
847
|
+
</div>
|
|
371
848
|
</div>
|
|
372
849
|
);
|
|
373
850
|
} else if (item.event === "AGENT_COMPLETED") {
|
|
374
|
-
const details = [
|
|
375
|
-
["agent", item.agent],
|
|
376
|
-
["attempts", item.attempts],
|
|
377
|
-
].filter((entry) => entry[1] !== undefined);
|
|
378
|
-
|
|
379
851
|
content = (
|
|
380
|
-
<div className="space-y-
|
|
381
|
-
|
|
382
|
-
<
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
852
|
+
<div className="space-y-10 py-6">
|
|
853
|
+
<div className="rounded-[28px] border border-black/10 dark:border-white/10 bg-black/[0.02] dark:bg-white/[0.04] p-6 sm:p-8 text-center space-y-4">
|
|
854
|
+
<AgentStartedIcon className="w-12 h-12" />
|
|
855
|
+
<div className="text-[11px] font-semibold tracking-[0.24em] uppercase text-black/50 dark:text-white/50">
|
|
856
|
+
Agent completed
|
|
857
|
+
</div>
|
|
858
|
+
<div className="text-2xl sm:text-3xl font-black tracking-tight">
|
|
859
|
+
{item.agent || "Agent"}
|
|
860
|
+
</div>
|
|
861
|
+
{typeof item.attempts === "number" ? (
|
|
862
|
+
<div className="text-sm text-black/60 dark:text-white/60">
|
|
863
|
+
{item.attempts} {item.attempts === 1 ? "attempt" : "attempts"}
|
|
390
864
|
</div>
|
|
865
|
+
) : null}
|
|
866
|
+
</div>
|
|
391
867
|
|
|
392
|
-
|
|
868
|
+
{item.output !== undefined ? (
|
|
869
|
+
<div className="space-y-4">
|
|
870
|
+
<div className="text-[11px] font-semibold tracking-[0.24em] uppercase text-black/50 dark:text-white/50">
|
|
871
|
+
Answer
|
|
872
|
+
</div>
|
|
873
|
+
<div className="rounded-[24px] border border-black/10 dark:border-white/10 bg-black/[0.02] dark:bg-white/[0.04] p-6">
|
|
874
|
+
{renderStructured(item.output)}
|
|
875
|
+
</div>
|
|
393
876
|
</div>
|
|
394
|
-
)}
|
|
395
|
-
|
|
396
|
-
{details.length > 0 && <div className="pt-4">{renderKeyValueGrid(details)}</div>}
|
|
877
|
+
) : null}
|
|
397
878
|
</div>
|
|
398
879
|
);
|
|
399
880
|
} else {
|
|
@@ -401,26 +882,49 @@ export default function ContentCard({ item }) {
|
|
|
401
882
|
([key]) => key !== "event" && key !== "timestamp"
|
|
402
883
|
);
|
|
403
884
|
|
|
404
|
-
const fallbackEntries = entries.length > 0 ? entries : [["event", item.event || "Event"]];
|
|
405
|
-
|
|
406
885
|
content = (
|
|
407
|
-
<div className="space-y-
|
|
408
|
-
<div className="
|
|
409
|
-
|
|
886
|
+
<div className="space-y-6 py-6">
|
|
887
|
+
<div className="rounded-[24px] border border-black/10 dark:border-white/10 bg-black/[0.02] dark:bg-white/[0.04] p-6">
|
|
888
|
+
<div className="text-lg sm:text-xl font-semibold leading-relaxed text-balance">
|
|
889
|
+
{item.event ? item.event.replace(/_/g, " ") : "Event"}
|
|
890
|
+
</div>
|
|
891
|
+
<div className="mt-3 text-sm text-black/60 dark:text-white/60">
|
|
892
|
+
{entries.length > 0 ? "Open Raw for full details." : "Open Raw for more info."}
|
|
893
|
+
</div>
|
|
894
|
+
</div>
|
|
410
895
|
</div>
|
|
411
896
|
);
|
|
412
897
|
}
|
|
413
898
|
|
|
414
899
|
return (
|
|
415
|
-
<div className="w-full h-full flex flex-col overflow-y-auto custom-scroll px-12 bg-white text-black dark:bg-black dark:text-white">
|
|
900
|
+
<div className="w-full h-full flex flex-col overflow-y-auto custom-scroll px-6 sm:px-10 lg:px-12 bg-white text-black dark:bg-black dark:text-white">
|
|
416
901
|
<div className="content-width flex-1">
|
|
417
|
-
<div className="flex items-center justify-between pt-10">
|
|
418
|
-
<div className="
|
|
419
|
-
{
|
|
902
|
+
<div className="flex flex-wrap items-center justify-between gap-4 pt-8 sm:pt-10 pb-6 border-b border-black/10 dark:border-white/10">
|
|
903
|
+
<div className="flex items-center gap-3">
|
|
904
|
+
{item.event === "AGENT_STARTED" ? (
|
|
905
|
+
<button
|
|
906
|
+
type="button"
|
|
907
|
+
onClick={() => setShowPromptSearch(true)}
|
|
908
|
+
className="w-12 h-12 rounded-full bg-white text-black dark:bg-black dark:text-white border border-black/10 dark:border-white/10 flex items-center justify-center shadow-2xl shadow-black/20 dark:shadow-white/10 hover:scale-[1.02] transition-transform"
|
|
909
|
+
aria-label="Search prompt sections"
|
|
910
|
+
>
|
|
911
|
+
<Search className="w-5 h-5" />
|
|
912
|
+
</button>
|
|
913
|
+
) : null}
|
|
914
|
+
</div>
|
|
915
|
+
<div className="flex items-center gap-3">
|
|
916
|
+
<RawToggle open={showRaw} onToggle={() => setShowRaw((prev) => !prev)} />
|
|
917
|
+
<CopyButton text={item} />
|
|
420
918
|
</div>
|
|
421
|
-
<CopyButton text={item} />
|
|
422
919
|
</div>
|
|
423
|
-
|
|
920
|
+
<div className="pt-6">
|
|
921
|
+
<div className="text-xs font-mono text-black/50 dark:text-white/50">{time}</div>
|
|
922
|
+
</div>
|
|
923
|
+
{showRaw ? (
|
|
924
|
+
<pre className="raw-json-block">{renderJsonWithHighlight(item)}</pre>
|
|
925
|
+
) : (
|
|
926
|
+
content
|
|
927
|
+
)}
|
|
424
928
|
</div>
|
|
425
929
|
</div>
|
|
426
930
|
);
|