@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.
- package/README.md +21 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +93 -0
- package/dist/lib/format.d.ts +3 -0
- package/dist/lib/format.js +9 -0
- package/dist/orchestrator-document.d.ts +37 -0
- package/dist/orchestrator-document.js +122 -0
- package/dist/plan-detail.d.ts +102 -0
- package/dist/plan-detail.js +385 -0
- package/dist/plan-graph.d.ts +39 -0
- package/dist/plan-graph.js +597 -0
- package/dist/plan-node-detail.d.ts +29 -0
- package/dist/plan-node-detail.js +346 -0
- package/dist/plan-task-detail.d.ts +76 -0
- package/dist/plan-task-detail.js +450 -0
- package/dist/plan-types.d.ts +85 -0
- package/dist/plan-types.js +51 -0
- package/dist/run-kanban-filter-menu.d.ts +24 -0
- package/dist/run-kanban-filter-menu.js +152 -0
- package/dist/run-kanban.d.ts +61 -0
- package/dist/run-kanban.js +234 -0
- package/dist/swarm-agent-badge.d.ts +15 -0
- package/dist/swarm-agent-badge.js +39 -0
- package/dist/swarm-run-activity.d.ts +39 -0
- package/dist/swarm-run-activity.js +289 -0
- package/dist/swarm-run-card.d.ts +22 -0
- package/dist/swarm-run-card.js +91 -0
- package/dist/swarm-run-detail.d.ts +45 -0
- package/dist/swarm-run-detail.js +559 -0
- package/dist/swarm-run-list.d.ts +22 -0
- package/dist/swarm-run-list.js +75 -0
- package/dist/swarm-run-row.d.ts +22 -0
- package/dist/swarm-run-row.js +125 -0
- package/dist/swarm-skeletons.d.ts +28 -0
- package/dist/swarm-skeletons.js +78 -0
- package/dist/swarm-status-bar.d.ts +15 -0
- package/dist/swarm-status-bar.js +79 -0
- package/dist/swarm-status-pill.d.ts +12 -0
- package/dist/swarm-status-pill.js +86 -0
- package/dist/swarm-timeline.d.ts +21 -0
- package/dist/swarm-timeline.js +414 -0
- package/dist/task-workspace-sidebar.d.ts +71 -0
- package/dist/task-workspace-sidebar.js +352 -0
- package/dist/types.d.ts +285 -0
- package/dist/types.js +44 -0
- 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 };
|