@lumea-labs/orchestrator 0.1.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.
Files changed (46) hide show
  1. package/README.md +21 -0
  2. package/dist/index.d.ts +21 -0
  3. package/dist/index.js +93 -0
  4. package/dist/lib/format.d.ts +3 -0
  5. package/dist/lib/format.js +9 -0
  6. package/dist/orchestrator-document.d.ts +37 -0
  7. package/dist/orchestrator-document.js +122 -0
  8. package/dist/plan-detail.d.ts +102 -0
  9. package/dist/plan-detail.js +385 -0
  10. package/dist/plan-graph.d.ts +39 -0
  11. package/dist/plan-graph.js +597 -0
  12. package/dist/plan-node-detail.d.ts +29 -0
  13. package/dist/plan-node-detail.js +346 -0
  14. package/dist/plan-task-detail.d.ts +76 -0
  15. package/dist/plan-task-detail.js +450 -0
  16. package/dist/plan-types.d.ts +85 -0
  17. package/dist/plan-types.js +51 -0
  18. package/dist/run-kanban-filter-menu.d.ts +24 -0
  19. package/dist/run-kanban-filter-menu.js +152 -0
  20. package/dist/run-kanban.d.ts +61 -0
  21. package/dist/run-kanban.js +234 -0
  22. package/dist/swarm-agent-badge.d.ts +15 -0
  23. package/dist/swarm-agent-badge.js +39 -0
  24. package/dist/swarm-run-activity.d.ts +39 -0
  25. package/dist/swarm-run-activity.js +289 -0
  26. package/dist/swarm-run-card.d.ts +22 -0
  27. package/dist/swarm-run-card.js +91 -0
  28. package/dist/swarm-run-detail.d.ts +45 -0
  29. package/dist/swarm-run-detail.js +559 -0
  30. package/dist/swarm-run-list.d.ts +22 -0
  31. package/dist/swarm-run-list.js +75 -0
  32. package/dist/swarm-run-row.d.ts +22 -0
  33. package/dist/swarm-run-row.js +125 -0
  34. package/dist/swarm-skeletons.d.ts +28 -0
  35. package/dist/swarm-skeletons.js +78 -0
  36. package/dist/swarm-status-bar.d.ts +15 -0
  37. package/dist/swarm-status-bar.js +79 -0
  38. package/dist/swarm-status-pill.d.ts +12 -0
  39. package/dist/swarm-status-pill.js +86 -0
  40. package/dist/swarm-timeline.d.ts +21 -0
  41. package/dist/swarm-timeline.js +414 -0
  42. package/dist/task-workspace-sidebar.d.ts +71 -0
  43. package/dist/task-workspace-sidebar.js +352 -0
  44. package/dist/types.d.ts +285 -0
  45. package/dist/types.js +44 -0
  46. package/package.json +41 -0
@@ -0,0 +1,559 @@
1
+ "use client";
2
+ import {
3
+ useLayoutEffect,
4
+ useRef,
5
+ useState
6
+ } from "react";
7
+ import {
8
+ ChevronDown,
9
+ ExternalLink,
10
+ FileText,
11
+ Hash,
12
+ Image as ImageIcon,
13
+ RotateCcw,
14
+ Square,
15
+ StickyNote,
16
+ X
17
+ } from "lucide-react";
18
+ import {
19
+ defaultSwarmLabels
20
+ } from "./types";
21
+ import { elapsedSeconds } from "./lib/format";
22
+ import { SwarmStatusPill } from "./swarm-status-pill";
23
+ import { SwarmAgentBadge } from "./swarm-agent-badge";
24
+ function SwarmRunDetail({
25
+ run,
26
+ now,
27
+ variant = "tabs",
28
+ renderAvatar,
29
+ renderToolCalls,
30
+ renderArtifacts,
31
+ renderEvaluation,
32
+ renderMarkdown,
33
+ hideTools = false,
34
+ onClose,
35
+ onCancel,
36
+ onRetry,
37
+ labels,
38
+ className
39
+ }) {
40
+ const L = { ...defaultSwarmLabels, ...labels };
41
+ const clock = now ?? Date.now();
42
+ const elapsed = elapsedSeconds(run.startedAt, run.finishedAt, clock);
43
+ const isLive = run.status === "running" || run.status === "pending";
44
+ const showCancel = onCancel && isLive;
45
+ const showRetry = onRetry && (run.status === "failed" || run.status === "cancelled");
46
+ return /* @__PURE__ */ React.createElement(
47
+ "section",
48
+ {
49
+ className: [
50
+ "flex h-full min-h-0 flex-col overflow-hidden rounded-2xl border border-p-line bg-p-surface",
51
+ className || ""
52
+ ].join(" ")
53
+ },
54
+ /* @__PURE__ */ React.createElement("header", { className: "flex shrink-0 flex-wrap items-center gap-2 border-b border-p-line px-4 py-3" }, /* @__PURE__ */ React.createElement(SwarmAgentBadge, { agent: run.agent, avatar: renderAvatar?.(run.agent) }), /* @__PURE__ */ React.createElement("span", { className: "ml-auto inline-flex items-center gap-1" }, showCancel ? /* @__PURE__ */ React.createElement(
55
+ "button",
56
+ {
57
+ type: "button",
58
+ onClick: () => onCancel(run.id),
59
+ className: "inline-flex items-center gap-1 rounded-md px-2 py-1 font-mono text-[10.5px] font-bold uppercase tracking-[0.16em] text-p-ink-3 transition-colors cursor-pointer hover:bg-p-warm hover:text-[#E63946]"
60
+ },
61
+ /* @__PURE__ */ React.createElement(Square, { className: "size-2.5" }),
62
+ L.cancel
63
+ ) : null, showRetry ? /* @__PURE__ */ React.createElement(
64
+ "button",
65
+ {
66
+ type: "button",
67
+ onClick: () => onRetry(run.id),
68
+ className: "inline-flex items-center gap-1 rounded-md px-2 py-1 font-mono text-[10.5px] font-bold uppercase tracking-[0.16em] text-p-ink-3 transition-colors cursor-pointer hover:bg-p-warm hover:text-p-ink"
69
+ },
70
+ /* @__PURE__ */ React.createElement(RotateCcw, { className: "size-2.5" }),
71
+ L.retry
72
+ ) : null, onClose ? /* @__PURE__ */ React.createElement(
73
+ "button",
74
+ {
75
+ type: "button",
76
+ onClick: onClose,
77
+ "aria-label": "Close",
78
+ title: "Close",
79
+ className: "grid size-7 place-items-center rounded-md text-p-ink-3 transition-colors cursor-pointer hover:bg-p-warm hover:text-p-ink"
80
+ },
81
+ /* @__PURE__ */ React.createElement(X, { className: "size-4" })
82
+ ) : null)),
83
+ /* @__PURE__ */ React.createElement(VitalSigns, { run, L, isLive, elapsed }),
84
+ /* @__PURE__ */ React.createElement("div", { className: "border-b border-p-line px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "font-mono text-[10px] font-bold uppercase tracking-[0.18em] text-p-ink-3" }, L.goal), /* @__PURE__ */ React.createElement("div", { className: "mt-1 font-body text-[14px] leading-snug text-p-ink" }, renderMarkdown ? renderMarkdown(run.goal) : /* @__PURE__ */ React.createElement("p", { className: "m-0" }, run.goal)), isLive && run.lastLog ? /* @__PURE__ */ React.createElement(
85
+ "div",
86
+ {
87
+ className: "mt-2 flex items-center gap-2 rounded-md border border-p-line bg-p-bg/60 px-2 py-1.5 font-mono text-[11px] italic text-p-ink-3",
88
+ "aria-live": "polite"
89
+ },
90
+ /* @__PURE__ */ React.createElement(
91
+ "span",
92
+ {
93
+ "aria-hidden": true,
94
+ className: "size-1.5 shrink-0 animate-pulse rounded-full bg-p-accent"
95
+ }
96
+ ),
97
+ /* @__PURE__ */ React.createElement("span", { className: "truncate" }, run.lastLog)
98
+ ) : null),
99
+ variant === "tabs" ? /* @__PURE__ */ React.createElement(
100
+ TabsBody,
101
+ {
102
+ run,
103
+ L,
104
+ hideTools,
105
+ renderToolCalls,
106
+ renderArtifacts,
107
+ renderEvaluation,
108
+ renderMarkdown
109
+ }
110
+ ) : /* @__PURE__ */ React.createElement(
111
+ FlatBody,
112
+ {
113
+ run,
114
+ L,
115
+ hideTools,
116
+ renderToolCalls,
117
+ renderArtifacts,
118
+ renderEvaluation,
119
+ renderMarkdown
120
+ }
121
+ )
122
+ );
123
+ }
124
+ function TabsBody({
125
+ run,
126
+ L,
127
+ hideTools,
128
+ renderToolCalls,
129
+ renderArtifacts,
130
+ renderEvaluation,
131
+ renderMarkdown
132
+ }) {
133
+ const hasEval = !!run.evaluation;
134
+ const isLive = run.status === "running" || run.status === "pending";
135
+ const [tab, setTab] = useState(
136
+ isLive && !hideTools ? "tools" : "output"
137
+ );
138
+ const artifactCount = run.artifacts?.length ?? 0;
139
+ const toolCount = run.toolCalls?.length ?? 0;
140
+ const safeTab = tab === "tools" && hideTools ? "output" : tab;
141
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex shrink-0 items-center gap-1 border-b border-p-line px-2" }, /* @__PURE__ */ React.createElement(
142
+ Tab,
143
+ {
144
+ active: safeTab === "output",
145
+ onClick: () => setTab("output"),
146
+ label: run.error ? L.error : L.output
147
+ }
148
+ ), /* @__PURE__ */ React.createElement(
149
+ Tab,
150
+ {
151
+ active: safeTab === "artifacts",
152
+ onClick: () => setTab("artifacts"),
153
+ label: "artifacts",
154
+ badge: artifactCount > 0 ? /* @__PURE__ */ React.createElement(CountBadge, { value: artifactCount }) : null
155
+ }
156
+ ), !hideTools ? /* @__PURE__ */ React.createElement(
157
+ Tab,
158
+ {
159
+ active: safeTab === "tools",
160
+ onClick: () => setTab("tools"),
161
+ label: L.tools,
162
+ badge: toolCount > 0 ? /* @__PURE__ */ React.createElement(CountBadge, { value: toolCount }) : null
163
+ }
164
+ ) : null, hasEval ? /* @__PURE__ */ React.createElement(
165
+ Tab,
166
+ {
167
+ active: safeTab === "eval",
168
+ onClick: () => setTab("eval"),
169
+ label: "eval",
170
+ badge: /* @__PURE__ */ React.createElement(
171
+ ScoreBadge,
172
+ {
173
+ score: run.evaluation.verdict.overall,
174
+ scale: run.evaluation.verdict.scale
175
+ }
176
+ )
177
+ }
178
+ ) : null), /* @__PURE__ */ React.createElement("div", { className: "min-h-0 flex-1 overflow-y-auto p-4" }, safeTab === "output" ? /* @__PURE__ */ React.createElement(OutputBlock, { run, L, renderMarkdown }) : null, safeTab === "artifacts" ? /* @__PURE__ */ React.createElement(ArtifactsBlock, { run, renderArtifacts }) : null, safeTab === "tools" && !hideTools ? /* @__PURE__ */ React.createElement(ToolsBlock, { run, renderToolCalls }) : null, safeTab === "eval" && run.evaluation ? /* @__PURE__ */ React.createElement(
179
+ EvalBlock,
180
+ {
181
+ evaluation: run.evaluation,
182
+ renderEvaluation
183
+ }
184
+ ) : null));
185
+ }
186
+ function FlatBody({
187
+ run,
188
+ L,
189
+ hideTools,
190
+ renderToolCalls,
191
+ renderArtifacts,
192
+ renderEvaluation,
193
+ renderMarkdown
194
+ }) {
195
+ const toolCount = run.toolCalls?.length ?? 0;
196
+ const artifactCount = run.artifacts?.length ?? 0;
197
+ const hasEval = !!run.evaluation;
198
+ return /* @__PURE__ */ React.createElement("div", { className: "min-h-0 flex-1 overflow-y-auto" }, /* @__PURE__ */ React.createElement(FlatSection, { title: run.error ? L.error : L.output }, /* @__PURE__ */ React.createElement(OutputBlock, { run, L, renderMarkdown })), /* @__PURE__ */ React.createElement(
199
+ FlatSection,
200
+ {
201
+ title: "artifacts",
202
+ meta: artifactCount > 0 ? String(artifactCount) : void 0
203
+ },
204
+ /* @__PURE__ */ React.createElement(ArtifactsBlock, { run, renderArtifacts })
205
+ ), !hideTools ? /* @__PURE__ */ React.createElement(
206
+ FlatSection,
207
+ {
208
+ title: L.tools,
209
+ meta: toolCount > 0 ? String(toolCount) : void 0,
210
+ last: !hasEval
211
+ },
212
+ /* @__PURE__ */ React.createElement(ToolsBlock, { run, renderToolCalls })
213
+ ) : null, hasEval ? /* @__PURE__ */ React.createElement(FlatSection, { title: "evaluation", last: true }, /* @__PURE__ */ React.createElement(
214
+ EvalBlock,
215
+ {
216
+ evaluation: run.evaluation,
217
+ renderEvaluation
218
+ }
219
+ )) : null);
220
+ }
221
+ function FlatSection({
222
+ title,
223
+ meta,
224
+ last,
225
+ /** Max height of the body before "Show more" appears. Default 220px.
226
+ * The body is clipped to this height with a soft fade gradient at
227
+ * the bottom; clicking "Show more" expands. */
228
+ previewMaxHeight = 220,
229
+ children
230
+ }) {
231
+ const [mode, setMode] = useState(
232
+ "preview"
233
+ );
234
+ const bodyRef = useRef(null);
235
+ const [overflows, setOverflows] = useState(false);
236
+ useLayoutEffect(() => {
237
+ if (mode === "collapsed" || !bodyRef.current) return;
238
+ setOverflows(bodyRef.current.scrollHeight > previewMaxHeight + 4);
239
+ }, [mode, previewMaxHeight, children]);
240
+ return /* @__PURE__ */ React.createElement(
241
+ "section",
242
+ {
243
+ className: [
244
+ "px-4 py-4",
245
+ last ? "" : "border-b border-p-line"
246
+ ].join(" ")
247
+ },
248
+ /* @__PURE__ */ React.createElement("div", { className: "mb-2 flex items-center gap-2" }, /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10px] font-bold uppercase tracking-[0.22em] text-p-ink-3" }, title), meta ? /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10px] uppercase tracking-[0.18em] text-p-ink-3/70 tabular-nums" }, "\xB7 ", meta) : null, /* @__PURE__ */ React.createElement(
249
+ "button",
250
+ {
251
+ type: "button",
252
+ onClick: () => setMode((m) => m === "collapsed" ? "preview" : "collapsed"),
253
+ "aria-label": mode === "collapsed" ? "Expand section" : "Collapse section",
254
+ "aria-expanded": mode !== "collapsed",
255
+ className: "ml-auto grid size-5 place-items-center rounded text-p-ink-3 transition-colors cursor-pointer hover:bg-p-warm hover:text-p-ink"
256
+ },
257
+ /* @__PURE__ */ React.createElement(
258
+ ChevronDown,
259
+ {
260
+ className: [
261
+ "size-3.5 transition-transform duration-150",
262
+ mode === "collapsed" ? "-rotate-90" : ""
263
+ ].join(" ")
264
+ }
265
+ )
266
+ )),
267
+ mode === "collapsed" ? null : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
268
+ "div",
269
+ {
270
+ ref: bodyRef,
271
+ className: "relative overflow-hidden",
272
+ style: {
273
+ maxHeight: mode === "preview" && overflows ? previewMaxHeight : void 0
274
+ }
275
+ },
276
+ children,
277
+ mode === "preview" && overflows ? /* @__PURE__ */ React.createElement(
278
+ "div",
279
+ {
280
+ "aria-hidden": true,
281
+ className: "pointer-events-none absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-p-surface to-transparent"
282
+ }
283
+ ) : null
284
+ ), overflows ? /* @__PURE__ */ React.createElement(
285
+ "button",
286
+ {
287
+ type: "button",
288
+ onClick: () => setMode((m) => m === "preview" ? "expanded" : "preview"),
289
+ className: "mt-2 inline-flex items-center gap-1 rounded-md font-mono text-[10.5px] font-bold uppercase tracking-[0.16em] text-p-ink-3 transition-colors cursor-pointer hover:text-p-ink"
290
+ },
291
+ mode === "preview" ? "show more" : "show less",
292
+ /* @__PURE__ */ React.createElement(
293
+ ChevronDown,
294
+ {
295
+ className: [
296
+ "size-3 transition-transform duration-150",
297
+ mode === "expanded" ? "rotate-180" : ""
298
+ ].join(" ")
299
+ }
300
+ )
301
+ ) : null)
302
+ );
303
+ }
304
+ function VitalSigns({
305
+ run,
306
+ L,
307
+ isLive,
308
+ elapsed
309
+ }) {
310
+ const attempts = (run.retries ?? 0) + 1;
311
+ const maxAttempts = typeof run.maxRetries === "number" ? run.maxRetries + 1 : void 0;
312
+ return /* @__PURE__ */ React.createElement("section", { className: "grid shrink-0 grid-cols-3 gap-px border-b border-p-line bg-p-line" }, /* @__PURE__ */ React.createElement(
313
+ Stat,
314
+ {
315
+ label: "duration",
316
+ value: /* @__PURE__ */ React.createElement("span", { className: "tabular-nums" }, isLive ? L.startedAgo(elapsed) : L.duration(elapsed))
317
+ }
318
+ ), /* @__PURE__ */ React.createElement(
319
+ Stat,
320
+ {
321
+ label: "status",
322
+ value: /* @__PURE__ */ React.createElement(
323
+ SwarmStatusPill,
324
+ {
325
+ status: run.status,
326
+ label: L[run.status],
327
+ size: "sm"
328
+ }
329
+ )
330
+ }
331
+ ), /* @__PURE__ */ React.createElement(
332
+ Stat,
333
+ {
334
+ label: "attempts",
335
+ value: /* @__PURE__ */ React.createElement("span", { className: "tabular-nums" }, attempts, typeof maxAttempts === "number" ? /* @__PURE__ */ React.createElement("span", { className: "text-p-ink-3" }, "/", maxAttempts) : null)
336
+ }
337
+ ));
338
+ }
339
+ function Stat({ label, value }) {
340
+ return /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1 bg-p-surface px-4 py-3" }, /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[9.5px] font-bold uppercase tracking-[0.22em] text-p-ink-3" }, label), /* @__PURE__ */ React.createElement("span", { className: "font-display text-[16px] font-bold leading-tight tracking-[-0.01em] text-p-ink" }, value));
341
+ }
342
+ function OutputBlock({
343
+ run,
344
+ L,
345
+ renderMarkdown
346
+ }) {
347
+ if (run.error) {
348
+ return /* @__PURE__ */ React.createElement("pre", { className: "m-0 whitespace-pre-wrap break-words font-mono text-[12px] leading-relaxed text-[#E63946]" }, run.error);
349
+ }
350
+ if (!run.output) {
351
+ return /* @__PURE__ */ React.createElement("span", { className: "block px-1 py-2 font-mono text-[11px] uppercase tracking-[0.16em] text-p-ink-3" }, L.output, " \u2014 \u2014");
352
+ }
353
+ if (renderMarkdown) {
354
+ return /* @__PURE__ */ React.createElement("div", { className: "font-body text-[14px] leading-[1.65] text-p-ink-2" }, renderMarkdown(run.output));
355
+ }
356
+ return /* @__PURE__ */ React.createElement("pre", { className: "m-0 whitespace-pre-wrap break-words font-mono text-[12px] leading-relaxed text-p-ink-2" }, run.output);
357
+ }
358
+ function ArtifactsBlock({
359
+ run,
360
+ renderArtifacts
361
+ }) {
362
+ const artifacts = run.artifacts ?? [];
363
+ if (renderArtifacts) return /* @__PURE__ */ React.createElement(React.Fragment, null, renderArtifacts(artifacts));
364
+ if (artifacts.length === 0) {
365
+ return /* @__PURE__ */ React.createElement("span", { className: "block px-1 py-2 font-mono text-[11px] uppercase tracking-[0.16em] text-p-ink-3" }, "no artifacts produced");
366
+ }
367
+ return /* @__PURE__ */ React.createElement(DefaultArtifactList, { artifacts });
368
+ }
369
+ function ToolsBlock({
370
+ run,
371
+ renderToolCalls
372
+ }) {
373
+ if (renderToolCalls) return /* @__PURE__ */ React.createElement(React.Fragment, null, renderToolCalls(run.toolCalls ?? []));
374
+ return /* @__PURE__ */ React.createElement(DefaultToolCallList, { toolCalls: run.toolCalls ?? [] });
375
+ }
376
+ function EvalBlock({
377
+ evaluation,
378
+ renderEvaluation
379
+ }) {
380
+ if (renderEvaluation) return /* @__PURE__ */ React.createElement(React.Fragment, null, renderEvaluation(evaluation));
381
+ return /* @__PURE__ */ React.createElement(DefaultEvalSummary, { evaluation });
382
+ }
383
+ function DefaultEvalSummary({
384
+ evaluation
385
+ }) {
386
+ const { verdict, scores, evaluatedAt } = evaluation;
387
+ const scale = verdict.scale ?? scores[0]?.scale ?? { min: 1, max: 5 };
388
+ const tone = verdictTone(verdict.overall, scale);
389
+ return /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-baseline gap-3" }, /* @__PURE__ */ React.createElement(
390
+ "span",
391
+ {
392
+ className: "font-display text-[40px] font-bold leading-none tracking-[-0.02em] tabular-nums",
393
+ style: { color: tone }
394
+ },
395
+ formatScore(verdict.overall)
396
+ ), /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10.5px] uppercase tracking-[0.18em] text-p-ink-3" }, "/", scale.max, " \xB7 evaluated", " ", new Date(evaluatedAt).toLocaleDateString(void 0, {
397
+ month: "short",
398
+ day: "numeric"
399
+ }), typeof verdict.passed === "boolean" ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "mx-1.5 text-p-line" }, "\xB7"), /* @__PURE__ */ React.createElement("span", { style: { color: verdict.passed ? "var(--green)" : "#E63946" } }, verdict.passed ? "passed" : "failed")) : null)), verdict.summary ? /* @__PURE__ */ React.createElement("p", { className: "font-body text-[13px] leading-[1.55] text-p-ink-2" }, verdict.summary) : null, /* @__PURE__ */ React.createElement("ul", { className: "flex flex-col gap-2" }, scores.map((s) => {
400
+ const sScale = s.scale ?? scale;
401
+ const pct = Math.max(
402
+ 0,
403
+ Math.min(
404
+ 100,
405
+ (s.value - sScale.min) / (sScale.max - sScale.min) * 100
406
+ )
407
+ );
408
+ const sTone = verdictTone(s.value, sScale);
409
+ return /* @__PURE__ */ React.createElement("li", { key: s.criterion, className: "flex flex-col gap-1" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-baseline justify-between gap-3 font-mono text-[10.5px] uppercase tracking-[0.16em]" }, /* @__PURE__ */ React.createElement("span", { className: "text-p-ink-2" }, s.criterion), /* @__PURE__ */ React.createElement("span", { className: "tabular-nums", style: { color: sTone } }, formatScore(s.value), /* @__PURE__ */ React.createElement("span", { className: "text-p-ink-3" }, "/", sScale.max))), /* @__PURE__ */ React.createElement("div", { className: "h-[3px] w-full overflow-hidden rounded-full bg-p-line" }, /* @__PURE__ */ React.createElement(
410
+ "div",
411
+ {
412
+ className: "h-full rounded-full",
413
+ style: { width: `${pct}%`, background: sTone }
414
+ }
415
+ )));
416
+ })));
417
+ }
418
+ function formatScore(n) {
419
+ return n.toFixed(1).replace(/\.0$/, "");
420
+ }
421
+ function verdictTone(value, scale) {
422
+ const range = scale.max - scale.min;
423
+ if (range <= 0) return "var(--ink-2)";
424
+ const norm = (value - scale.min) / range;
425
+ if (norm >= 0.75) return "var(--green)";
426
+ if (norm >= 0.5) return "var(--p-accent)";
427
+ if (norm >= 0.25) return "#D4A017";
428
+ return "#E63946";
429
+ }
430
+ function Tab({
431
+ active,
432
+ onClick,
433
+ label,
434
+ badge
435
+ }) {
436
+ return /* @__PURE__ */ React.createElement(
437
+ "button",
438
+ {
439
+ type: "button",
440
+ onClick,
441
+ className: [
442
+ "relative inline-flex items-center gap-1.5 px-3 py-2 font-mono text-[11px] font-bold uppercase tracking-[0.16em] transition-colors cursor-pointer",
443
+ active ? "text-p-ink" : "text-p-ink-3 hover:text-p-ink"
444
+ ].join(" ")
445
+ },
446
+ label,
447
+ badge,
448
+ active ? /* @__PURE__ */ React.createElement(
449
+ "span",
450
+ {
451
+ "aria-hidden": true,
452
+ className: "absolute inset-x-2 -bottom-px h-[2px] rounded-t bg-p-accent"
453
+ }
454
+ ) : null
455
+ );
456
+ }
457
+ function CountBadge({ value }) {
458
+ return /* @__PURE__ */ React.createElement("span", { className: "inline-flex h-[18px] min-w-[18px] items-center justify-center rounded-full border border-p-line bg-transparent px-1.5 font-mono text-[10px] font-bold tabular-nums text-p-ink-3" }, value);
459
+ }
460
+ function ScoreBadge({
461
+ score,
462
+ scale
463
+ }) {
464
+ const sc = scale ?? { min: 1, max: 5 };
465
+ const range = sc.max - sc.min;
466
+ const norm = range > 0 ? (score - sc.min) / range : 0;
467
+ const palette = norm >= 0.75 ? { fg: "text-p-green", bg: "bg-p-green-light", ring: "border-p-green/30" } : norm >= 0.5 ? {
468
+ fg: "text-p-accent",
469
+ bg: "bg-p-accent-light",
470
+ ring: "border-p-accent/30"
471
+ } : norm >= 0.25 ? {
472
+ fg: "text-[#B45309]",
473
+ bg: "bg-[#FEF3C7]",
474
+ ring: "border-[#D4A017]/30"
475
+ } : {
476
+ fg: "text-[#E63946]",
477
+ bg: "bg-[#FEF2F2]",
478
+ ring: "border-[#E63946]/30"
479
+ };
480
+ return /* @__PURE__ */ React.createElement(
481
+ "span",
482
+ {
483
+ className: [
484
+ "inline-flex h-[18px] items-center justify-center rounded-full border px-1.5 font-mono text-[10px] font-bold tabular-nums",
485
+ palette.fg,
486
+ palette.bg,
487
+ palette.ring
488
+ ].join(" ")
489
+ },
490
+ formatScore(score)
491
+ );
492
+ }
493
+ function DefaultToolCallList({ toolCalls }) {
494
+ if (toolCalls.length === 0) {
495
+ return /* @__PURE__ */ React.createElement("span", { className: "block px-1 py-2 font-mono text-[11px] uppercase tracking-[0.16em] text-p-ink-3" }, "no tool calls yet");
496
+ }
497
+ return /* @__PURE__ */ React.createElement("ul", { className: "flex flex-col gap-1.5" }, toolCalls.map((t) => /* @__PURE__ */ React.createElement(
498
+ "li",
499
+ {
500
+ key: t.id,
501
+ className: "flex items-center gap-2 rounded-md border border-p-line bg-p-bg px-2 py-1.5"
502
+ },
503
+ /* @__PURE__ */ React.createElement(
504
+ "span",
505
+ {
506
+ "aria-hidden": true,
507
+ className: [
508
+ "size-1.5 shrink-0 rounded-full",
509
+ t.state === "calling" ? "bg-p-accent animate-pulse" : t.state === "completed" ? "bg-p-green" : "bg-[#E63946]"
510
+ ].join(" ")
511
+ }
512
+ ),
513
+ /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[11.5px] font-semibold text-p-ink" }, t.name),
514
+ t.summary ? /* @__PURE__ */ React.createElement("span", { className: "truncate font-mono text-[11px] text-p-ink-3" }, t.summary) : null
515
+ )));
516
+ }
517
+ const ARTIFACT_ICON = {
518
+ file: FileText,
519
+ url: ExternalLink,
520
+ image: ImageIcon,
521
+ data: Hash,
522
+ note: StickyNote
523
+ };
524
+ function DefaultArtifactList({ artifacts }) {
525
+ return /* @__PURE__ */ React.createElement("ul", { className: "flex flex-col gap-1.5" }, artifacts.map((a) => {
526
+ const Icon = ARTIFACT_ICON[a.kind];
527
+ const interactive = !!a.url;
528
+ const Wrapper = interactive ? "a" : "div";
529
+ return /* @__PURE__ */ React.createElement("li", { key: a.id }, /* @__PURE__ */ React.createElement(
530
+ Wrapper,
531
+ {
532
+ ...interactive ? {
533
+ href: a.url,
534
+ target: "_blank",
535
+ rel: "noreferrer noopener"
536
+ } : {},
537
+ className: [
538
+ "flex items-center gap-2 rounded-md border border-p-line bg-p-surface px-2.5 py-1.5 no-underline transition-colors",
539
+ interactive ? "cursor-pointer hover:bg-p-warm/40" : ""
540
+ ].join(" ")
541
+ },
542
+ /* @__PURE__ */ React.createElement(Icon, { className: "size-3 shrink-0 text-p-ink-3", "aria-hidden": true }),
543
+ /* @__PURE__ */ React.createElement("span", { className: "min-w-0 flex-1 truncate font-display text-[12.5px] font-semibold text-p-ink" }, a.label),
544
+ a.path ? /* @__PURE__ */ React.createElement("code", { className: "hidden truncate font-mono text-[10.5px] text-p-ink-3 sm:inline" }, a.path) : null,
545
+ a.tags && a.tags.length > 0 ? /* @__PURE__ */ React.createElement("span", { className: "hidden flex-wrap gap-1 sm:inline-flex" }, a.tags.map((t) => /* @__PURE__ */ React.createElement(
546
+ "span",
547
+ {
548
+ key: t,
549
+ className: "inline-flex items-center rounded border border-p-line bg-p-bg px-1 py-px font-mono text-[9.5px] uppercase tracking-[0.14em] text-p-ink-2"
550
+ },
551
+ t
552
+ ))) : null,
553
+ a.summary ? /* @__PURE__ */ React.createElement("span", { className: "hidden truncate font-mono text-[10.5px] text-p-ink-3 lg:inline" }, a.summary) : null
554
+ ));
555
+ }));
556
+ }
557
+ export {
558
+ SwarmRunDetail
559
+ };
@@ -0,0 +1,22 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { SwarmRun, SwarmLabels } from './types.js';
4
+
5
+ interface SwarmRunListProps {
6
+ runs: SwarmRun[];
7
+ now?: number;
8
+ /** Group background runs into a separate trailing section. */
9
+ groupBackground?: boolean;
10
+ /** Currently-selected run id — passes through to row `active` flag. */
11
+ activeId?: string | null;
12
+ renderAvatar?: (agent: SwarmRun["agent"]) => ReactNode;
13
+ onCancel?: (id: string) => void;
14
+ onRetry?: (id: string) => void;
15
+ onOpen?: (id: string) => void;
16
+ emptyState?: ReactNode;
17
+ labels?: Partial<SwarmLabels>;
18
+ className?: string;
19
+ }
20
+ declare function SwarmRunList({ runs, now, groupBackground, activeId, renderAvatar, onCancel, onRetry, onOpen, emptyState, labels, className, }: SwarmRunListProps): react.JSX.Element;
21
+
22
+ export { SwarmRunList, type SwarmRunListProps };
@@ -0,0 +1,75 @@
1
+ "use client";
2
+ import { useMemo } from "react";
3
+ import {
4
+ defaultSwarmLabels
5
+ } from "./types";
6
+ import { SwarmRunRow } from "./swarm-run-row";
7
+ function SwarmRunList({
8
+ runs,
9
+ now,
10
+ groupBackground = false,
11
+ activeId,
12
+ renderAvatar,
13
+ onCancel,
14
+ onRetry,
15
+ onOpen,
16
+ emptyState,
17
+ labels,
18
+ className
19
+ }) {
20
+ const L = { ...defaultSwarmLabels, ...labels };
21
+ const { foreground, background } = useMemo(() => {
22
+ if (!groupBackground)
23
+ return { foreground: runs, background: [] };
24
+ const fg = [];
25
+ const bg = [];
26
+ for (const r of runs) (r.background ? bg : fg).push(r);
27
+ return { foreground: fg, background: bg };
28
+ }, [runs, groupBackground]);
29
+ if (runs.length === 0) {
30
+ return /* @__PURE__ */ React.createElement("div", { className: ["px-3 py-10 text-center", className || ""].join(" ") }, emptyState ?? /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[11px] uppercase tracking-[0.16em] text-p-ink-3" }, L.noRuns));
31
+ }
32
+ const Section = ({
33
+ label,
34
+ list
35
+ }) => /* @__PURE__ */ React.createElement("div", { className: "flex flex-col" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-baseline gap-2 border-b border-p-line bg-p-bg/50 px-3 py-1.5" }, /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10px] font-bold uppercase tracking-[0.22em] text-p-ink-3" }, label), /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10px] tabular-nums text-p-ink-3" }, String(list.length).padStart(2, "0"))), /* @__PURE__ */ React.createElement("div", { className: "flex flex-col" }, list.map((r) => /* @__PURE__ */ React.createElement(
36
+ SwarmRunRow,
37
+ {
38
+ key: r.id,
39
+ run: r,
40
+ now,
41
+ renderAvatar,
42
+ onCancel,
43
+ onRetry,
44
+ onOpen,
45
+ active: activeId === r.id,
46
+ labels
47
+ }
48
+ ))));
49
+ return /* @__PURE__ */ React.createElement(
50
+ "div",
51
+ {
52
+ className: [
53
+ "overflow-hidden rounded-xl border border-p-line bg-p-surface",
54
+ className || ""
55
+ ].join(" ")
56
+ },
57
+ groupBackground ? /* @__PURE__ */ React.createElement(React.Fragment, null, foreground.length > 0 ? /* @__PURE__ */ React.createElement(Section, { label: L.foregroundGroup, list: foreground }) : null, background.length > 0 ? /* @__PURE__ */ React.createElement(Section, { label: L.backgroundGroup, list: background }) : null) : /* @__PURE__ */ React.createElement("div", { className: "flex flex-col" }, runs.map((r) => /* @__PURE__ */ React.createElement(
58
+ SwarmRunRow,
59
+ {
60
+ key: r.id,
61
+ run: r,
62
+ now,
63
+ renderAvatar,
64
+ onCancel,
65
+ onRetry,
66
+ onOpen,
67
+ active: activeId === r.id,
68
+ labels
69
+ }
70
+ )))
71
+ );
72
+ }
73
+ export {
74
+ SwarmRunList
75
+ };
@@ -0,0 +1,22 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { SwarmRun, SwarmLabels } from './types.js';
4
+
5
+ interface SwarmRunRowProps {
6
+ run: SwarmRun;
7
+ /** Live clock — pass `state.now` from `useSwarm()` to make the
8
+ * elapsed counter tick. */
9
+ now?: number;
10
+ renderAvatar?: (agent: SwarmRun["agent"]) => ReactNode;
11
+ onCancel?: (id: string) => void;
12
+ onRetry?: (id: string) => void;
13
+ onOpen?: (id: string) => void;
14
+ /** Highlight the row as currently selected (e.g. open in a detail
15
+ * panel next to it). */
16
+ active?: boolean;
17
+ labels?: Partial<SwarmLabels>;
18
+ className?: string;
19
+ }
20
+ declare function SwarmRunRow({ run, now, renderAvatar, onCancel, onRetry, onOpen, active, labels, className, }: SwarmRunRowProps): react.JSX.Element;
21
+
22
+ export { SwarmRunRow, type SwarmRunRowProps };