@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,450 @@
1
+ "use client";
2
+ import { useMemo, useState } from "react";
3
+ import {
4
+ AlertTriangle,
5
+ ArrowRight,
6
+ CheckCircle2,
7
+ Circle,
8
+ Clock,
9
+ Flag,
10
+ Pause,
11
+ Play,
12
+ RotateCcw,
13
+ Scale,
14
+ Square,
15
+ XCircle,
16
+ Zap
17
+ } from "lucide-react";
18
+ import {
19
+ defaultPlanGraphLabels
20
+ } from "./plan-types";
21
+ const STATUS_ICON = {
22
+ draft: Circle,
23
+ pending: Pause,
24
+ running: RotateCcw,
25
+ review: Scale,
26
+ done: CheckCircle2,
27
+ failed: XCircle
28
+ };
29
+ const STATUS_TONE = {
30
+ draft: "text-p-ink-3",
31
+ pending: "text-p-ink-3",
32
+ running: "text-p-accent",
33
+ review: "text-[#2B44FF]",
34
+ done: "text-p-green",
35
+ failed: "text-[#E63946]"
36
+ };
37
+ const STATUS_HEX = {
38
+ draft: "var(--ink-3)",
39
+ pending: "var(--ink-3)",
40
+ running: "var(--p-accent)",
41
+ review: "#2B44FF",
42
+ done: "var(--green)",
43
+ failed: "#E63946"
44
+ };
45
+ const STATUS_BG = {
46
+ draft: "bg-p-warm",
47
+ pending: "bg-p-warm",
48
+ running: "bg-p-accent-light",
49
+ review: "bg-[#EEF2FF]",
50
+ done: "bg-p-green-light",
51
+ failed: "bg-[#FEF2F2]"
52
+ };
53
+ function PlanTaskDetail({
54
+ task,
55
+ plan,
56
+ runtimeByTitle,
57
+ agents,
58
+ variant = "panel",
59
+ headerTrailing,
60
+ actionsSlot,
61
+ actions,
62
+ confirm,
63
+ onOpenRelated,
64
+ renderMarkdown,
65
+ labels,
66
+ className
67
+ }) {
68
+ const L = useMemo(
69
+ () => ({ ...defaultPlanGraphLabels, ...labels }),
70
+ [labels]
71
+ );
72
+ const runtime = runtimeByTitle?.get(task.title);
73
+ const status = runtime?.status ?? "draft";
74
+ const StatusIcon = STATUS_ICON[status];
75
+ const tone = STATUS_TONE[status];
76
+ const agentName = runtime?.agentName ?? task.assignTo;
77
+ const agent = agentName ? agents?.find((a) => a.name === agentName) : void 0;
78
+ const dependsOn = task.dependsOn ?? [];
79
+ const dependents = useMemo(() => {
80
+ if (!plan) return [];
81
+ const out = [];
82
+ for (const tk of plan.tasks) {
83
+ if ((tk.dependsOn ?? []).includes(task.title)) out.push(tk.title);
84
+ }
85
+ return out;
86
+ }, [plan, task.title]);
87
+ if (variant === "page") {
88
+ return /* @__PURE__ */ React.createElement(
89
+ "article",
90
+ {
91
+ className: [
92
+ "flex h-full min-h-0 flex-col overflow-hidden bg-p-surface",
93
+ className || ""
94
+ ].join(" ")
95
+ },
96
+ /* @__PURE__ */ React.createElement(
97
+ PageHeader,
98
+ {
99
+ status,
100
+ phase: runtime?.phase ?? task.phase,
101
+ priority: runtime?.priority,
102
+ sideEffects: runtime?.sideEffects,
103
+ L,
104
+ headerTrailing
105
+ }
106
+ ),
107
+ /* @__PURE__ */ React.createElement("div", { className: "flex min-h-0 flex-1 flex-col gap-7 overflow-y-auto px-7 py-7" }, /* @__PURE__ */ React.createElement("section", null, /* @__PURE__ */ React.createElement("h2", { className: "font-display text-[28px] font-bold leading-[1.06] tracking-[-0.02em] text-p-ink" }, task.title), task.description ? renderMarkdown ? /* @__PURE__ */ React.createElement("div", { className: "mt-3 max-w-[64ch]" }, renderMarkdown(task.description)) : /* @__PURE__ */ React.createElement("p", { className: "mt-3 max-w-[64ch] font-body text-[14.5px] leading-[1.6] text-p-ink-2 whitespace-pre-wrap" }, task.description) : null), actions ? /* @__PURE__ */ React.createElement(
108
+ ActionDock,
109
+ {
110
+ actions,
111
+ status,
112
+ confirm
113
+ }
114
+ ) : null, /* @__PURE__ */ React.createElement("section", { className: "grid grid-cols-1 gap-px overflow-hidden rounded-xl border border-p-line bg-p-line sm:grid-cols-3" }, /* @__PURE__ */ React.createElement(PageStat, { label: "assignee" }, agent ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-2" }, /* @__PURE__ */ React.createElement(
115
+ "span",
116
+ {
117
+ "aria-hidden": true,
118
+ className: "grid size-5 shrink-0 place-items-center rounded font-display text-[10px] font-bold text-white",
119
+ style: { background: agent.color || "#999" }
120
+ },
121
+ agent.glyph || (agent.displayName ?? agent.name).charAt(0).toUpperCase()
122
+ ), /* @__PURE__ */ React.createElement("span", { className: "text-[13.5px] font-semibold text-p-ink" }, agent.displayName ?? agent.name)) : /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[11px] uppercase tracking-[0.14em] text-p-ink-3" }, agentName ?? "\u2014")), /* @__PURE__ */ React.createElement(PageStat, { label: "phase" }, /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[11px] uppercase tracking-[0.14em] text-p-ink" }, task.phase ?? runtime?.phase ?? "execution")), /* @__PURE__ */ React.createElement(PageStat, { label: "status" }, /* @__PURE__ */ React.createElement(
123
+ "span",
124
+ {
125
+ className: "inline-flex items-center gap-1.5 font-mono text-[11px] font-bold uppercase tracking-[0.16em]",
126
+ style: { color: STATUS_HEX[status] }
127
+ },
128
+ /* @__PURE__ */ React.createElement(StatusIcon, { className: "size-3", "aria-hidden": true }),
129
+ L.status[status]
130
+ ))), dependsOn.length > 0 ? /* @__PURE__ */ React.createElement(
131
+ PageRelatedSection,
132
+ {
133
+ title: "depends on",
134
+ icon: /* @__PURE__ */ React.createElement(ArrowRight, { className: "size-2.5 rotate-180" }),
135
+ items: dependsOn,
136
+ runtimeByTitle,
137
+ onOpen: onOpenRelated,
138
+ L
139
+ }
140
+ ) : null, dependents.length > 0 ? /* @__PURE__ */ React.createElement(
141
+ PageRelatedSection,
142
+ {
143
+ title: "blocks",
144
+ icon: /* @__PURE__ */ React.createElement(ArrowRight, { className: "size-2.5" }),
145
+ items: dependents,
146
+ runtimeByTitle,
147
+ onOpen: onOpenRelated,
148
+ L
149
+ }
150
+ ) : null, !task.description && dependsOn.length === 0 && dependents.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "flex flex-col items-center gap-2 rounded-md border border-dashed border-p-line bg-p-bg p-8 text-center" }, /* @__PURE__ */ React.createElement(Flag, { className: "size-4 text-p-ink-3", "aria-hidden": true }), /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10.5px] uppercase tracking-[0.16em] text-p-ink-3" }, "no description, no dependencies")) : null)
151
+ );
152
+ }
153
+ return /* @__PURE__ */ React.createElement(
154
+ "article",
155
+ {
156
+ className: [
157
+ "flex h-full min-h-0 flex-col overflow-hidden bg-p-surface",
158
+ className || ""
159
+ ].join(" ")
160
+ },
161
+ /* @__PURE__ */ React.createElement(
162
+ "header",
163
+ {
164
+ className: [
165
+ "flex shrink-0 items-start gap-3 border-b border-p-line px-5 py-4",
166
+ STATUS_BG[status]
167
+ ].join(" ")
168
+ },
169
+ /* @__PURE__ */ React.createElement(
170
+ "span",
171
+ {
172
+ className: [
173
+ "mt-0.5 grid size-7 shrink-0 place-items-center rounded-full bg-p-surface ring-1",
174
+ tone,
175
+ status === "running" ? "ring-p-accent/30" : "ring-p-line"
176
+ ].join(" ")
177
+ },
178
+ /* @__PURE__ */ React.createElement(StatusIcon, { className: "size-3.5", "aria-hidden": true })
179
+ ),
180
+ /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("div", { className: "font-mono text-[10px] font-bold uppercase tracking-[0.22em] text-p-ink-3" }, L.task, " \xB7", " ", /* @__PURE__ */ React.createElement("span", { className: tone }, L.status[status]), runtime?.phase && runtime.phase !== "execution" ? /* @__PURE__ */ React.createElement("span", null, " \xB7 ", runtime.phase) : null), /* @__PURE__ */ React.createElement("h3", { className: "mt-1 font-display text-[18px] font-bold leading-tight tracking-[-0.01em] text-p-ink" }, task.title)),
181
+ headerTrailing
182
+ ),
183
+ /* @__PURE__ */ React.createElement("div", { className: "flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto px-5 py-4" }, task.description ? /* @__PURE__ */ React.createElement("section", null, /* @__PURE__ */ React.createElement(Kicker, null, "brief"), renderMarkdown ? /* @__PURE__ */ React.createElement("div", { className: "mt-1" }, renderMarkdown(task.description)) : /* @__PURE__ */ React.createElement("p", { className: "mt-1 font-body text-[13px] leading-[1.55] text-p-ink-2" }, task.description)) : null, /* @__PURE__ */ React.createElement("section", { className: "grid grid-cols-2 gap-px overflow-hidden rounded-md border border-p-line bg-p-line text-[11px]" }, /* @__PURE__ */ React.createElement(Cell, { label: "agent" }, agent ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-1.5" }, /* @__PURE__ */ React.createElement(
184
+ "span",
185
+ {
186
+ "aria-hidden": true,
187
+ className: "grid size-4 shrink-0 place-items-center rounded font-display text-[9px] font-bold text-white",
188
+ style: { background: agent.color || "#999" }
189
+ },
190
+ agent.glyph || (agent.displayName ?? agent.name).charAt(0).toUpperCase()
191
+ ), /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10.5px] font-bold uppercase tracking-[0.14em] text-p-ink" }, agent.displayName ?? agent.name)) : /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10.5px] uppercase tracking-[0.14em] text-p-ink-3" }, agentName ?? "\u2014")), /* @__PURE__ */ React.createElement(Cell, { label: "phase" }, /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10.5px] uppercase tracking-[0.14em] text-p-ink" }, task.phase ?? runtime?.phase ?? "execution")), typeof runtime?.priority === "number" ? /* @__PURE__ */ React.createElement(Cell, { label: "priority" }, /* @__PURE__ */ React.createElement("span", { className: "font-display text-[14px] font-bold tabular-nums text-p-ink" }, runtime.priority, "\xD7")) : null, runtime?.sideEffects ? /* @__PURE__ */ React.createElement(Cell, { label: "warnings" }, /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-1 text-[#E63946]" }, /* @__PURE__ */ React.createElement(AlertTriangle, { className: "size-3", "aria-hidden": true }), /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10.5px] uppercase tracking-[0.14em]" }, "side effects"))) : null), dependsOn.length > 0 ? /* @__PURE__ */ React.createElement("section", null, /* @__PURE__ */ React.createElement(Kicker, { icon: /* @__PURE__ */ React.createElement(ArrowRight, { className: "size-2.5 rotate-180" }) }, "depends on"), /* @__PURE__ */ React.createElement("ol", { className: "mt-1 flex flex-col gap-0.5" }, dependsOn.map((title) => /* @__PURE__ */ React.createElement(
192
+ RelatedRow,
193
+ {
194
+ key: title,
195
+ title,
196
+ runtime: runtimeByTitle?.get(title),
197
+ onOpen: onOpenRelated,
198
+ L
199
+ }
200
+ )))) : null, dependents.length > 0 ? /* @__PURE__ */ React.createElement("section", null, /* @__PURE__ */ React.createElement(Kicker, { icon: /* @__PURE__ */ React.createElement(ArrowRight, { className: "size-2.5" }) }, "blocks"), /* @__PURE__ */ React.createElement("ol", { className: "mt-1 flex flex-col gap-0.5" }, dependents.map((title) => /* @__PURE__ */ React.createElement(
201
+ RelatedRow,
202
+ {
203
+ key: title,
204
+ title,
205
+ runtime: runtimeByTitle?.get(title),
206
+ onOpen: onOpenRelated,
207
+ L
208
+ }
209
+ )))) : null, !task.description && dependsOn.length === 0 && dependents.length === 0 ? /* @__PURE__ */ React.createElement("section", { className: "flex flex-col items-center gap-2 rounded-md border border-dashed border-p-line bg-p-bg p-6 text-center" }, /* @__PURE__ */ React.createElement(Flag, { className: "size-4 text-p-ink-3", "aria-hidden": true }), /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10.5px] uppercase tracking-[0.16em] text-p-ink-3" }, "no description, no dependencies")) : null),
210
+ actions || actionsSlot ? /* @__PURE__ */ React.createElement("footer", { className: "flex shrink-0 flex-wrap items-center justify-end gap-1.5 border-t border-p-line bg-p-bg/50 px-5 py-3" }, actions ? /* @__PURE__ */ React.createElement(ActionDock, { actions, status, confirm }) : actionsSlot) : null
211
+ );
212
+ }
213
+ function PageHeader({
214
+ status,
215
+ phase,
216
+ priority,
217
+ sideEffects,
218
+ L,
219
+ headerTrailing
220
+ }) {
221
+ const StatusIcon = STATUS_ICON[status];
222
+ const color = STATUS_HEX[status];
223
+ return /* @__PURE__ */ React.createElement("header", { className: "flex shrink-0 items-start justify-between gap-3 border-b border-p-line px-7 py-5" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-1.5" }, /* @__PURE__ */ React.createElement(
224
+ "span",
225
+ {
226
+ className: "inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 font-mono text-[10px] font-bold uppercase tracking-[0.18em]",
227
+ style: {
228
+ background: `color-mix(in srgb, ${color} 14%, transparent)`,
229
+ color
230
+ }
231
+ },
232
+ /* @__PURE__ */ React.createElement(StatusIcon, { className: "size-2.5" }),
233
+ L.status[status]
234
+ ), phase && phase !== "execution" ? /* @__PURE__ */ React.createElement(PageChip, { tone: "blue", icon: /* @__PURE__ */ React.createElement(RotateCcw, { className: "size-2.5" }) }, phase) : null, typeof priority === "number" && priority !== 1 ? /* @__PURE__ */ React.createElement(
235
+ PageChip,
236
+ {
237
+ tone: priority > 1 ? "accent" : "neutral",
238
+ icon: /* @__PURE__ */ React.createElement(Zap, { className: "size-2.5" })
239
+ },
240
+ priority,
241
+ "\xD7"
242
+ ) : null, sideEffects ? /* @__PURE__ */ React.createElement(PageChip, { tone: "red", icon: /* @__PURE__ */ React.createElement(AlertTriangle, { className: "size-2.5" }) }, "side effects") : null), headerTrailing ? /* @__PURE__ */ React.createElement("div", { className: "shrink-0" }, headerTrailing) : null);
243
+ }
244
+ function PageChip({
245
+ tone,
246
+ icon,
247
+ children
248
+ }) {
249
+ const palette = {
250
+ blue: { bg: "#EEF2FF", fg: "#2B44FF" },
251
+ accent: { bg: "var(--accent-light)", fg: "var(--p-accent)" },
252
+ neutral: { bg: "var(--warm)", fg: "var(--ink-2)" },
253
+ red: { bg: "#FEF2F2", fg: "#E63946" }
254
+ };
255
+ const c = palette[tone];
256
+ return /* @__PURE__ */ React.createElement(
257
+ "span",
258
+ {
259
+ className: "inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 font-mono text-[10px] font-bold uppercase tracking-[0.18em]",
260
+ style: { background: c.bg, color: c.fg }
261
+ },
262
+ icon,
263
+ children
264
+ );
265
+ }
266
+ function PageStat({
267
+ label,
268
+ children
269
+ }) {
270
+ return /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1.5 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: "min-h-[20px]" }, children));
271
+ }
272
+ function PageRelatedSection({
273
+ title,
274
+ icon,
275
+ items,
276
+ runtimeByTitle,
277
+ onOpen,
278
+ L
279
+ }) {
280
+ return /* @__PURE__ */ React.createElement("section", null, /* @__PURE__ */ React.createElement("div", { className: "mb-2 flex items-baseline gap-2" }, /* @__PURE__ */ React.createElement(Kicker, { icon }, title), /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10px] uppercase tracking-[0.18em] text-p-ink-3/70 tabular-nums" }, "\xB7 ", items.length), /* @__PURE__ */ React.createElement("span", { className: "ml-2 h-px flex-1 bg-p-line" })), /* @__PURE__ */ React.createElement("ol", { className: "flex flex-col gap-1" }, items.map((t) => /* @__PURE__ */ React.createElement(
281
+ RelatedRow,
282
+ {
283
+ key: t,
284
+ title: t,
285
+ runtime: runtimeByTitle?.get(t),
286
+ onOpen,
287
+ L
288
+ }
289
+ ))));
290
+ }
291
+ const ACTION_DESCRIPTORS = [
292
+ {
293
+ key: "run",
294
+ icon: Play,
295
+ label: "Run now",
296
+ defaultConfirm: {
297
+ title: "Run this task now?",
298
+ message: "A new run will be queued for the assigned agent.",
299
+ confirmLabel: "Run"
300
+ }
301
+ },
302
+ {
303
+ key: "retry",
304
+ icon: RotateCcw,
305
+ label: "Retry",
306
+ defaultConfirm: {
307
+ title: "Retry this task?",
308
+ message: "A fresh run will overwrite the previous outcome.",
309
+ confirmLabel: "Retry"
310
+ }
311
+ },
312
+ {
313
+ key: "reassess",
314
+ icon: Scale,
315
+ label: "Reassess",
316
+ defaultConfirm: {
317
+ title: "Reassess the outcome?",
318
+ message: "The judge will re-evaluate the task without re-running it.",
319
+ confirmLabel: "Reassess"
320
+ }
321
+ },
322
+ {
323
+ key: "abort",
324
+ icon: Square,
325
+ label: "Abort",
326
+ defaultConfirm: {
327
+ title: "Abort this task?",
328
+ message: "All in-progress work will be stopped immediately.",
329
+ confirmLabel: "Abort",
330
+ destructive: true
331
+ },
332
+ destructiveButton: true
333
+ }
334
+ ];
335
+ function ActionDock({
336
+ actions,
337
+ status,
338
+ confirm
339
+ }) {
340
+ const [pending, setPending] = useState(null);
341
+ const handle = async (key, desc, cfg) => {
342
+ if (pending) return;
343
+ setPending(key);
344
+ try {
345
+ if (cfg.confirm !== false && confirm) {
346
+ const override = cfg.confirm;
347
+ const merged = {
348
+ ...desc.defaultConfirm,
349
+ ...override ?? {}
350
+ };
351
+ const ok = await confirm({
352
+ title: merged.title,
353
+ message: merged.message,
354
+ confirmLabel: merged.confirmLabel,
355
+ destructive: merged.destructive
356
+ });
357
+ if (!ok) return;
358
+ }
359
+ cfg.onConfirm();
360
+ } finally {
361
+ setPending(null);
362
+ }
363
+ };
364
+ const visible = ACTION_DESCRIPTORS.filter((desc) => {
365
+ const cfg = actions[desc.key];
366
+ if (!cfg) return false;
367
+ return isActionApplicable(desc.key, status);
368
+ });
369
+ if (visible.length === 0) return null;
370
+ return /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-1.5" }, visible.map((desc) => {
371
+ const cfg = actions[desc.key];
372
+ const Icon = desc.icon;
373
+ const isPending = pending === desc.key;
374
+ return /* @__PURE__ */ React.createElement(
375
+ "button",
376
+ {
377
+ key: desc.key,
378
+ type: "button",
379
+ onClick: () => handle(desc.key, desc, cfg),
380
+ disabled: cfg.disabled || isPending,
381
+ className: [
382
+ "inline-flex h-8 items-center gap-1.5 rounded-md border px-2.5 font-mono text-[11px] font-bold uppercase tracking-[0.14em] transition-colors cursor-pointer disabled:cursor-not-allowed disabled:opacity-50",
383
+ desc.destructiveButton ? "border-[#FECACA] bg-p-surface text-[#E63946] hover:bg-[#FEF2F2]" : "border-p-line bg-p-surface text-p-ink-2 hover:border-p-ink-3 hover:bg-p-warm hover:text-p-ink"
384
+ ].join(" ")
385
+ },
386
+ /* @__PURE__ */ React.createElement(Icon, { className: "size-3", "aria-hidden": true }),
387
+ desc.label
388
+ );
389
+ }));
390
+ }
391
+ function isActionApplicable(key, status) {
392
+ switch (key) {
393
+ case "run":
394
+ return status === "draft" || status === "pending" || status === "failed";
395
+ case "retry":
396
+ return status === "done" || status === "failed";
397
+ case "reassess":
398
+ return status === "done" || status === "failed" || status === "review";
399
+ case "abort":
400
+ return status === "running" || status === "review";
401
+ }
402
+ }
403
+ function Kicker({
404
+ children,
405
+ icon
406
+ }) {
407
+ return /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-1.5 font-mono text-[10px] font-bold uppercase tracking-[0.22em] text-p-ink-3" }, icon, children);
408
+ }
409
+ function Cell({ label, children }) {
410
+ return /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1 bg-p-surface px-3 py-2" }, /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[9.5px] font-bold uppercase tracking-[0.18em] text-p-ink-3" }, label), /* @__PURE__ */ React.createElement("span", { className: "min-h-[18px]" }, children));
411
+ }
412
+ function RelatedRow({
413
+ title,
414
+ runtime,
415
+ onOpen,
416
+ L
417
+ }) {
418
+ const status = runtime?.status ?? "draft";
419
+ const Icon = STATUS_ICON[status];
420
+ const tone = STATUS_TONE[status];
421
+ const interactive = !!onOpen;
422
+ const Wrapper = interactive ? "button" : "div";
423
+ return /* @__PURE__ */ React.createElement(
424
+ Wrapper,
425
+ {
426
+ type: interactive ? "button" : void 0,
427
+ onClick: interactive ? () => onOpen(title) : void 0,
428
+ className: [
429
+ "group flex items-center gap-2 rounded-md border border-p-line bg-p-surface px-2.5 py-1.5 text-left transition-colors",
430
+ interactive ? "cursor-pointer hover:border-p-ink-3 hover:bg-p-warm/40" : ""
431
+ ].join(" ")
432
+ },
433
+ /* @__PURE__ */ React.createElement(Icon, { className: ["size-3 shrink-0", tone].join(" "), "aria-hidden": true }),
434
+ /* @__PURE__ */ React.createElement("span", { className: "min-w-0 flex-1 truncate font-display text-[12.5px] font-semibold text-p-ink" }, title),
435
+ /* @__PURE__ */ React.createElement(
436
+ "span",
437
+ {
438
+ className: [
439
+ "shrink-0 font-mono text-[9.5px] font-bold uppercase tracking-[0.16em]",
440
+ tone
441
+ ].join(" ")
442
+ },
443
+ L.status[status]
444
+ ),
445
+ interactive ? /* @__PURE__ */ React.createElement(Clock, { className: "size-2.5 shrink-0 text-p-ink-3 opacity-0 transition-opacity group-hover:opacity-100" }) : null
446
+ );
447
+ }
448
+ export {
449
+ PlanTaskDetail
450
+ };
@@ -0,0 +1,85 @@
1
+ type PlanTaskStatus = "draft" | "pending" | "running" | "review" | "done" | "failed";
2
+ interface PlanTask {
3
+ title: string;
4
+ description?: string;
5
+ /** Agent name responsible for this task. */
6
+ assignTo?: string;
7
+ /** Predecessor task titles. */
8
+ dependsOn?: string[];
9
+ /** Optional category — e.g. execution / planning / review. */
10
+ phase?: string;
11
+ }
12
+ interface PlanCheckpoint {
13
+ name: string;
14
+ afterTasks: string[];
15
+ blocksTasks: string[];
16
+ message?: string;
17
+ }
18
+ interface PlanQualityGate {
19
+ name: string;
20
+ afterTasks: string[];
21
+ blocksTasks: string[];
22
+ minScore?: number;
23
+ requireAllPassed?: boolean;
24
+ condition?: string;
25
+ }
26
+ interface PlanDelay {
27
+ name: string;
28
+ afterTasks: string[];
29
+ blocksTasks: string[];
30
+ /** ISO 8601 duration (e.g. PT2H, P1D). */
31
+ duration: string;
32
+ message?: string;
33
+ }
34
+ interface Plan {
35
+ name?: string;
36
+ tasks: PlanTask[];
37
+ checkpoints?: PlanCheckpoint[];
38
+ qualityGates?: PlanQualityGate[];
39
+ delays?: PlanDelay[];
40
+ }
41
+ interface PlanTaskRuntime {
42
+ status: PlanTaskStatus;
43
+ agentName?: string;
44
+ phase?: string;
45
+ /** Surfaced as a small red dot in the node corner. */
46
+ sideEffects?: boolean;
47
+ priority?: number;
48
+ }
49
+ interface PlanGraphLabels {
50
+ task: string;
51
+ checkpoint: string;
52
+ qualityGate: string;
53
+ delay: string;
54
+ start: string;
55
+ end: string;
56
+ emptyTitle: string;
57
+ emptyBody: string;
58
+ qualityMinScore: string;
59
+ qualityRequireAll: string;
60
+ status: Record<PlanTaskStatus, string>;
61
+ }
62
+ declare const defaultPlanGraphLabels: PlanGraphLabels;
63
+ interface PlanDetailLabels {
64
+ kicker: string;
65
+ taskCountLabel: (n: number) => string;
66
+ checkpointCountLabel: (n: number) => string;
67
+ gateCountLabel: (n: number) => string;
68
+ delayCountLabel: (n: number) => string;
69
+ breakdown: string;
70
+ noTasks: string;
71
+ status: Record<PlanTaskStatus, string>;
72
+ }
73
+ declare const defaultPlanDetailLabels: PlanDetailLabels;
74
+ interface RunKanbanLabels {
75
+ scheduled: string;
76
+ pending: string;
77
+ running: string;
78
+ done: string;
79
+ failed: string;
80
+ cancelled: string;
81
+ emptyLane: string;
82
+ }
83
+ declare const defaultRunKanbanLabels: RunKanbanLabels;
84
+
85
+ export { type Plan, type PlanCheckpoint, type PlanDelay, type PlanDetailLabels, type PlanGraphLabels, type PlanQualityGate, type PlanTask, type PlanTaskRuntime, type PlanTaskStatus, type RunKanbanLabels, defaultPlanDetailLabels, defaultPlanGraphLabels, defaultRunKanbanLabels };
@@ -0,0 +1,51 @@
1
+ const defaultPlanGraphLabels = {
2
+ task: "task",
3
+ checkpoint: "checkpoint",
4
+ qualityGate: "gate",
5
+ delay: "delay",
6
+ start: "start",
7
+ end: "end",
8
+ emptyTitle: "No plan yet",
9
+ emptyBody: "Add tasks, checkpoints or quality gates to shape the workflow.",
10
+ qualityMinScore: "min score",
11
+ qualityRequireAll: "all must pass",
12
+ status: {
13
+ draft: "draft",
14
+ pending: "pending",
15
+ running: "running",
16
+ review: "review",
17
+ done: "done",
18
+ failed: "failed"
19
+ }
20
+ };
21
+ const defaultPlanDetailLabels = {
22
+ kicker: "plan",
23
+ taskCountLabel: (n) => `${n} tasks`,
24
+ checkpointCountLabel: (n) => `${n} checkpoints`,
25
+ gateCountLabel: (n) => `${n} gates`,
26
+ delayCountLabel: (n) => `${n} delays`,
27
+ breakdown: "Tasks breakdown",
28
+ noTasks: "No tasks defined yet.",
29
+ status: {
30
+ draft: "draft",
31
+ pending: "pending",
32
+ running: "running",
33
+ review: "review",
34
+ done: "done",
35
+ failed: "failed"
36
+ }
37
+ };
38
+ const defaultRunKanbanLabels = {
39
+ scheduled: "Scheduled",
40
+ pending: "Pending",
41
+ running: "Running",
42
+ done: "Done",
43
+ failed: "Failed",
44
+ cancelled: "Cancelled",
45
+ emptyLane: "\u2014"
46
+ };
47
+ export {
48
+ defaultPlanDetailLabels,
49
+ defaultPlanGraphLabels,
50
+ defaultRunKanbanLabels
51
+ };
@@ -0,0 +1,24 @@
1
+ import * as react from 'react';
2
+ import { RunKanbanLaneSummary } from './run-kanban.js';
3
+ import './plan-types.js';
4
+ import './types.js';
5
+
6
+ interface RunKanbanFilterMenuProps {
7
+ lanes: RunKanbanLaneSummary[];
8
+ value: ReadonlySet<string>;
9
+ onChange: (next: Set<string>) => void;
10
+ /** Hide lanes that have zero items from the menu. Default false. */
11
+ hideEmpty?: boolean;
12
+ /** Trigger label, e.g. "Filter". Defaults to "Filter". */
13
+ triggerLabel?: string;
14
+ className?: string;
15
+ }
16
+ /**
17
+ * Click-to-open dropdown variant of `<RunKanbanFilters>`. Use when
18
+ * the kanban has too many lanes to comfortably show as inline chips
19
+ * (Linear's pattern). The trigger shows `visible / total` as a
20
+ * tabular badge and the menu lists every lane with a checkmark.
21
+ */
22
+ declare function RunKanbanFilterMenu({ lanes, value, onChange, hideEmpty, triggerLabel, className, }: RunKanbanFilterMenuProps): react.JSX.Element;
23
+
24
+ export { RunKanbanFilterMenu, type RunKanbanFilterMenuProps };