@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,346 @@
1
+ "use client";
2
+ import { useMemo } from "react";
3
+ import {
4
+ ArrowRight,
5
+ CheckCircle2,
6
+ Circle,
7
+ Clock,
8
+ Hourglass,
9
+ Pause,
10
+ RotateCcw,
11
+ Scale,
12
+ ShieldCheck,
13
+ XCircle
14
+ } from "lucide-react";
15
+ import {
16
+ defaultPlanGraphLabels
17
+ } from "./plan-types";
18
+ function PlanCheckpointDetail({
19
+ checkpoint,
20
+ runtimeByTitle,
21
+ plan,
22
+ headerTrailing,
23
+ actionsSlot,
24
+ onOpenRelated,
25
+ labels,
26
+ className
27
+ }) {
28
+ const L = useMergedLabels(labels);
29
+ const after = checkpoint.afterTasks ?? [];
30
+ const blocks = checkpoint.blocksTasks ?? [];
31
+ return /* @__PURE__ */ React.createElement(
32
+ Frame,
33
+ {
34
+ tone: CHECKPOINT_TONE,
35
+ kind: L.checkpoint,
36
+ icon: /* @__PURE__ */ React.createElement(Hourglass, { className: "size-3.5", "aria-hidden": true }),
37
+ name: checkpoint.name,
38
+ headerTrailing,
39
+ actionsSlot,
40
+ className
41
+ },
42
+ checkpoint.message ? /* @__PURE__ */ React.createElement(Section, null, /* @__PURE__ */ React.createElement(Kicker, null, "review prompt"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 font-body text-[13px] leading-[1.55] text-p-ink-2" }, checkpoint.message)) : null,
43
+ /* @__PURE__ */ React.createElement(MetaGrid, null, /* @__PURE__ */ React.createElement(Cell, { label: "kind" }, /* @__PURE__ */ React.createElement(ChipText, { tone: CHECKPOINT_TONE }, L.checkpoint)), /* @__PURE__ */ React.createElement(Cell, { label: "gates" }, /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10.5px] uppercase tracking-[0.14em] text-p-ink" }, "human review"))),
44
+ /* @__PURE__ */ React.createElement(
45
+ RelatedSections,
46
+ {
47
+ after,
48
+ blocks,
49
+ runtimeByTitle,
50
+ plan,
51
+ onOpenRelated,
52
+ L
53
+ }
54
+ )
55
+ );
56
+ }
57
+ function PlanQualityGateDetail({
58
+ gate,
59
+ runtimeByTitle,
60
+ plan,
61
+ headerTrailing,
62
+ actionsSlot,
63
+ onOpenRelated,
64
+ labels,
65
+ className
66
+ }) {
67
+ const L = useMergedLabels(labels);
68
+ const after = gate.afterTasks ?? [];
69
+ const blocks = gate.blocksTasks ?? [];
70
+ return /* @__PURE__ */ React.createElement(
71
+ Frame,
72
+ {
73
+ tone: GATE_TONE,
74
+ kind: L.qualityGate,
75
+ icon: /* @__PURE__ */ React.createElement(ShieldCheck, { className: "size-3.5", "aria-hidden": true }),
76
+ name: gate.name,
77
+ headerTrailing,
78
+ actionsSlot,
79
+ className
80
+ },
81
+ gate.condition ? /* @__PURE__ */ React.createElement(Section, null, /* @__PURE__ */ React.createElement(Kicker, null, "condition"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 font-body text-[13px] leading-[1.55] text-p-ink-2" }, gate.condition)) : null,
82
+ /* @__PURE__ */ React.createElement(MetaGrid, null, /* @__PURE__ */ React.createElement(Cell, { label: L.qualityMinScore }, typeof gate.minScore === "number" ? /* @__PURE__ */ React.createElement("span", { className: "font-display text-[16px] font-bold tabular-nums text-p-ink" }, gate.minScore, /* @__PURE__ */ React.createElement("span", { className: "ml-0.5 font-mono text-[10px] font-bold uppercase tracking-[0.14em] text-p-ink-3" }, "/10")) : /* @__PURE__ */ React.createElement(Dash, null)), /* @__PURE__ */ React.createElement(Cell, { label: L.qualityRequireAll }, /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10.5px] font-bold uppercase tracking-[0.14em] text-p-ink" }, gate.requireAllPassed ? "yes" : "no"))),
83
+ /* @__PURE__ */ React.createElement(
84
+ RelatedSections,
85
+ {
86
+ after,
87
+ blocks,
88
+ runtimeByTitle,
89
+ plan,
90
+ onOpenRelated,
91
+ L
92
+ }
93
+ )
94
+ );
95
+ }
96
+ function PlanDelayDetail({
97
+ delay,
98
+ runtimeByTitle,
99
+ plan,
100
+ headerTrailing,
101
+ actionsSlot,
102
+ onOpenRelated,
103
+ labels,
104
+ className
105
+ }) {
106
+ const L = useMergedLabels(labels);
107
+ const after = delay.afterTasks ?? [];
108
+ const blocks = delay.blocksTasks ?? [];
109
+ const human = useMemo(() => parseIsoDuration(delay.duration), [delay.duration]);
110
+ return /* @__PURE__ */ React.createElement(
111
+ Frame,
112
+ {
113
+ tone: DELAY_TONE,
114
+ kind: L.delay,
115
+ icon: /* @__PURE__ */ React.createElement(Clock, { className: "size-3.5", "aria-hidden": true }),
116
+ name: delay.name,
117
+ headerTrailing,
118
+ actionsSlot,
119
+ className
120
+ },
121
+ delay.message ? /* @__PURE__ */ React.createElement(Section, null, /* @__PURE__ */ React.createElement(Kicker, null, "note"), /* @__PURE__ */ React.createElement("p", { className: "mt-1 font-body text-[13px] leading-[1.55] text-p-ink-2" }, delay.message)) : null,
122
+ /* @__PURE__ */ React.createElement(MetaGrid, null, /* @__PURE__ */ React.createElement(Cell, { label: "duration" }, /* @__PURE__ */ React.createElement("span", { className: "font-display text-[18px] font-bold tabular-nums text-p-ink" }, human)), /* @__PURE__ */ React.createElement(Cell, { label: "iso" }, /* @__PURE__ */ React.createElement("code", { className: "font-mono text-[11px] tracking-[0.04em] text-p-ink-2" }, delay.duration))),
123
+ /* @__PURE__ */ React.createElement(
124
+ RelatedSections,
125
+ {
126
+ after,
127
+ blocks,
128
+ runtimeByTitle,
129
+ plan,
130
+ onOpenRelated,
131
+ L
132
+ }
133
+ )
134
+ );
135
+ }
136
+ const CHECKPOINT_TONE = {
137
+ bg: "#FDF8E8",
138
+ fg: "#8A6B0B",
139
+ border: "#D4A017",
140
+ chipBg: "#FDF8E8",
141
+ chipFg: "#8A6B0B"
142
+ };
143
+ const GATE_TONE = {
144
+ bg: "var(--accent-light)",
145
+ fg: "var(--p-accent)",
146
+ border: "var(--p-accent)",
147
+ chipBg: "var(--accent-light)",
148
+ chipFg: "var(--p-accent)"
149
+ };
150
+ const DELAY_TONE = {
151
+ bg: "#F3EEFF",
152
+ fg: "#7B3FE4",
153
+ border: "#7B3FE4",
154
+ chipBg: "#F3EEFF",
155
+ chipFg: "#7B3FE4"
156
+ };
157
+ function Frame({
158
+ tone,
159
+ kind,
160
+ icon,
161
+ name,
162
+ children,
163
+ headerTrailing,
164
+ actionsSlot,
165
+ className
166
+ }) {
167
+ return /* @__PURE__ */ React.createElement(
168
+ "article",
169
+ {
170
+ className: [
171
+ "flex h-full min-h-0 flex-col overflow-hidden bg-p-surface",
172
+ className || ""
173
+ ].join(" ")
174
+ },
175
+ /* @__PURE__ */ React.createElement(
176
+ "header",
177
+ {
178
+ className: "flex shrink-0 items-start gap-3 border-b border-p-line px-5 py-4",
179
+ style: { background: tone.bg }
180
+ },
181
+ /* @__PURE__ */ React.createElement(
182
+ "span",
183
+ {
184
+ "aria-hidden": true,
185
+ className: "mt-0.5 grid size-7 shrink-0 place-items-center rounded-full bg-p-surface ring-1",
186
+ style: { color: tone.fg, boxShadow: `inset 0 0 0 1px ${tone.border}` }
187
+ },
188
+ icon
189
+ ),
190
+ /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement(
191
+ "div",
192
+ {
193
+ className: "font-mono text-[10px] font-bold uppercase tracking-[0.22em]",
194
+ style: { color: tone.fg }
195
+ },
196
+ kind
197
+ ), /* @__PURE__ */ React.createElement("h3", { className: "mt-1 font-display text-[18px] font-bold leading-tight tracking-[-0.01em] text-p-ink" }, name)),
198
+ headerTrailing
199
+ ),
200
+ /* @__PURE__ */ React.createElement("div", { className: "flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto px-5 py-4" }, children),
201
+ actionsSlot ? /* @__PURE__ */ React.createElement("footer", { className: "flex shrink-0 items-center justify-end gap-2 border-t border-p-line bg-p-bg/50 px-5 py-3" }, actionsSlot) : null
202
+ );
203
+ }
204
+ const STATUS_ICON = {
205
+ draft: Circle,
206
+ pending: Pause,
207
+ running: RotateCcw,
208
+ review: Scale,
209
+ done: CheckCircle2,
210
+ failed: XCircle
211
+ };
212
+ const STATUS_TONE = {
213
+ draft: "text-p-ink-3",
214
+ pending: "text-p-ink-3",
215
+ running: "text-p-accent",
216
+ review: "text-[#2B44FF]",
217
+ done: "text-p-green",
218
+ failed: "text-[#E63946]"
219
+ };
220
+ function useMergedLabels(labels) {
221
+ return useMemo(
222
+ () => ({ ...defaultPlanGraphLabels, ...labels }),
223
+ [labels]
224
+ );
225
+ }
226
+ function Section({ children }) {
227
+ return /* @__PURE__ */ React.createElement("section", null, children);
228
+ }
229
+ function MetaGrid({ children }) {
230
+ return /* @__PURE__ */ React.createElement("section", { className: "grid grid-cols-2 gap-px overflow-hidden rounded-md border border-p-line bg-p-line text-[11px]" }, children);
231
+ }
232
+ function Cell({ label, children }) {
233
+ 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));
234
+ }
235
+ function Kicker({
236
+ children,
237
+ icon
238
+ }) {
239
+ 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);
240
+ }
241
+ function Dash() {
242
+ return /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10.5px] uppercase tracking-[0.14em] text-p-ink-3" }, "\u2014");
243
+ }
244
+ function ChipText({
245
+ tone,
246
+ children
247
+ }) {
248
+ return /* @__PURE__ */ React.createElement(
249
+ "span",
250
+ {
251
+ className: "inline-flex items-center rounded-sm px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase tracking-[0.16em]",
252
+ style: { background: tone.chipBg, color: tone.chipFg }
253
+ },
254
+ children
255
+ );
256
+ }
257
+ function RelatedSections({
258
+ after,
259
+ blocks,
260
+ runtimeByTitle,
261
+ plan,
262
+ onOpenRelated,
263
+ L
264
+ }) {
265
+ const knownTitles = useMemo(() => {
266
+ if (!plan) return null;
267
+ return new Set(plan.tasks.map((t) => t.title));
268
+ }, [plan]);
269
+ const filterValid = (titles) => knownTitles ? titles.filter((t) => knownTitles.has(t)) : titles;
270
+ const safeAfter = filterValid(after);
271
+ const safeBlocks = filterValid(blocks);
272
+ if (safeAfter.length === 0 && safeBlocks.length === 0) {
273
+ return /* @__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("span", { className: "font-mono text-[10.5px] uppercase tracking-[0.16em] text-p-ink-3" }, "standalone \u2014 not gating any task"));
274
+ }
275
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, safeAfter.length > 0 ? /* @__PURE__ */ React.createElement("section", null, /* @__PURE__ */ React.createElement(Kicker, { icon: /* @__PURE__ */ React.createElement(ArrowRight, { className: "size-2.5 rotate-180" }) }, "after"), /* @__PURE__ */ React.createElement("ol", { className: "mt-1 flex flex-col gap-0.5" }, safeAfter.map((title) => /* @__PURE__ */ React.createElement(
276
+ RelatedRow,
277
+ {
278
+ key: title,
279
+ title,
280
+ runtime: runtimeByTitle?.get(title),
281
+ onOpen: onOpenRelated,
282
+ L
283
+ }
284
+ )))) : null, safeBlocks.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" }, safeBlocks.map((title) => /* @__PURE__ */ React.createElement(
285
+ RelatedRow,
286
+ {
287
+ key: title,
288
+ title,
289
+ runtime: runtimeByTitle?.get(title),
290
+ onOpen: onOpenRelated,
291
+ L
292
+ }
293
+ )))) : null);
294
+ }
295
+ function RelatedRow({
296
+ title,
297
+ runtime,
298
+ onOpen,
299
+ L
300
+ }) {
301
+ const status = runtime?.status ?? "draft";
302
+ const Icon = STATUS_ICON[status];
303
+ const tone = STATUS_TONE[status];
304
+ const interactive = !!onOpen;
305
+ const Wrapper = interactive ? "button" : "div";
306
+ return /* @__PURE__ */ React.createElement(
307
+ Wrapper,
308
+ {
309
+ type: interactive ? "button" : void 0,
310
+ onClick: interactive ? () => onOpen(title) : void 0,
311
+ className: [
312
+ "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",
313
+ interactive ? "cursor-pointer hover:border-p-ink-3 hover:bg-p-warm/40" : ""
314
+ ].join(" ")
315
+ },
316
+ /* @__PURE__ */ React.createElement(Icon, { className: ["size-3 shrink-0", tone].join(" "), "aria-hidden": true }),
317
+ /* @__PURE__ */ React.createElement("span", { className: "min-w-0 flex-1 truncate font-display text-[12.5px] font-semibold text-p-ink" }, title),
318
+ /* @__PURE__ */ React.createElement(
319
+ "span",
320
+ {
321
+ className: [
322
+ "shrink-0 font-mono text-[9.5px] font-bold uppercase tracking-[0.16em]",
323
+ tone
324
+ ].join(" ")
325
+ },
326
+ L.status[status]
327
+ )
328
+ );
329
+ }
330
+ function parseIsoDuration(iso) {
331
+ if (!iso) return "\u2014";
332
+ const m = iso.match(/^P(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/);
333
+ if (!m) return iso;
334
+ const [, d, h, mi, s] = m;
335
+ const out = [];
336
+ if (d) out.push(`${d}d`);
337
+ if (h) out.push(`${h}h`);
338
+ if (mi) out.push(`${mi}m`);
339
+ if (s) out.push(`${s}s`);
340
+ return out.join(" ") || iso;
341
+ }
342
+ export {
343
+ PlanCheckpointDetail,
344
+ PlanDelayDetail,
345
+ PlanQualityGateDetail
346
+ };
@@ -0,0 +1,76 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { PlanTask, Plan, PlanTaskRuntime, PlanGraphLabels } from './plan-types.js';
4
+ import { SwarmAgentRef } from './types.js';
5
+
6
+ type PlanTaskDetailVariant = "panel" | "page";
7
+ /**
8
+ * Async confirm slot — consumer wires this to `<ConfirmDialog>` (or
9
+ * `window.confirm` in dev). Returns `true` to proceed, `false` to abort.
10
+ */
11
+ type PlanTaskConfirmFn = (opts: {
12
+ title: string;
13
+ message?: string;
14
+ confirmLabel: string;
15
+ cancelLabel?: string;
16
+ destructive?: boolean;
17
+ }) => Promise<boolean>;
18
+ interface PlanTaskActionConfig {
19
+ /** Fired after the user passes the confirm gate (or immediately when
20
+ * no confirm slot is wired). */
21
+ onConfirm: () => void;
22
+ /** Override the default confirm copy/strength. Pass `false` to skip
23
+ * the confirm gate entirely for this action. */
24
+ confirm?: false | {
25
+ title?: string;
26
+ message?: string;
27
+ confirmLabel?: string;
28
+ destructive?: boolean;
29
+ };
30
+ /** Disable the button while another action is in flight. */
31
+ disabled?: boolean;
32
+ }
33
+ interface PlanTaskActions {
34
+ /** Start a fresh run for this task. Default copy: non-destructive. */
35
+ run?: PlanTaskActionConfig;
36
+ /** Re-run the task after success or failure. Non-destructive. */
37
+ retry?: PlanTaskActionConfig;
38
+ /** Re-evaluate outcomes (judge / quality gates). Non-destructive. */
39
+ reassess?: PlanTaskActionConfig;
40
+ /** Stop a running task. Default copy: DESTRUCTIVE. */
41
+ abort?: PlanTaskActionConfig;
42
+ }
43
+ interface PlanTaskDetailProps {
44
+ task: PlanTask;
45
+ /** When provided, used to resolve `dependsOn` titles and the
46
+ * reverse "blocks" list. */
47
+ plan?: Plan;
48
+ runtimeByTitle?: Map<string, PlanTaskRuntime>;
49
+ agents?: SwarmAgentRef[];
50
+ /** `"panel"` (default) — compact side-pane shape used in drawers and
51
+ * graph overlays. `"page"` — editorial full-page shape with pills
52
+ * row, big title and inline action dock. */
53
+ variant?: PlanTaskDetailVariant;
54
+ /** Trailing slot in the header (e.g. close button). */
55
+ headerTrailing?: ReactNode;
56
+ /** Footer slot — used by the panel variant for compact controls.
57
+ * Ignored by the page variant, which renders `actions` inline. */
58
+ actionsSlot?: ReactNode;
59
+ /** Declarative action descriptors. Each enabled action is rendered
60
+ * as a button; clicking gates through `confirm` (when supplied)
61
+ * before invoking `onConfirm`. */
62
+ actions?: PlanTaskActions;
63
+ /** Async confirm gate. When omitted, actions fire immediately. */
64
+ confirm?: PlanTaskConfirmFn;
65
+ /** Click on a related task (dep or dependent) — passes the title. */
66
+ onOpenRelated?: (title: string) => void;
67
+ /** Render the task description as Markdown — wire `<MarkdownViewer/>`
68
+ * from `@lumea-labs/markdown`. When omitted, description is plain
69
+ * text. */
70
+ renderMarkdown?: (text: string) => ReactNode;
71
+ labels?: Partial<PlanGraphLabels>;
72
+ className?: string;
73
+ }
74
+ declare function PlanTaskDetail({ task, plan, runtimeByTitle, agents, variant, headerTrailing, actionsSlot, actions, confirm, onOpenRelated, renderMarkdown, labels, className, }: PlanTaskDetailProps): react.JSX.Element;
75
+
76
+ export { type PlanTaskActionConfig, type PlanTaskActions, type PlanTaskConfirmFn, PlanTaskDetail, type PlanTaskDetailProps, type PlanTaskDetailVariant };