@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,385 @@
1
+ "use client";
2
+ import { useMemo, useState } from "react";
3
+ import {
4
+ CheckCircle2,
5
+ ChevronDown,
6
+ ChevronRight,
7
+ Circle,
8
+ Pause,
9
+ RotateCcw,
10
+ Scale,
11
+ XCircle
12
+ } from "lucide-react";
13
+ import { PlanGraph } from "./plan-graph";
14
+ import {
15
+ defaultPlanDetailLabels,
16
+ defaultPlanGraphLabels
17
+ } from "./plan-types";
18
+ const STATUS_ICON = {
19
+ draft: Circle,
20
+ pending: Pause,
21
+ running: RotateCcw,
22
+ review: Scale,
23
+ done: CheckCircle2,
24
+ failed: XCircle
25
+ };
26
+ const STATUS_TONE = {
27
+ draft: "text-p-ink-3",
28
+ pending: "text-p-ink-3",
29
+ running: "text-p-accent",
30
+ review: "text-[#2B44FF]",
31
+ done: "text-p-green",
32
+ failed: "text-[#E63946]"
33
+ };
34
+ function PlanDetailHeader({
35
+ plan,
36
+ runtimeByTitle,
37
+ briefSlot,
38
+ actionsSlot,
39
+ headerLeading,
40
+ headerTrailing,
41
+ labels,
42
+ className
43
+ }) {
44
+ const L = useMemo(
45
+ () => ({ ...defaultPlanDetailLabels, ...labels }),
46
+ [labels]
47
+ );
48
+ const taskCount = plan.tasks.length;
49
+ const cpCount = plan.checkpoints?.length ?? 0;
50
+ const gateCount = plan.qualityGates?.length ?? 0;
51
+ const delayCount = plan.delays?.length ?? 0;
52
+ const counts = useMemo(() => statusCounts(plan, runtimeByTitle), [
53
+ plan,
54
+ runtimeByTitle
55
+ ]);
56
+ const hasRuntime = runtimeByTitle && runtimeByTitle.size > 0;
57
+ return /* @__PURE__ */ React.createElement(
58
+ "header",
59
+ {
60
+ className: [
61
+ "flex flex-col border-b border-p-line",
62
+ className || ""
63
+ ].join(" ")
64
+ },
65
+ /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1 px-5 py-3" }, headerLeading, /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10px] font-bold uppercase tracking-[0.24em] text-p-ink-3" }, L.kicker), /* @__PURE__ */ React.createElement("h2", { className: "font-display text-[22px] font-bold leading-tight tracking-[-0.02em] text-p-ink" }, plan.name ?? "\u2014"), /* @__PURE__ */ React.createElement("span", { className: "ml-auto inline-flex items-center gap-2" }, headerTrailing, actionsSlot)),
66
+ /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-2 gap-px border-t border-p-line bg-p-line sm:grid-cols-4" }, /* @__PURE__ */ React.createElement(MetaCell, { label: L.taskCountLabel(taskCount), accent: true }), /* @__PURE__ */ React.createElement(MetaCell, { label: L.checkpointCountLabel(cpCount) }), /* @__PURE__ */ React.createElement(MetaCell, { label: L.gateCountLabel(gateCount) }), /* @__PURE__ */ React.createElement(MetaCell, { label: L.delayCountLabel(delayCount) })),
67
+ hasRuntime ? /* @__PURE__ */ React.createElement(StatusCountsStrip, { counts, L }) : null,
68
+ briefSlot ? /* @__PURE__ */ React.createElement("div", { className: "border-t border-p-line px-5 py-4 font-body text-[14px] leading-[1.55] text-p-ink-2" }, briefSlot) : null
69
+ );
70
+ }
71
+ function PlanDetailMasthead({
72
+ plan,
73
+ runtimeByTitle,
74
+ briefSlot,
75
+ actionsSlot,
76
+ headerLeading,
77
+ headerTrailing,
78
+ labels,
79
+ className
80
+ }) {
81
+ const L = useMemo(
82
+ () => ({ ...defaultPlanDetailLabels, ...labels }),
83
+ [labels]
84
+ );
85
+ const taskCount = plan.tasks.length;
86
+ const cpCount = plan.checkpoints?.length ?? 0;
87
+ const gateCount = plan.qualityGates?.length ?? 0;
88
+ const delayCount = plan.delays?.length ?? 0;
89
+ const counts = useMemo(() => statusCounts(plan, runtimeByTitle), [
90
+ plan,
91
+ runtimeByTitle
92
+ ]);
93
+ const hasRuntime = runtimeByTitle && runtimeByTitle.size > 0;
94
+ return /* @__PURE__ */ React.createElement(
95
+ "header",
96
+ {
97
+ className: [
98
+ "flex flex-col gap-5 border-b border-p-line px-7 py-7",
99
+ className || ""
100
+ ].join(" ")
101
+ },
102
+ /* @__PURE__ */ React.createElement("div", { className: "flex items-start gap-4" }, headerLeading ? /* @__PURE__ */ React.createElement("div", { className: "shrink-0 pt-1" }, headerLeading) : null, /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10px] font-bold uppercase tracking-[0.24em] text-p-ink-3" }, L.kicker), /* @__PURE__ */ React.createElement("h2", { className: "mt-2 font-display text-[34px] font-bold leading-[1.04] tracking-[-0.025em] text-p-ink" }, plan.name ?? "\u2014")), headerTrailing || actionsSlot ? /* @__PURE__ */ React.createElement("div", { className: "shrink-0 flex flex-col items-end gap-2" }, headerTrailing, actionsSlot) : null),
103
+ briefSlot ? /* @__PURE__ */ React.createElement("div", { className: "max-w-[64ch] font-body text-[14.5px] leading-[1.65] text-p-ink-2" }, briefSlot) : null,
104
+ /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap items-baseline gap-x-4 gap-y-1.5 font-mono text-[10.5px] uppercase tracking-[0.22em]" }, /* @__PURE__ */ React.createElement(InventoryItem, { label: L.taskCountLabel(taskCount), accent: true }), /* @__PURE__ */ React.createElement(Sep, null), /* @__PURE__ */ React.createElement(InventoryItem, { label: L.checkpointCountLabel(cpCount) }), /* @__PURE__ */ React.createElement(Sep, null), /* @__PURE__ */ React.createElement(InventoryItem, { label: L.gateCountLabel(gateCount) }), /* @__PURE__ */ React.createElement(Sep, null), /* @__PURE__ */ React.createElement(InventoryItem, { label: L.delayCountLabel(delayCount) })),
105
+ hasRuntime ? /* @__PURE__ */ React.createElement(StatusCountsStrip, { counts, L, dense: true }) : null
106
+ );
107
+ }
108
+ function PlanDetailGraph({
109
+ plan,
110
+ runtimeByTitle,
111
+ agents,
112
+ graphHeight = 720,
113
+ fillHeight = false,
114
+ onOpenTask,
115
+ onOpenCheckpoint,
116
+ onOpenGate,
117
+ onOpenDelay,
118
+ selectedNodeId,
119
+ zoomToSelected,
120
+ graphLabels,
121
+ className
122
+ }) {
123
+ const GL = useMemo(
124
+ () => ({ ...defaultPlanGraphLabels, ...graphLabels }),
125
+ [graphLabels]
126
+ );
127
+ return /* @__PURE__ */ React.createElement(
128
+ "div",
129
+ {
130
+ className: [fillHeight ? "min-h-0 flex-1" : "", className || ""].join(" ").trim(),
131
+ style: fillHeight ? void 0 : { height: graphHeight }
132
+ },
133
+ /* @__PURE__ */ React.createElement(
134
+ PlanGraph,
135
+ {
136
+ tasks: plan.tasks,
137
+ runtimeByTitle,
138
+ checkpoints: plan.checkpoints,
139
+ qualityGates: plan.qualityGates,
140
+ delays: plan.delays,
141
+ agents,
142
+ onOpenTask,
143
+ onOpenCheckpoint,
144
+ onOpenGate,
145
+ onOpenDelay,
146
+ selectedNodeId,
147
+ zoomToSelected,
148
+ labels: GL,
149
+ className: "h-full"
150
+ }
151
+ )
152
+ );
153
+ }
154
+ function PlanDetailBreakdown({
155
+ plan,
156
+ runtimeByTitle,
157
+ agents,
158
+ defaultOpen = true,
159
+ alwaysOpen = false,
160
+ onOpenTask,
161
+ labels,
162
+ className
163
+ }) {
164
+ const L = useMemo(
165
+ () => ({ ...defaultPlanDetailLabels, ...labels }),
166
+ [labels]
167
+ );
168
+ const [open, setOpen] = useState(defaultOpen);
169
+ const isOpen = alwaysOpen || open;
170
+ const agentByName = useMemo(() => {
171
+ const m = /* @__PURE__ */ new Map();
172
+ for (const a of agents ?? []) m.set(a.name, a);
173
+ return m;
174
+ }, [agents]);
175
+ return /* @__PURE__ */ React.createElement(
176
+ "section",
177
+ {
178
+ className: [
179
+ "flex flex-col gap-2 border-t border-p-line px-5 py-4",
180
+ className || ""
181
+ ].join(" ")
182
+ },
183
+ alwaysOpen ? /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10px] font-bold uppercase tracking-[0.22em] text-p-ink-3" }, L.breakdown), /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10px] tabular-nums text-p-ink-3" }, String(plan.tasks.length).padStart(2, "0")), /* @__PURE__ */ React.createElement("span", { className: "h-px flex-1 bg-p-line" })) : /* @__PURE__ */ React.createElement(
184
+ "button",
185
+ {
186
+ type: "button",
187
+ onClick: () => setOpen((v) => !v),
188
+ "aria-expanded": open,
189
+ className: "group flex items-center gap-3 rounded-md border-none bg-transparent p-0 text-left cursor-pointer"
190
+ },
191
+ /* @__PURE__ */ React.createElement("span", { className: "grid size-5 shrink-0 place-items-center rounded text-p-ink-3 transition-colors group-hover:bg-p-warm group-hover:text-p-ink" }, open ? /* @__PURE__ */ React.createElement(ChevronDown, { className: "size-3.5" }) : /* @__PURE__ */ React.createElement(ChevronRight, { className: "size-3.5" })),
192
+ /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10px] font-bold uppercase tracking-[0.22em] text-p-ink-3 group-hover:text-p-ink" }, L.breakdown),
193
+ /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10px] tabular-nums text-p-ink-3" }, String(plan.tasks.length).padStart(2, "0")),
194
+ /* @__PURE__ */ React.createElement("span", { className: "h-px flex-1 bg-p-line" })
195
+ ),
196
+ !isOpen ? null : plan.tasks.length === 0 ? /* @__PURE__ */ React.createElement("p", { className: "px-1 py-2 font-mono text-[11px] uppercase tracking-[0.16em] text-p-ink-3" }, L.noTasks) : /* @__PURE__ */ React.createElement("ol", { className: "flex flex-col divide-y divide-p-line/60" }, plan.tasks.map((tk, i) => {
197
+ const rt = runtimeByTitle?.get(tk.title);
198
+ const status = rt?.status ?? "draft";
199
+ const Icon = STATUS_ICON[status];
200
+ const tone = STATUS_TONE[status];
201
+ const agentName = rt?.agentName ?? tk.assignTo ?? void 0;
202
+ const agentRef = agentName ? agentByName.get(agentName) : void 0;
203
+ return /* @__PURE__ */ React.createElement(
204
+ "li",
205
+ {
206
+ key: tk.title,
207
+ className: [
208
+ "grid grid-cols-[24px_minmax(0,1fr)_auto_88px] items-center gap-3 py-2",
209
+ onOpenTask ? "cursor-pointer transition-colors hover:bg-p-warm/40" : ""
210
+ ].join(" "),
211
+ onClick: onOpenTask ? () => onOpenTask({ title: tk.title }) : void 0
212
+ },
213
+ /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10px] font-bold uppercase tracking-[0.14em] text-p-ink-3 tabular-nums" }, String(i + 1).padStart(2, "0")),
214
+ /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex flex-col leading-tight" }, /* @__PURE__ */ React.createElement("span", { className: "truncate font-display text-[13.5px] font-bold text-p-ink" }, tk.title), tk.description ? /* @__PURE__ */ React.createElement("span", { className: "truncate font-body text-[11.5px] text-p-ink-3" }, tk.description) : null),
215
+ /* @__PURE__ */ React.createElement("span", { className: "inline-flex shrink-0 items-center gap-1.5 font-mono text-[10.5px] uppercase tracking-[0.14em] text-p-ink-2" }, agentRef ? /* @__PURE__ */ React.createElement(
216
+ "span",
217
+ {
218
+ "aria-hidden": true,
219
+ className: "grid size-4 shrink-0 place-items-center rounded font-display text-[9px] font-bold text-white",
220
+ style: { background: agentRef.color || "#999" }
221
+ },
222
+ agentRef.glyph || (agentRef.displayName ?? agentRef.name).charAt(0).toUpperCase()
223
+ ) : null, /* @__PURE__ */ React.createElement("span", { className: "truncate" }, agentName ?? "\u2014")),
224
+ /* @__PURE__ */ React.createElement(
225
+ "span",
226
+ {
227
+ className: [
228
+ "inline-flex shrink-0 items-center justify-end gap-1.5 font-mono text-[10.5px] font-bold uppercase tracking-[0.14em]",
229
+ tone
230
+ ].join(" ")
231
+ },
232
+ /* @__PURE__ */ React.createElement(Icon, { className: "size-3" }),
233
+ L.status[status]
234
+ )
235
+ );
236
+ }))
237
+ );
238
+ }
239
+ function PlanDetail({
240
+ plan,
241
+ runtimeByTitle,
242
+ agents,
243
+ briefSlot,
244
+ actionsSlot,
245
+ headerTrailing,
246
+ hideBreakdown = false,
247
+ defaultBreakdownOpen = true,
248
+ graphHeight = 720,
249
+ onOpenTask,
250
+ labels,
251
+ graphLabels,
252
+ className
253
+ }) {
254
+ return /* @__PURE__ */ React.createElement(
255
+ "article",
256
+ {
257
+ className: [
258
+ "flex flex-col overflow-hidden rounded-2xl border border-p-line bg-p-surface",
259
+ className || ""
260
+ ].join(" ")
261
+ },
262
+ /* @__PURE__ */ React.createElement(
263
+ PlanDetailHeader,
264
+ {
265
+ plan,
266
+ runtimeByTitle,
267
+ briefSlot,
268
+ actionsSlot,
269
+ headerTrailing,
270
+ labels
271
+ }
272
+ ),
273
+ /* @__PURE__ */ React.createElement(
274
+ PlanDetailGraph,
275
+ {
276
+ plan,
277
+ runtimeByTitle,
278
+ agents,
279
+ graphHeight,
280
+ onOpenTask,
281
+ graphLabels
282
+ }
283
+ ),
284
+ !hideBreakdown ? /* @__PURE__ */ React.createElement(
285
+ PlanDetailBreakdown,
286
+ {
287
+ plan,
288
+ runtimeByTitle,
289
+ agents,
290
+ defaultOpen: defaultBreakdownOpen,
291
+ onOpenTask,
292
+ labels
293
+ }
294
+ ) : null
295
+ );
296
+ }
297
+ function statusCounts(plan, runtimeByTitle) {
298
+ const c = {
299
+ draft: 0,
300
+ pending: 0,
301
+ running: 0,
302
+ review: 0,
303
+ done: 0,
304
+ failed: 0
305
+ };
306
+ for (const tk of plan.tasks) {
307
+ const s = runtimeByTitle?.get(tk.title)?.status ?? "draft";
308
+ c[s] += 1;
309
+ }
310
+ return c;
311
+ }
312
+ function StatusCountsStrip({
313
+ counts,
314
+ L,
315
+ dense
316
+ }) {
317
+ const order = [
318
+ "running",
319
+ "review",
320
+ "done",
321
+ "failed",
322
+ "pending",
323
+ "draft"
324
+ ];
325
+ const visible = order.filter((s) => counts[s] > 0);
326
+ if (visible.length === 0) return null;
327
+ return /* @__PURE__ */ React.createElement(
328
+ "div",
329
+ {
330
+ className: [
331
+ "flex flex-wrap items-center gap-3 font-mono text-[10.5px] uppercase tracking-[0.16em] text-p-ink-3",
332
+ dense ? "" : "border-t border-p-line px-5 py-2.5"
333
+ ].join(" ")
334
+ },
335
+ visible.map((s) => {
336
+ const Icon = STATUS_ICON[s];
337
+ return /* @__PURE__ */ React.createElement(
338
+ "span",
339
+ {
340
+ key: s,
341
+ className: ["inline-flex items-center gap-1.5", STATUS_TONE[s]].join(
342
+ " "
343
+ )
344
+ },
345
+ /* @__PURE__ */ React.createElement(Icon, { className: "size-3" }),
346
+ /* @__PURE__ */ React.createElement("span", { className: "font-display text-[14px] font-bold tabular-nums leading-none text-p-ink" }, counts[s]),
347
+ /* @__PURE__ */ React.createElement("span", null, L.status[s])
348
+ );
349
+ })
350
+ );
351
+ }
352
+ function MetaCell({ label, accent }) {
353
+ return /* @__PURE__ */ React.createElement("div", { className: "bg-p-surface px-4 py-2.5" }, /* @__PURE__ */ React.createElement(
354
+ "span",
355
+ {
356
+ className: [
357
+ "block font-mono text-[10px] font-bold uppercase tracking-[0.18em]",
358
+ accent ? "text-p-accent" : "text-p-ink-3"
359
+ ].join(" ")
360
+ },
361
+ label
362
+ ));
363
+ }
364
+ function InventoryItem({ label, accent }) {
365
+ return /* @__PURE__ */ React.createElement(
366
+ "span",
367
+ {
368
+ className: [
369
+ "font-bold",
370
+ accent ? "text-p-accent" : "text-p-ink-2"
371
+ ].join(" ")
372
+ },
373
+ label
374
+ );
375
+ }
376
+ function Sep() {
377
+ return /* @__PURE__ */ React.createElement("span", { "aria-hidden": true, className: "text-p-line" }, "\xB7");
378
+ }
379
+ export {
380
+ PlanDetail,
381
+ PlanDetailBreakdown,
382
+ PlanDetailGraph,
383
+ PlanDetailHeader,
384
+ PlanDetailMasthead
385
+ };
@@ -0,0 +1,39 @@
1
+ import * as react from 'react';
2
+ import { PlanTask, PlanTaskRuntime, PlanCheckpoint, PlanQualityGate, PlanDelay, PlanGraphLabels } from './plan-types.js';
3
+ import { SwarmAgentRef } from './types.js';
4
+
5
+ interface PlanGraphTaskRef {
6
+ title: string;
7
+ /** Optional opaque runtime id passed back to the consumer when present. */
8
+ runtimeId?: string;
9
+ }
10
+ interface PlanGraphProps {
11
+ tasks: PlanTask[];
12
+ /** Runtime status overlay, keyed by task title. */
13
+ runtimeByTitle?: Map<string, PlanTaskRuntime>;
14
+ checkpoints?: PlanCheckpoint[];
15
+ qualityGates?: PlanQualityGate[];
16
+ delays?: PlanDelay[];
17
+ agents?: SwarmAgentRef[];
18
+ onOpenTask?: (ref: PlanGraphTaskRef) => void;
19
+ /** Open callbacks for non-task nodes — receive the node's `name`. */
20
+ onOpenCheckpoint?: (name: string) => void;
21
+ onOpenGate?: (name: string) => void;
22
+ onOpenDelay?: (name: string) => void;
23
+ /** Controlled-selection prop — the canonical node id to highlight:
24
+ * - `task:<title>` for tasks
25
+ * - `cp:<name>` for checkpoints
26
+ * - `qg:<name>` for quality gates
27
+ * - `dl:<name>` for delays
28
+ * Pair with a toggle `onOpen*` handler to get "click again to
29
+ * deselect" behaviour. */
30
+ selectedNodeId?: string | null;
31
+ /** When `true`, animate the viewport to centre + zoom on the
32
+ * currently `selectedNodeId` node. Off by default. */
33
+ zoomToSelected?: boolean;
34
+ labels?: Partial<PlanGraphLabels>;
35
+ className?: string;
36
+ }
37
+ declare function PlanGraph(props: PlanGraphProps): react.JSX.Element;
38
+
39
+ export { PlanGraph, type PlanGraphProps, type PlanGraphTaskRef };