@elizaos/plugin-task-coordinator 2.0.3-beta.2 → 2.0.3-beta.3

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 (176) hide show
  1. package/assets/hero.png +0 -0
  2. package/dist/AgentTabsSection.d.ts +16 -0
  3. package/dist/AgentTabsSection.d.ts.map +1 -0
  4. package/dist/AgentTabsSection.js +169 -0
  5. package/dist/AgentTabsSection.js.map +1 -0
  6. package/dist/CodingAgentControlChip.d.ts +2 -0
  7. package/dist/CodingAgentControlChip.d.ts.map +1 -0
  8. package/dist/CodingAgentControlChip.js +73 -0
  9. package/dist/CodingAgentControlChip.js.map +1 -0
  10. package/dist/CodingAgentSettingsSection.d.ts +2 -0
  11. package/dist/CodingAgentSettingsSection.d.ts.map +1 -0
  12. package/dist/CodingAgentSettingsSection.js +379 -0
  13. package/dist/CodingAgentSettingsSection.js.map +1 -0
  14. package/dist/CodingAgentTasksPanel.d.ts +4 -0
  15. package/dist/CodingAgentTasksPanel.d.ts.map +1 -0
  16. package/dist/CodingAgentTasksPanel.interact.d.ts +2 -0
  17. package/dist/CodingAgentTasksPanel.interact.d.ts.map +1 -0
  18. package/dist/CodingAgentTasksPanel.interact.js +46 -0
  19. package/dist/CodingAgentTasksPanel.interact.js.map +1 -0
  20. package/dist/CodingAgentTasksPanel.js +740 -0
  21. package/dist/CodingAgentTasksPanel.js.map +1 -0
  22. package/dist/GitHubConnectionCard.d.ts +2 -0
  23. package/dist/GitHubConnectionCard.d.ts.map +1 -0
  24. package/dist/GitHubConnectionCard.js +172 -0
  25. package/dist/GitHubConnectionCard.js.map +1 -0
  26. package/dist/GlobalPrefsSection.d.ts +10 -0
  27. package/dist/GlobalPrefsSection.d.ts.map +1 -0
  28. package/dist/GlobalPrefsSection.js +166 -0
  29. package/dist/GlobalPrefsSection.js.map +1 -0
  30. package/dist/LlmProviderSection.d.ts +10 -0
  31. package/dist/LlmProviderSection.d.ts.map +1 -0
  32. package/dist/LlmProviderSection.js +161 -0
  33. package/dist/LlmProviderSection.js.map +1 -0
  34. package/dist/ModelConfigSection.d.ts +15 -0
  35. package/dist/ModelConfigSection.d.ts.map +1 -0
  36. package/dist/ModelConfigSection.js +86 -0
  37. package/dist/ModelConfigSection.js.map +1 -0
  38. package/dist/OrchestratorView.d.ts +20 -0
  39. package/dist/OrchestratorView.d.ts.map +1 -0
  40. package/dist/OrchestratorView.js +231 -0
  41. package/dist/OrchestratorView.js.map +1 -0
  42. package/dist/OrchestratorWorkbench.d.ts +32 -0
  43. package/dist/OrchestratorWorkbench.d.ts.map +1 -0
  44. package/dist/OrchestratorWorkbench.js +3200 -0
  45. package/dist/OrchestratorWorkbench.js.map +1 -0
  46. package/dist/PtyConsoleBase.d.ts +9 -0
  47. package/dist/PtyConsoleBase.d.ts.map +1 -0
  48. package/dist/PtyConsoleBase.js +174 -0
  49. package/dist/PtyConsoleBase.js.map +1 -0
  50. package/dist/PtyConsoleDrawer.d.ts +10 -0
  51. package/dist/PtyConsoleDrawer.d.ts.map +1 -0
  52. package/dist/PtyConsoleDrawer.js +77 -0
  53. package/dist/PtyConsoleDrawer.js.map +1 -0
  54. package/dist/PtyConsoleSidePanel.d.ts +8 -0
  55. package/dist/PtyConsoleSidePanel.d.ts.map +1 -0
  56. package/dist/PtyConsoleSidePanel.js +9 -0
  57. package/dist/PtyConsoleSidePanel.js.map +1 -0
  58. package/dist/PtyTerminalPane.d.ts +10 -0
  59. package/dist/PtyTerminalPane.d.ts.map +1 -0
  60. package/dist/PtyTerminalPane.js +147 -0
  61. package/dist/PtyTerminalPane.js.map +1 -0
  62. package/dist/TaskCardList.d.ts +76 -0
  63. package/dist/TaskCardList.d.ts.map +1 -0
  64. package/dist/TaskCardList.js +327 -0
  65. package/dist/TaskCardList.js.map +1 -0
  66. package/dist/TaskCoordinatorView.d.ts +20 -0
  67. package/dist/TaskCoordinatorView.d.ts.map +1 -0
  68. package/dist/TaskCoordinatorView.js +146 -0
  69. package/dist/TaskCoordinatorView.js.map +1 -0
  70. package/dist/__e2e__/dashboard-fixture.d.ts +9 -0
  71. package/dist/__e2e__/dashboard-fixture.d.ts.map +1 -0
  72. package/dist/__e2e__/dashboard-fixture.js +123 -0
  73. package/dist/__e2e__/dashboard-fixture.js.map +1 -0
  74. package/dist/api/coding-agents-auth-sanitize.d.ts +23 -0
  75. package/dist/api/coding-agents-auth-sanitize.d.ts.map +1 -0
  76. package/dist/api/coding-agents-auth-sanitize.js +22 -0
  77. package/dist/api/coding-agents-auth-sanitize.js.map +1 -0
  78. package/dist/api/coding-agents-preflight-normalize.d.ts +29 -0
  79. package/dist/api/coding-agents-preflight-normalize.d.ts.map +1 -0
  80. package/dist/api/coding-agents-preflight-normalize.js +20 -0
  81. package/dist/api/coding-agents-preflight-normalize.js.map +1 -0
  82. package/dist/coding-agent-settings-shared.d.ts +42 -0
  83. package/dist/coding-agent-settings-shared.d.ts.map +1 -0
  84. package/dist/coding-agent-settings-shared.js +121 -0
  85. package/dist/coding-agent-settings-shared.js.map +1 -0
  86. package/dist/components/OrchestratorSpatialView.d.ts +56 -0
  87. package/dist/components/OrchestratorSpatialView.d.ts.map +1 -0
  88. package/dist/components/OrchestratorSpatialView.js +501 -0
  89. package/dist/components/OrchestratorSpatialView.js.map +1 -0
  90. package/dist/components/TaskCoordinatorSpatialView.d.ts +59 -0
  91. package/dist/components/TaskCoordinatorSpatialView.d.ts.map +1 -0
  92. package/dist/components/TaskCoordinatorSpatialView.js +294 -0
  93. package/dist/components/TaskCoordinatorSpatialView.js.map +1 -0
  94. package/dist/index.d.ts +5 -0
  95. package/dist/index.d.ts.map +1 -0
  96. package/dist/index.js +286 -0
  97. package/dist/index.js.map +1 -0
  98. package/dist/orchestrator-capabilities.d.ts +3 -0
  99. package/dist/orchestrator-capabilities.d.ts.map +1 -0
  100. package/dist/orchestrator-capabilities.js +136 -0
  101. package/dist/orchestrator-capabilities.js.map +1 -0
  102. package/dist/orchestrator-command.d.ts +39 -0
  103. package/dist/orchestrator-command.d.ts.map +1 -0
  104. package/dist/orchestrator-command.js +52 -0
  105. package/dist/orchestrator-command.js.map +1 -0
  106. package/dist/orchestrator-diff.d.ts +19 -0
  107. package/dist/orchestrator-diff.d.ts.map +1 -0
  108. package/dist/orchestrator-diff.helpers.d.ts +18 -0
  109. package/dist/orchestrator-diff.helpers.d.ts.map +1 -0
  110. package/dist/orchestrator-diff.helpers.js +76 -0
  111. package/dist/orchestrator-diff.helpers.js.map +1 -0
  112. package/dist/orchestrator-diff.js +119 -0
  113. package/dist/orchestrator-diff.js.map +1 -0
  114. package/dist/orchestrator-markdown.d.ts +5 -0
  115. package/dist/orchestrator-markdown.d.ts.map +1 -0
  116. package/dist/orchestrator-markdown.helpers.d.ts +2 -0
  117. package/dist/orchestrator-markdown.helpers.d.ts.map +1 -0
  118. package/dist/orchestrator-markdown.helpers.js +21 -0
  119. package/dist/orchestrator-markdown.helpers.js.map +1 -0
  120. package/dist/orchestrator-markdown.js +199 -0
  121. package/dist/orchestrator-markdown.js.map +1 -0
  122. package/dist/orchestrator-params.d.ts +8 -0
  123. package/dist/orchestrator-params.d.ts.map +1 -0
  124. package/dist/orchestrator-params.js +27 -0
  125. package/dist/orchestrator-params.js.map +1 -0
  126. package/dist/orchestrator-plan.d.ts +5 -0
  127. package/dist/orchestrator-plan.d.ts.map +1 -0
  128. package/dist/orchestrator-plan.js +95 -0
  129. package/dist/orchestrator-plan.js.map +1 -0
  130. package/dist/orchestrator-reasoning.d.ts +21 -0
  131. package/dist/orchestrator-reasoning.d.ts.map +1 -0
  132. package/dist/orchestrator-reasoning.js +105 -0
  133. package/dist/orchestrator-reasoning.js.map +1 -0
  134. package/dist/orchestrator-stream.d.ts +15 -0
  135. package/dist/orchestrator-stream.d.ts.map +1 -0
  136. package/dist/orchestrator-stream.helpers.d.ts +89 -0
  137. package/dist/orchestrator-stream.helpers.d.ts.map +1 -0
  138. package/dist/orchestrator-stream.helpers.js +361 -0
  139. package/dist/orchestrator-stream.helpers.js.map +1 -0
  140. package/dist/orchestrator-stream.js +307 -0
  141. package/dist/orchestrator-stream.js.map +1 -0
  142. package/dist/pty-status-dots.d.ts +2 -0
  143. package/dist/pty-status-dots.d.ts.map +1 -0
  144. package/dist/pty-status-dots.js +6 -0
  145. package/dist/pty-status-dots.js.map +1 -0
  146. package/dist/register-slots.d.ts +20 -0
  147. package/dist/register-slots.d.ts.map +1 -0
  148. package/dist/register-slots.js +50 -0
  149. package/dist/register-slots.js.map +1 -0
  150. package/dist/register-terminal-view.d.ts +21 -0
  151. package/dist/register-terminal-view.d.ts.map +1 -0
  152. package/dist/register-terminal-view.js +46 -0
  153. package/dist/register-terminal-view.js.map +1 -0
  154. package/dist/register.d.ts +2 -0
  155. package/dist/register.d.ts.map +1 -0
  156. package/dist/register.js +23 -0
  157. package/dist/register.js.map +1 -0
  158. package/dist/session-hydration.d.ts +2 -0
  159. package/dist/session-hydration.d.ts.map +1 -0
  160. package/dist/session-hydration.js +9 -0
  161. package/dist/session-hydration.js.map +1 -0
  162. package/dist/task-coordinator-view-bundle.d.ts +4 -0
  163. package/dist/task-coordinator-view-bundle.d.ts.map +1 -0
  164. package/dist/task-coordinator-view-bundle.js +9 -0
  165. package/dist/task-coordinator-view-bundle.js.map +1 -0
  166. package/dist/ui.d.ts +2 -0
  167. package/dist/ui.d.ts.map +1 -0
  168. package/dist/ui.js +13 -0
  169. package/dist/ui.js.map +1 -0
  170. package/dist/view-format.d.ts +25 -0
  171. package/dist/view-format.d.ts.map +1 -0
  172. package/dist/view-format.js +64 -0
  173. package/dist/view-format.js.map +1 -0
  174. package/dist/views/bundle.js +1383 -0
  175. package/dist/views/bundle.js.map +1 -0
  176. package/package.json +7 -6
@@ -0,0 +1,3200 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import {
3
+ AlertDialog,
4
+ AlertDialogAction,
5
+ AlertDialogCancel,
6
+ AlertDialogContent,
7
+ AlertDialogDescription,
8
+ AlertDialogFooter,
9
+ AlertDialogHeader,
10
+ AlertDialogTitle,
11
+ AlertDialogTrigger,
12
+ Button,
13
+ client,
14
+ DiffReviewPanel,
15
+ useAppSelectorShallow
16
+ } from "@elizaos/ui";
17
+ import { useAgentElement } from "@elizaos/ui/agent-surface";
18
+ import {
19
+ Select,
20
+ SelectContent,
21
+ SelectItem,
22
+ SelectTrigger
23
+ } from "@elizaos/ui/components/ui/select";
24
+ import {
25
+ Archive,
26
+ ArrowDownToLine,
27
+ ArrowLeft,
28
+ Bot,
29
+ Check,
30
+ ChevronDown,
31
+ ChevronsUp,
32
+ ChevronUp,
33
+ Circle,
34
+ CircleAlert,
35
+ CircleCheck,
36
+ CircleDashed,
37
+ CirclePlay,
38
+ CircleStop,
39
+ CircleX,
40
+ Copy,
41
+ Gauge,
42
+ GitFork,
43
+ Layers,
44
+ OctagonX,
45
+ PanelRightOpen,
46
+ Pause,
47
+ Play,
48
+ RotateCcw,
49
+ Trash2,
50
+ UserPlus,
51
+ UserRound,
52
+ X
53
+ } from "lucide-react";
54
+ import {
55
+ useCallback,
56
+ useDeferredValue,
57
+ useEffect,
58
+ useMemo,
59
+ useRef,
60
+ useState
61
+ } from "react";
62
+ import {
63
+ paramPriority,
64
+ TASK_LIST_LIMIT
65
+ } from "./orchestrator-params.js";
66
+ import {
67
+ ConversationBlockView,
68
+ ToolBody
69
+ } from "./orchestrator-stream.js";
70
+ import { buildConversation } from "./orchestrator-stream.helpers";
71
+ import {
72
+ BackChip,
73
+ SparseWatermark,
74
+ TaskCard,
75
+ TaskEmptyState,
76
+ TaskMetaChip,
77
+ TaskSearchInput,
78
+ TaskStatusChip
79
+ } from "./TaskCardList.js";
80
+ import {
81
+ formatClockTime,
82
+ formatCompactNumber,
83
+ formatDuration,
84
+ formatIsoRelative,
85
+ formatRelativeTime,
86
+ formatUsd
87
+ } from "./view-format.js";
88
+ const fallbackTranslate = (key, vars) => String(vars?.defaultValue ?? key);
89
+ const TIMELINE_PAGE_LIMIT = 50;
90
+ const POLL_INTERVAL_MS = 5e3;
91
+ const ACTIVE_POLL_INTERVAL_MS = 1500;
92
+ const STATUS_ICON = {
93
+ open: Circle,
94
+ active: CirclePlay,
95
+ waiting_on_user: UserRound,
96
+ blocked: OctagonX,
97
+ validating: CircleDashed,
98
+ done: CircleCheck,
99
+ failed: CircleX,
100
+ archived: Archive,
101
+ interrupted: CircleAlert
102
+ };
103
+ const STATUS_TONE = {
104
+ open: "text-muted",
105
+ active: "text-ok",
106
+ waiting_on_user: "text-warn",
107
+ blocked: "text-warn",
108
+ validating: "text-accent",
109
+ done: "text-ok",
110
+ failed: "text-danger",
111
+ archived: "text-muted",
112
+ interrupted: "text-warn"
113
+ };
114
+ const STATUS_PULSE = /* @__PURE__ */ new Set([
115
+ "active",
116
+ "validating"
117
+ ]);
118
+ const TERMINAL_TASK_STATUSES = /* @__PURE__ */ new Set([
119
+ "done",
120
+ "failed",
121
+ "archived"
122
+ ]);
123
+ const PRIORITY_ICON = {
124
+ low: ChevronDown,
125
+ normal: null,
126
+ high: ChevronUp,
127
+ urgent: ChevronsUp
128
+ };
129
+ const SESSION_ICON = {
130
+ active: CirclePlay,
131
+ running: CirclePlay,
132
+ tool_running: CirclePlay,
133
+ blocked: OctagonX,
134
+ idle: Circle,
135
+ completed: CircleCheck,
136
+ stopped: CircleStop,
137
+ error: CircleX,
138
+ errored: CircleX
139
+ };
140
+ const SESSION_TONE = {
141
+ active: "text-ok",
142
+ running: "text-ok",
143
+ tool_running: "text-ok",
144
+ blocked: "text-warn",
145
+ idle: "text-muted",
146
+ completed: "text-ok",
147
+ stopped: "text-muted",
148
+ error: "text-danger",
149
+ errored: "text-danger"
150
+ };
151
+ const SESSION_PULSE = /* @__PURE__ */ new Set([
152
+ "active",
153
+ "running",
154
+ "tool_running"
155
+ ]);
156
+ const VERIFICATION_ICON = {
157
+ passed: CircleCheck,
158
+ failed: CircleX,
159
+ pending: CircleDashed,
160
+ unknown: Circle
161
+ };
162
+ const VERIFICATION_TONE = {
163
+ passed: "text-ok",
164
+ failed: "text-danger",
165
+ pending: "text-warn",
166
+ unknown: "text-muted"
167
+ };
168
+ const PLAN_STEP_ICON = {
169
+ done: CircleCheck,
170
+ completed: CircleCheck,
171
+ passed: CircleCheck,
172
+ in_progress: CircleDashed,
173
+ active: CircleDashed,
174
+ running: CircleDashed,
175
+ blocked: OctagonX,
176
+ failed: CircleX,
177
+ pending: Circle,
178
+ todo: Circle
179
+ };
180
+ const PLAN_STEP_TONE = {
181
+ done: "text-ok",
182
+ completed: "text-ok",
183
+ passed: "text-ok",
184
+ in_progress: "text-accent",
185
+ active: "text-accent",
186
+ running: "text-accent",
187
+ blocked: "text-warn",
188
+ failed: "text-danger",
189
+ pending: "text-muted",
190
+ todo: "text-muted"
191
+ };
192
+ const FILTER_OPTIONS = [
193
+ "all",
194
+ "active",
195
+ "blocked",
196
+ "validating",
197
+ "waiting_on_user",
198
+ "interrupted",
199
+ "open",
200
+ "done",
201
+ "failed"
202
+ ];
203
+ const STATUS_LABEL_KEY = {
204
+ open: "orchestrator.status.open",
205
+ active: "orchestrator.status.active",
206
+ waiting_on_user: "orchestrator.status.waitingOnUser",
207
+ blocked: "orchestrator.status.blocked",
208
+ validating: "orchestrator.status.validating",
209
+ done: "orchestrator.status.done",
210
+ failed: "orchestrator.status.failed",
211
+ archived: "orchestrator.status.archived",
212
+ interrupted: "orchestrator.status.interrupted"
213
+ };
214
+ function labelStatus(status, t) {
215
+ return t(STATUS_LABEL_KEY[status], {
216
+ defaultValue: status.replace(/_/g, " ")
217
+ });
218
+ }
219
+ function labelPriority(priority, t) {
220
+ return t(`orchestrator.priority.${priority}`, { defaultValue: priority });
221
+ }
222
+ function StatusGlyph({
223
+ status,
224
+ paused,
225
+ t,
226
+ size = "h-3.5 w-3.5"
227
+ }) {
228
+ const Icon = STATUS_ICON[status];
229
+ const label = labelStatus(status, t);
230
+ const pulse = STATUS_PULSE.has(status) && !paused ? " animate-pulse" : "";
231
+ return /* @__PURE__ */ jsx(
232
+ "span",
233
+ {
234
+ className: "inline-flex shrink-0",
235
+ title: label,
236
+ "aria-label": label,
237
+ role: "img",
238
+ children: /* @__PURE__ */ jsx(Icon, { className: `${size} ${STATUS_TONE[status]}${pulse}`, "aria-hidden": true })
239
+ }
240
+ );
241
+ }
242
+ function SessionGlyph({
243
+ status,
244
+ t,
245
+ size = "h-3.5 w-3.5"
246
+ }) {
247
+ const Icon = SESSION_ICON[status] ?? Circle;
248
+ const tone = SESSION_TONE[status] ?? "text-muted";
249
+ const label = labelSessionStatus(status, t);
250
+ const pulse = SESSION_PULSE.has(status) ? " animate-pulse" : "";
251
+ return /* @__PURE__ */ jsx(
252
+ "span",
253
+ {
254
+ className: "inline-flex shrink-0",
255
+ title: label,
256
+ "aria-label": label,
257
+ role: "img",
258
+ children: /* @__PURE__ */ jsx(Icon, { className: `${size} ${tone}${pulse}`, "aria-hidden": true })
259
+ }
260
+ );
261
+ }
262
+ function VerificationGlyph({
263
+ status,
264
+ t
265
+ }) {
266
+ const Icon = VERIFICATION_ICON[status];
267
+ const label = t(`orchestrator.verification.${status}`, {
268
+ defaultValue: status
269
+ });
270
+ return /* @__PURE__ */ jsx(
271
+ "span",
272
+ {
273
+ className: "inline-flex shrink-0",
274
+ title: label,
275
+ "aria-label": label,
276
+ role: "img",
277
+ children: /* @__PURE__ */ jsx(
278
+ Icon,
279
+ {
280
+ className: `h-3.5 w-3.5 ${VERIFICATION_TONE[status]}`,
281
+ "aria-hidden": true
282
+ }
283
+ )
284
+ }
285
+ );
286
+ }
287
+ function PlanStepGlyph({ status, t }) {
288
+ const key = status.trim().toLowerCase().replace(/[\s-]+/g, "_");
289
+ const Icon = PLAN_STEP_ICON[key] ?? Circle;
290
+ const tone = PLAN_STEP_TONE[key] ?? "text-muted";
291
+ const label = t(`orchestrator.planStatus.${key}`, {
292
+ defaultValue: status.replace(/_/g, " ")
293
+ });
294
+ return /* @__PURE__ */ jsx(
295
+ "span",
296
+ {
297
+ className: "mt-px inline-flex shrink-0",
298
+ title: label,
299
+ "aria-label": label,
300
+ role: "img",
301
+ children: /* @__PURE__ */ jsx(Icon, { className: `h-3.5 w-3.5 ${tone}`, "aria-hidden": true })
302
+ }
303
+ );
304
+ }
305
+ const SENDER_LABEL_KEY = {
306
+ user: { key: "orchestrator.sender.user", fallback: "You" },
307
+ orchestrator: {
308
+ key: "orchestrator.sender.orchestrator",
309
+ fallback: "Orchestrator"
310
+ },
311
+ sub_agent: { key: "orchestrator.sender.subAgent", fallback: "Sub-agent" },
312
+ system: { key: "orchestrator.sender.system", fallback: "System" }
313
+ };
314
+ function labelSender(kind, t) {
315
+ const meta = SENDER_LABEL_KEY[kind];
316
+ return t(meta.key, { defaultValue: meta.fallback });
317
+ }
318
+ function resolveSenderName(message, sessionLabelById, mainAgentName, t) {
319
+ if (message.senderKind === "sub_agent") {
320
+ const label = message.sessionId ? sessionLabelById.get(message.sessionId)?.trim() : void 0;
321
+ return label || labelSender("sub_agent", t);
322
+ }
323
+ if (message.senderKind === "orchestrator") {
324
+ return mainAgentName?.trim() || labelSender("orchestrator", t);
325
+ }
326
+ return labelSender(message.senderKind, t);
327
+ }
328
+ function getClientErrorMessage(error, fallback) {
329
+ return error instanceof Error && error.message ? error.message : fallback;
330
+ }
331
+ function isOrchestratorBackendAbsent(error) {
332
+ const status = error?.status;
333
+ if (status === 404) return true;
334
+ const msg = error instanceof Error ? error.message.toLowerCase() : "";
335
+ return msg === "not found" || msg.includes("404");
336
+ }
337
+ function mergeById(previous, incoming) {
338
+ if (incoming.length === 0) return previous;
339
+ const byId = /* @__PURE__ */ new Map();
340
+ for (const item of previous) byId.set(item.id, item);
341
+ for (const item of incoming) byId.set(item.id, item);
342
+ return [...byId.values()].sort((a, b) => a.timestamp - b.timestamp);
343
+ }
344
+ function splitTimelineItems(items) {
345
+ const messages = [];
346
+ const events = [];
347
+ for (const item of items) {
348
+ if (item.kind === "message") messages.push(item.message);
349
+ else events.push(item.event);
350
+ }
351
+ return { messages, events };
352
+ }
353
+ function normalizePlan(plan) {
354
+ if (!plan) return null;
355
+ const summary = typeof plan.summary === "string" ? plan.summary : null;
356
+ const rawSteps = Array.isArray(plan.steps) ? plan.steps : [];
357
+ const steps = [];
358
+ for (const raw of rawSteps) {
359
+ if (typeof raw === "string" && raw.trim()) {
360
+ steps.push({
361
+ key: `step-${steps.length}`,
362
+ label: raw.trim(),
363
+ status: null
364
+ });
365
+ continue;
366
+ }
367
+ if (raw && typeof raw === "object") {
368
+ const obj = raw;
369
+ const label = typeof obj.title === "string" && obj.title || typeof obj.label === "string" && obj.label || typeof obj.description === "string" && obj.description || null;
370
+ if (!label) continue;
371
+ steps.push({
372
+ key: `step-${steps.length}`,
373
+ label,
374
+ status: typeof obj.status === "string" ? obj.status : null
375
+ });
376
+ }
377
+ }
378
+ if (!summary && steps.length === 0) return null;
379
+ return { summary, steps };
380
+ }
381
+ function formatTokenCount(state, totalTokens, t, locale) {
382
+ if (state === "unavailable") {
383
+ return t("orchestrator.usage.unavailable", { defaultValue: "\u2014" });
384
+ }
385
+ const value = formatCompactNumber(totalTokens, locale);
386
+ return state === "estimated" ? t("orchestrator.usage.estimatedTokens", {
387
+ defaultValue: "~{{value}}",
388
+ value
389
+ }) : value;
390
+ }
391
+ function renderTokens(usage, t, locale) {
392
+ return formatTokenCount(usage.state, usage.totalTokens, t, locale);
393
+ }
394
+ function renderCost(usage, t, locale) {
395
+ if (usage.state === "unavailable") {
396
+ return t("orchestrator.usage.unavailable", { defaultValue: "\u2014" });
397
+ }
398
+ const value = formatUsd(usage.costUsd, locale);
399
+ return usage.state === "estimated" ? t("orchestrator.usage.estimatedCost", {
400
+ defaultValue: "~{{value}}",
401
+ value
402
+ }) : value;
403
+ }
404
+ function HeaderStat({
405
+ value,
406
+ label,
407
+ toneClass = "text-txt-strong"
408
+ }) {
409
+ return /* @__PURE__ */ jsxs("span", { className: "inline-flex shrink-0 items-baseline gap-1", title: label, children: [
410
+ /* @__PURE__ */ jsx("span", { className: `text-sm font-semibold tabular-nums ${toneClass}`, children: value }),
411
+ /* @__PURE__ */ jsx("span", { className: "text-2xs text-muted", children: label })
412
+ ] });
413
+ }
414
+ function InspectorSection({
415
+ title,
416
+ action,
417
+ children
418
+ }) {
419
+ return /* @__PURE__ */ jsxs("section", { className: "space-y-1.5", children: [
420
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
421
+ /* @__PURE__ */ jsx("h3", { className: "text-xs font-medium text-muted", children: title }),
422
+ action
423
+ ] }),
424
+ children
425
+ ] });
426
+ }
427
+ function WorkbenchHeader({
428
+ status,
429
+ busy,
430
+ isMobile,
431
+ onPauseAll,
432
+ onResumeAll,
433
+ t,
434
+ locale
435
+ }) {
436
+ const title = /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
437
+ /* @__PURE__ */ jsx(Layers, { className: "h-4 w-4 text-accent" }),
438
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-txt-strong", children: t("orchestrator.title", { defaultValue: "Orchestrator" }) })
439
+ ] });
440
+ const summary = /* @__PURE__ */ jsxs(
441
+ "div",
442
+ {
443
+ className: "flex min-w-0 items-center gap-4 overflow-x-auto",
444
+ style: isMobile ? void 0 : { flex: "1 1 0%" },
445
+ children: [
446
+ /* @__PURE__ */ jsx(HeaderStat, { value: String(status?.taskCount ?? 0), label: "tasks" }),
447
+ status?.activeTaskCount ? /* @__PURE__ */ jsx(
448
+ HeaderStat,
449
+ {
450
+ value: String(status.activeTaskCount),
451
+ label: "active",
452
+ toneClass: "text-ok"
453
+ }
454
+ ) : null,
455
+ status?.blockedTaskCount ? /* @__PURE__ */ jsx(
456
+ HeaderStat,
457
+ {
458
+ value: String(status.blockedTaskCount),
459
+ label: "blocked",
460
+ toneClass: "text-warn"
461
+ }
462
+ ) : null,
463
+ status?.validatingTaskCount ? /* @__PURE__ */ jsx(
464
+ HeaderStat,
465
+ {
466
+ value: String(status.validatingTaskCount),
467
+ label: "validating",
468
+ toneClass: "text-accent"
469
+ }
470
+ ) : null,
471
+ status?.activeSessionCount ? /* @__PURE__ */ jsx(
472
+ HeaderStat,
473
+ {
474
+ value: `${status.activeSessionCount}/${status.sessionCount}`,
475
+ label: "agents"
476
+ }
477
+ ) : null
478
+ ]
479
+ }
480
+ );
481
+ const usageReadout = status && status.usage.state !== "unavailable" ? /* @__PURE__ */ jsxs(
482
+ "span",
483
+ {
484
+ className: "flex shrink-0 items-center gap-1.5 text-xs tabular-nums text-muted",
485
+ title: t("orchestrator.stat.usage", { defaultValue: "Usage" }),
486
+ children: [
487
+ /* @__PURE__ */ jsx(Gauge, { className: "h-3 w-3 text-muted/70" }),
488
+ renderTokens(status.usage, t, locale),
489
+ /* @__PURE__ */ jsx("span", { className: "text-muted/50", children: "\xB7" }),
490
+ renderCost(status.usage, t, locale)
491
+ ]
492
+ }
493
+ ) : null;
494
+ const pauseAllLabel = t("orchestrator.action.pauseAll", {
495
+ defaultValue: "Pause all"
496
+ });
497
+ const resumeAllLabel = t("orchestrator.action.resumeAll", {
498
+ defaultValue: "Resume all"
499
+ });
500
+ const { ref: pauseAllRef, agentProps: pauseAllAgentProps } = useAgentElement({
501
+ id: "header-pause-all",
502
+ role: "button",
503
+ label: pauseAllLabel,
504
+ group: "orchestrator-header",
505
+ description: "Pause every active orchestrator task"
506
+ });
507
+ const { ref: resumeAllRef, agentProps: resumeAllAgentProps } = useAgentElement({
508
+ id: "header-resume-all",
509
+ role: "button",
510
+ label: resumeAllLabel,
511
+ group: "orchestrator-header",
512
+ description: "Resume every paused orchestrator task"
513
+ });
514
+ const actions = status?.activeTaskCount || status?.pausedTaskCount ? /* @__PURE__ */ jsxs("div", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: [
515
+ status?.activeTaskCount ? /* @__PURE__ */ jsx(
516
+ Button,
517
+ {
518
+ ref: pauseAllRef,
519
+ variant: "ghost",
520
+ size: "sm",
521
+ disabled: busy,
522
+ onClick: onPauseAll,
523
+ className: "h-7 w-7 p-0",
524
+ "aria-label": pauseAllLabel,
525
+ title: pauseAllLabel,
526
+ "data-testid": "orchestrator-pause-all",
527
+ ...pauseAllAgentProps,
528
+ children: /* @__PURE__ */ jsx(Pause, { className: "h-3.5 w-3.5" })
529
+ }
530
+ ) : null,
531
+ status?.pausedTaskCount ? /* @__PURE__ */ jsx(
532
+ Button,
533
+ {
534
+ ref: resumeAllRef,
535
+ variant: "ghost",
536
+ size: "sm",
537
+ disabled: busy,
538
+ onClick: onResumeAll,
539
+ className: "h-7 w-7 p-0",
540
+ "aria-label": resumeAllLabel,
541
+ title: resumeAllLabel,
542
+ "data-testid": "orchestrator-resume-all",
543
+ ...resumeAllAgentProps,
544
+ children: /* @__PURE__ */ jsx(Play, { className: "h-3.5 w-3.5" })
545
+ }
546
+ ) : null
547
+ ] }) : null;
548
+ if (isMobile) {
549
+ return /* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-2 bg-bg px-4 py-2.5", children: [
550
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
551
+ title,
552
+ actions
553
+ ] }),
554
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
555
+ summary,
556
+ usageReadout
557
+ ] })
558
+ ] });
559
+ }
560
+ return /* @__PURE__ */ jsxs("header", { className: "flex items-center gap-4 bg-bg px-4 py-2.5", children: [
561
+ title,
562
+ summary,
563
+ usageReadout,
564
+ actions
565
+ ] });
566
+ }
567
+ function FilterSelect({
568
+ status,
569
+ active,
570
+ onSelect,
571
+ t
572
+ }) {
573
+ const countFor = (filter) => {
574
+ if (!status) return 0;
575
+ if (filter === "all") return status.taskCount;
576
+ return status.byStatus[filter] ?? 0;
577
+ };
578
+ const filterLabel = t("orchestrator.filter.label", {
579
+ defaultValue: "Filter by status"
580
+ });
581
+ const { ref, agentProps } = useAgentElement({
582
+ id: "rail-filter-status",
583
+ role: "select",
584
+ label: filterLabel,
585
+ group: "orchestrator-rail",
586
+ description: "Filter the task list by status",
587
+ options: FILTER_OPTIONS,
588
+ getValue: () => active,
589
+ onFill: (value) => {
590
+ if (FILTER_OPTIONS.includes(value)) {
591
+ onSelect(value);
592
+ }
593
+ }
594
+ });
595
+ const labelFor = (filter) => filter === "all" ? t("orchestrator.filter.all", { defaultValue: "All" }) : labelStatus(filter, t);
596
+ return /* @__PURE__ */ jsxs(
597
+ Select,
598
+ {
599
+ value: active,
600
+ onValueChange: (value) => onSelect(value),
601
+ children: [
602
+ /* @__PURE__ */ jsx(
603
+ SelectTrigger,
604
+ {
605
+ ref,
606
+ "aria-label": filterLabel,
607
+ "data-testid": "orchestrator-filter",
608
+ className: "h-9 border-0 bg-transparent px-1 text-xs",
609
+ ...agentProps,
610
+ children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
611
+ active !== "all" ? /* @__PURE__ */ jsx(TaskStatusChip, { status: active, t }) : /* @__PURE__ */ jsx("span", { className: "text-txt", children: labelFor("all") }),
612
+ /* @__PURE__ */ jsxs("span", { className: "text-muted tabular-nums", children: [
613
+ "(",
614
+ countFor(active),
615
+ ")"
616
+ ] })
617
+ ] })
618
+ }
619
+ ),
620
+ /* @__PURE__ */ jsx(SelectContent, { children: FILTER_OPTIONS.map((filter) => /* @__PURE__ */ jsx(SelectItem, { value: filter, className: "text-xs", children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
621
+ filter === "all" ? /* @__PURE__ */ jsx("span", { children: labelFor("all") }) : /* @__PURE__ */ jsx(TaskStatusChip, { status: filter, t }),
622
+ /* @__PURE__ */ jsxs("span", { className: "text-muted tabular-nums", children: [
623
+ "(",
624
+ countFor(filter),
625
+ ")"
626
+ ] })
627
+ ] }) }, filter)) })
628
+ ]
629
+ }
630
+ );
631
+ }
632
+ function orchestratorTaskChips(thread, t, locale) {
633
+ const lastActivity = thread.latestActivityAt != null ? formatRelativeTime(thread.latestActivityAt, locale) : formatIsoRelative(
634
+ thread.updatedAt,
635
+ locale,
636
+ t("orchestrator.unknown", { defaultValue: "\u2014" })
637
+ );
638
+ const PriorityIcon = PRIORITY_ICON[thread.priority];
639
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
640
+ thread.sessionCount > 0 ? /* @__PURE__ */ jsx(
641
+ TaskMetaChip,
642
+ {
643
+ icon: /* @__PURE__ */ jsx(Bot, { className: "h-3 w-3" }),
644
+ tone: thread.activeSessionCount > 0 ? "accent" : "muted",
645
+ children: t("orchestrator.chip.agents", {
646
+ defaultValue: "{{active}}/{{total}} agents",
647
+ active: thread.activeSessionCount,
648
+ total: thread.sessionCount
649
+ })
650
+ }
651
+ ) : null,
652
+ thread.paused ? /* @__PURE__ */ jsx(TaskMetaChip, { icon: /* @__PURE__ */ jsx(Pause, { className: "h-3 w-3" }), children: t("orchestrator.status.paused", { defaultValue: "Paused" }) }) : null,
653
+ PriorityIcon && thread.priority !== "normal" ? /* @__PURE__ */ jsx(TaskMetaChip, { icon: /* @__PURE__ */ jsx(PriorityIcon, { className: "h-3 w-3" }), children: labelPriority(thread.priority, t) }) : null,
654
+ /* @__PURE__ */ jsx("span", { className: "text-2xs text-muted/80", children: lastActivity })
655
+ ] });
656
+ }
657
+ function SubAgentCard({
658
+ session,
659
+ busy,
660
+ onInspect,
661
+ onStop,
662
+ t,
663
+ locale
664
+ }) {
665
+ const stoppable = session.stoppedAt == null && session.status !== "completed";
666
+ const provider = [session.framework, session.model].filter((part) => Boolean(part)).join(" \xB7 ");
667
+ const workspace = session.repo || session.workdir || t("orchestrator.noWorkspace", { defaultValue: "None" });
668
+ const stopLabel = t("orchestrator.action.stopAgent", {
669
+ defaultValue: "Stop agent"
670
+ });
671
+ const inspectLabel = t("orchestrator.action.inspectAgent", {
672
+ defaultValue: "Inspect agent"
673
+ });
674
+ const { ref: inspectRef, agentProps: inspectAgentProps } = useAgentElement({
675
+ id: `sub-agent-inspect-${session.sessionId}`,
676
+ role: "button",
677
+ label: `${inspectLabel}: ${session.label}`,
678
+ group: "orchestrator-sub-agents",
679
+ description: `Open recovery and event details for the "${session.label}" sub-agent`
680
+ });
681
+ const { ref: stopRef, agentProps: stopAgentProps } = useAgentElement({
682
+ id: `sub-agent-stop-${session.sessionId}`,
683
+ role: "button",
684
+ label: `${stopLabel}: ${session.label}`,
685
+ group: "orchestrator-sub-agents",
686
+ description: `Stop the "${session.label}" sub-agent`
687
+ });
688
+ return /* @__PURE__ */ jsxs("div", { className: "py-1", children: [
689
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
690
+ /* @__PURE__ */ jsx(SessionGlyph, { status: session.status, t }),
691
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-xs font-medium text-txt", children: session.label }),
692
+ /* @__PURE__ */ jsx(
693
+ "button",
694
+ {
695
+ ref: inspectRef,
696
+ type: "button",
697
+ onClick: () => onInspect(session.sessionId),
698
+ className: "flex items-center gap-0.5 px-1 py-0.5 text-2xs text-muted transition-colors hover:text-txt",
699
+ "data-testid": "orchestrator-inspect-session",
700
+ "aria-label": inspectLabel,
701
+ title: inspectLabel,
702
+ ...inspectAgentProps,
703
+ children: /* @__PURE__ */ jsx(PanelRightOpen, { className: "h-3 w-3" })
704
+ }
705
+ ),
706
+ stoppable ? /* @__PURE__ */ jsx(
707
+ "button",
708
+ {
709
+ ref: stopRef,
710
+ type: "button",
711
+ disabled: busy,
712
+ onClick: () => onStop(session.sessionId),
713
+ className: "flex items-center gap-0.5 px-1 py-0.5 text-2xs text-muted transition-colors hover:text-danger disabled:opacity-50",
714
+ "data-testid": "orchestrator-stop-agent",
715
+ "aria-label": stopLabel,
716
+ ...stopAgentProps,
717
+ children: /* @__PURE__ */ jsx(CircleStop, { className: "h-3 w-3" })
718
+ }
719
+ ) : null
720
+ ] }),
721
+ provider ? /* @__PURE__ */ jsx("div", { className: "mt-0.5 truncate text-2xs text-muted", children: provider }) : null,
722
+ /* @__PURE__ */ jsxs("div", { className: "mt-0.5 flex items-center gap-2 text-2xs text-muted", children: [
723
+ session.activeTool ? /* @__PURE__ */ jsx("span", { className: "truncate text-warn", children: session.activeTool }) : null,
724
+ /* @__PURE__ */ jsx("span", { className: "ml-auto tabular-nums", children: formatTokenCount(session.usageState, session.totalTokens, t, locale) })
725
+ ] }),
726
+ /* @__PURE__ */ jsx("div", { className: "mt-0.5 truncate text-2xs text-muted/80", children: workspace })
727
+ ] });
728
+ }
729
+ function labelSessionStatus(status, t) {
730
+ return t(`orchestrator.sessionStatus.${status}`, {
731
+ defaultValue: status.replace(/_/g, " ")
732
+ });
733
+ }
734
+ function PlanSection({ plan, t }) {
735
+ return /* @__PURE__ */ jsxs(InspectorSection, { title: t("orchestrator.plan", { defaultValue: "Plan" }), children: [
736
+ plan.summary ? /* @__PURE__ */ jsx("p", { className: "mb-2 text-xs-tight text-txt", children: plan.summary }) : null,
737
+ plan.steps.length > 0 ? /* @__PURE__ */ jsx("ol", { className: "space-y-1", children: plan.steps.map((step, index) => /* @__PURE__ */ jsxs(
738
+ "li",
739
+ {
740
+ className: "flex items-start gap-1.5 text-xs-tight text-txt",
741
+ children: [
742
+ /* @__PURE__ */ jsxs("span", { className: "mt-px shrink-0 tabular-nums text-muted", children: [
743
+ index + 1,
744
+ "."
745
+ ] }),
746
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1", children: step.label }),
747
+ step.status ? /* @__PURE__ */ jsx(PlanStepGlyph, { status: step.status, t }) : null
748
+ ]
749
+ },
750
+ step.key
751
+ )) }) : null
752
+ ] });
753
+ }
754
+ function EditedPlanRestartSection({
755
+ plan,
756
+ latestPlanRevisionId,
757
+ busy,
758
+ onSubmit,
759
+ t
760
+ }) {
761
+ const planSource = useMemo(() => JSON.stringify(plan, null, 2), [plan]);
762
+ const [open, setOpen] = useState(false);
763
+ const [draft, setDraft] = useState(planSource);
764
+ const [summary, setSummary] = useState("");
765
+ const [error, setError] = useState(null);
766
+ const toggleLabel = t("orchestrator.action.editPlan", {
767
+ defaultValue: "Edit plan"
768
+ });
769
+ const restartLabel = t("orchestrator.action.restartWithPlan", {
770
+ defaultValue: "Restart with plan"
771
+ });
772
+ const summaryLabel = t("orchestrator.planEdit.summary", {
773
+ defaultValue: "Edit summary"
774
+ });
775
+ const draftLabel = t("orchestrator.planEdit.draft", {
776
+ defaultValue: "Plan JSON"
777
+ });
778
+ const baseLabel = t("orchestrator.planEdit.base", {
779
+ defaultValue: "Base revision"
780
+ });
781
+ const currentPlanLabel = t("orchestrator.planEdit.currentPlan", {
782
+ defaultValue: "Current plan"
783
+ });
784
+ const { ref: toggleRef, agentProps: toggleAgentProps } = useAgentElement({
785
+ id: "inspector-plan-edit-toggle",
786
+ role: "button",
787
+ label: toggleLabel,
788
+ group: "orchestrator-inspector",
789
+ description: "Open the plan JSON editor"
790
+ });
791
+ const { ref: restartRef, agentProps: restartAgentProps } = useAgentElement({
792
+ id: "inspector-restart-edited-plan",
793
+ role: "button",
794
+ label: restartLabel,
795
+ group: "orchestrator-inspector",
796
+ description: "Restart this task with the edited plan"
797
+ });
798
+ useEffect(() => {
799
+ setDraft(planSource);
800
+ setSummary("");
801
+ setError(null);
802
+ }, [planSource]);
803
+ const submit = () => {
804
+ let parsed;
805
+ try {
806
+ parsed = JSON.parse(draft);
807
+ } catch {
808
+ setError(
809
+ t("orchestrator.planEdit.invalidJson", {
810
+ defaultValue: "Plan must be valid JSON."
811
+ })
812
+ );
813
+ return;
814
+ }
815
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
816
+ setError(
817
+ t("orchestrator.planEdit.invalidObject", {
818
+ defaultValue: "Plan must be a JSON object."
819
+ })
820
+ );
821
+ return;
822
+ }
823
+ const confirmed = typeof window === "undefined" || window.confirm(
824
+ t("orchestrator.confirmRestartWithPlan", {
825
+ defaultValue: "Restart this task with the edited plan? Active agents will be stopped first."
826
+ })
827
+ );
828
+ if (!confirmed) return;
829
+ setError(null);
830
+ onSubmit({
831
+ plan: parsed,
832
+ basePlanRevisionId: latestPlanRevisionId,
833
+ editSummary: summary.trim() || void 0,
834
+ stopActive: true
835
+ });
836
+ };
837
+ return /* @__PURE__ */ jsxs(
838
+ InspectorSection,
839
+ {
840
+ title: t("orchestrator.planEdit.title", {
841
+ defaultValue: "Plan editor"
842
+ }),
843
+ children: [
844
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
845
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 text-2xs text-muted", children: [
846
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-muted-strong", children: baseLabel }),
847
+ /* @__PURE__ */ jsx("span", { className: "ml-1 truncate", children: latestPlanRevisionId ?? currentPlanLabel })
848
+ ] }),
849
+ /* @__PURE__ */ jsxs(
850
+ "button",
851
+ {
852
+ ref: toggleRef,
853
+ type: "button",
854
+ disabled: busy,
855
+ onClick: () => setOpen((prev) => !prev),
856
+ className: "inline-flex h-7 shrink-0 items-center gap-1.5 px-1 text-2xs font-semibold text-muted transition-colors hover:text-txt disabled:opacity-50",
857
+ "data-testid": "orchestrator-plan-edit-toggle",
858
+ ...toggleAgentProps,
859
+ children: [
860
+ open ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3" }),
861
+ toggleLabel
862
+ ]
863
+ }
864
+ )
865
+ ] }),
866
+ open ? /* @__PURE__ */ jsxs("div", { className: "mt-2 space-y-2", children: [
867
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
868
+ /* @__PURE__ */ jsx(FieldLabel, { children: summaryLabel }),
869
+ /* @__PURE__ */ jsx(
870
+ "input",
871
+ {
872
+ value: summary,
873
+ onChange: (event) => setSummary(event.target.value),
874
+ className: FIELD_CLASS,
875
+ placeholder: t("orchestrator.planEdit.summaryPlaceholder", {
876
+ defaultValue: "What changed"
877
+ }),
878
+ "data-testid": "orchestrator-plan-edit-summary"
879
+ }
880
+ )
881
+ ] }),
882
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
883
+ /* @__PURE__ */ jsx(FieldLabel, { children: draftLabel }),
884
+ /* @__PURE__ */ jsx(
885
+ "textarea",
886
+ {
887
+ value: draft,
888
+ onChange: (event) => setDraft(event.target.value),
889
+ rows: 8,
890
+ className: `${FIELD_CLASS} resize-y font-mono leading-relaxed`,
891
+ spellCheck: false,
892
+ "data-testid": "orchestrator-plan-draft"
893
+ }
894
+ )
895
+ ] }),
896
+ error ? /* @__PURE__ */ jsx("p", { className: "text-2xs text-danger", children: error }) : null,
897
+ /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs(
898
+ Button,
899
+ {
900
+ ref: restartRef,
901
+ type: "button",
902
+ size: "sm",
903
+ disabled: busy,
904
+ onClick: submit,
905
+ className: "h-7 gap-1.5 px-2.5 text-xs-tight",
906
+ "data-testid": "orchestrator-plan-restart",
907
+ ...restartAgentProps,
908
+ children: [
909
+ /* @__PURE__ */ jsx(RotateCcw, { className: "h-3 w-3" }),
910
+ restartLabel
911
+ ]
912
+ }
913
+ ) })
914
+ ] }) : null
915
+ ]
916
+ }
917
+ );
918
+ }
919
+ function AcceptanceSection({
920
+ criteria,
921
+ t
922
+ }) {
923
+ return /* @__PURE__ */ jsx(
924
+ InspectorSection,
925
+ {
926
+ title: t("orchestrator.acceptance", { defaultValue: "Acceptance" }),
927
+ children: /* @__PURE__ */ jsx("ul", { className: "space-y-1", children: criteria.map((criterion, index) => /* @__PURE__ */ jsxs(
928
+ "li",
929
+ {
930
+ className: "flex items-start gap-1.5 text-xs-tight text-txt",
931
+ children: [
932
+ /* @__PURE__ */ jsx("span", { className: "mt-1 inline-block h-1.5 w-1.5 shrink-0 rounded-full bg-accent/60" }),
933
+ /* @__PURE__ */ jsx("span", { children: criterion })
934
+ ]
935
+ },
936
+ `${criterion}-${index}`
937
+ )) })
938
+ }
939
+ );
940
+ }
941
+ function ArtifactSection({
942
+ artifacts,
943
+ t
944
+ }) {
945
+ return /* @__PURE__ */ jsx(
946
+ InspectorSection,
947
+ {
948
+ title: t("orchestrator.artifacts", { defaultValue: "Artifacts" }),
949
+ children: /* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: artifacts.map((artifact) => /* @__PURE__ */ jsxs("div", { className: "text-xs-tight", children: [
950
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
951
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate font-medium text-txt", children: artifact.title }),
952
+ /* @__PURE__ */ jsx(VerificationGlyph, { status: artifact.verificationStatus, t })
953
+ ] }),
954
+ /* @__PURE__ */ jsxs("div", { className: "truncate text-muted", children: [
955
+ artifact.artifactType,
956
+ artifact.path || artifact.uri ? ` \xB7 ${artifact.path ?? artifact.uri}` : ""
957
+ ] })
958
+ ] }, artifact.id)) })
959
+ }
960
+ );
961
+ }
962
+ function UsageSection({
963
+ usage,
964
+ t,
965
+ locale
966
+ }) {
967
+ return /* @__PURE__ */ jsxs(
968
+ InspectorSection,
969
+ {
970
+ title: t("orchestrator.usage.title", { defaultValue: "Tokens & cost" }),
971
+ children: [
972
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-3 text-xs", children: [
973
+ /* @__PURE__ */ jsxs("span", { className: "text-txt", children: [
974
+ /* @__PURE__ */ jsx("span", { className: "font-semibold tabular-nums", children: renderTokens(usage, t, locale) }),
975
+ " ",
976
+ /* @__PURE__ */ jsx("span", { className: "text-muted", children: t("orchestrator.usage.tokens", { defaultValue: "tokens" }) })
977
+ ] }),
978
+ /* @__PURE__ */ jsx("span", { className: "text-txt", children: /* @__PURE__ */ jsx("span", { className: "font-semibold tabular-nums", children: renderCost(usage, t, locale) }) })
979
+ ] }),
980
+ usage.byProvider.length > 1 ? /* @__PURE__ */ jsx("div", { className: "space-y-1", children: usage.byProvider.map((entry) => /* @__PURE__ */ jsxs(
981
+ "div",
982
+ {
983
+ className: "flex items-center gap-2 text-2xs text-muted",
984
+ children: [
985
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1 truncate", children: [
986
+ entry.provider,
987
+ entry.model ? ` \xB7 ${entry.model}` : ""
988
+ ] }),
989
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 tabular-nums", children: formatTokenCount(entry.state, entry.totalTokens, t, locale) }),
990
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 tabular-nums", children: entry.state === "unavailable" ? t("orchestrator.usage.unavailable", { defaultValue: "\u2014" }) : formatUsd(entry.costUsd, locale) })
991
+ ]
992
+ },
993
+ `${entry.provider}-${entry.model ?? "default"}`
994
+ )) }) : null
995
+ ]
996
+ }
997
+ );
998
+ }
999
+ const FIELD_CLASS = "w-full border-border/35 border-b bg-transparent px-1 py-1.5 text-xs text-txt outline-none transition-colors placeholder:text-muted focus:border-accent/60";
1000
+ function FieldLabel({ children }) {
1001
+ return /* @__PURE__ */ jsx("span", { className: "mb-1 block text-xs font-medium text-muted", children });
1002
+ }
1003
+ function AddAgentForm({
1004
+ busy,
1005
+ onClose,
1006
+ onSubmit,
1007
+ t
1008
+ }) {
1009
+ const [label, setLabel] = useState("");
1010
+ const [framework, setFramework] = useState("");
1011
+ const [model, setModel] = useState("");
1012
+ const [workdir, setWorkdir] = useState("");
1013
+ const [repo, setRepo] = useState("");
1014
+ const [task, setTask] = useState("");
1015
+ const fieldLabels = {
1016
+ label: t("orchestrator.addAgent.label", {
1017
+ defaultValue: "Label (optional)"
1018
+ }),
1019
+ framework: t("orchestrator.addAgent.framework", {
1020
+ defaultValue: "Framework"
1021
+ }),
1022
+ model: t("orchestrator.addAgent.model", { defaultValue: "Model" }),
1023
+ workdir: t("orchestrator.addAgent.workdir", {
1024
+ defaultValue: "Workdir (optional)"
1025
+ }),
1026
+ repo: t("orchestrator.addAgent.repo", {
1027
+ defaultValue: "Repo URL (optional)"
1028
+ }),
1029
+ task: t("orchestrator.addAgent.task", {
1030
+ defaultValue: "Sub-task for this agent (optional)"
1031
+ })
1032
+ };
1033
+ const spawnLabel = t("orchestrator.action.spawn", {
1034
+ defaultValue: "Spawn agent"
1035
+ });
1036
+ const cancelLabel = t("orchestrator.action.cancel", {
1037
+ defaultValue: "Cancel"
1038
+ });
1039
+ const spawn = () => onSubmit({
1040
+ label: label.trim() || void 0,
1041
+ framework: framework.trim() || void 0,
1042
+ model: model.trim() || void 0,
1043
+ workdir: workdir.trim() || void 0,
1044
+ repo: repo.trim() || void 0,
1045
+ task: task.trim() || void 0
1046
+ });
1047
+ const { ref: labelRef, agentProps: labelAgentProps } = useAgentElement({
1048
+ id: "add-agent-label",
1049
+ role: "text-input",
1050
+ label: fieldLabels.label,
1051
+ group: "orchestrator-add-agent",
1052
+ description: "Optional label for the spawned sub-agent",
1053
+ getValue: () => label,
1054
+ onFill: (value) => setLabel(value)
1055
+ });
1056
+ const { ref: frameworkRef, agentProps: frameworkAgentProps } = useAgentElement({
1057
+ id: "add-agent-framework",
1058
+ role: "text-input",
1059
+ label: fieldLabels.framework,
1060
+ group: "orchestrator-add-agent",
1061
+ description: "Coding-agent framework for the sub-agent",
1062
+ getValue: () => framework,
1063
+ onFill: (value) => setFramework(value)
1064
+ });
1065
+ const { ref: modelRef, agentProps: modelAgentProps } = useAgentElement({
1066
+ id: "add-agent-model",
1067
+ role: "text-input",
1068
+ label: fieldLabels.model,
1069
+ group: "orchestrator-add-agent",
1070
+ description: "Model for the sub-agent",
1071
+ getValue: () => model,
1072
+ onFill: (value) => setModel(value)
1073
+ });
1074
+ const { ref: workdirRef, agentProps: workdirAgentProps } = useAgentElement({
1075
+ id: "add-agent-workdir",
1076
+ role: "text-input",
1077
+ label: fieldLabels.workdir,
1078
+ group: "orchestrator-add-agent",
1079
+ description: "Optional working directory for the sub-agent",
1080
+ getValue: () => workdir,
1081
+ onFill: (value) => setWorkdir(value)
1082
+ });
1083
+ const { ref: repoRef, agentProps: repoAgentProps } = useAgentElement({
1084
+ id: "add-agent-repo",
1085
+ role: "text-input",
1086
+ label: fieldLabels.repo,
1087
+ group: "orchestrator-add-agent",
1088
+ description: "Optional repo URL for the sub-agent",
1089
+ getValue: () => repo,
1090
+ onFill: (value) => setRepo(value)
1091
+ });
1092
+ const { ref: taskRef, agentProps: taskAgentProps } = useAgentElement({
1093
+ id: "add-agent-task",
1094
+ role: "textarea",
1095
+ label: fieldLabels.task,
1096
+ group: "orchestrator-add-agent",
1097
+ description: "Optional sub-task description for the sub-agent",
1098
+ getValue: () => task,
1099
+ onFill: (value) => setTask(value)
1100
+ });
1101
+ const { ref: cancelRef, agentProps: cancelAgentProps } = useAgentElement({
1102
+ id: "add-agent-cancel",
1103
+ role: "button",
1104
+ label: cancelLabel,
1105
+ group: "orchestrator-add-agent",
1106
+ description: "Cancel adding a sub-agent"
1107
+ });
1108
+ const { ref: spawnRef, agentProps: spawnAgentProps } = useAgentElement({
1109
+ id: "add-agent-spawn",
1110
+ role: "button",
1111
+ label: spawnLabel,
1112
+ group: "orchestrator-add-agent",
1113
+ description: "Spawn a new sub-agent on this task",
1114
+ onActivate: () => {
1115
+ if (!busy) spawn();
1116
+ }
1117
+ });
1118
+ return /* @__PURE__ */ jsxs("div", { className: "mt-1.5 space-y-1.5", children: [
1119
+ /* @__PURE__ */ jsx(
1120
+ "input",
1121
+ {
1122
+ ref: labelRef,
1123
+ value: label,
1124
+ onChange: (event) => setLabel(event.target.value),
1125
+ className: FIELD_CLASS,
1126
+ placeholder: fieldLabels.label,
1127
+ "aria-label": fieldLabels.label,
1128
+ "data-testid": "orchestrator-add-agent-label",
1129
+ ...labelAgentProps
1130
+ }
1131
+ ),
1132
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5", children: [
1133
+ /* @__PURE__ */ jsx(
1134
+ "input",
1135
+ {
1136
+ ref: frameworkRef,
1137
+ value: framework,
1138
+ onChange: (event) => setFramework(event.target.value),
1139
+ className: FIELD_CLASS,
1140
+ placeholder: fieldLabels.framework,
1141
+ "aria-label": fieldLabels.framework,
1142
+ ...frameworkAgentProps
1143
+ }
1144
+ ),
1145
+ /* @__PURE__ */ jsx(
1146
+ "input",
1147
+ {
1148
+ ref: modelRef,
1149
+ value: model,
1150
+ onChange: (event) => setModel(event.target.value),
1151
+ className: FIELD_CLASS,
1152
+ placeholder: fieldLabels.model,
1153
+ "aria-label": fieldLabels.model,
1154
+ ...modelAgentProps
1155
+ }
1156
+ )
1157
+ ] }),
1158
+ /* @__PURE__ */ jsx(
1159
+ "input",
1160
+ {
1161
+ ref: workdirRef,
1162
+ value: workdir,
1163
+ onChange: (event) => setWorkdir(event.target.value),
1164
+ className: FIELD_CLASS,
1165
+ placeholder: fieldLabels.workdir,
1166
+ "aria-label": fieldLabels.workdir,
1167
+ ...workdirAgentProps
1168
+ }
1169
+ ),
1170
+ /* @__PURE__ */ jsx(
1171
+ "input",
1172
+ {
1173
+ ref: repoRef,
1174
+ value: repo,
1175
+ onChange: (event) => setRepo(event.target.value),
1176
+ className: FIELD_CLASS,
1177
+ placeholder: fieldLabels.repo,
1178
+ "aria-label": fieldLabels.repo,
1179
+ ...repoAgentProps
1180
+ }
1181
+ ),
1182
+ /* @__PURE__ */ jsx(
1183
+ "textarea",
1184
+ {
1185
+ ref: taskRef,
1186
+ value: task,
1187
+ onChange: (event) => setTask(event.target.value),
1188
+ rows: 2,
1189
+ className: `${FIELD_CLASS} resize-none`,
1190
+ placeholder: fieldLabels.task,
1191
+ "aria-label": fieldLabels.task,
1192
+ ...taskAgentProps
1193
+ }
1194
+ ),
1195
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
1196
+ /* @__PURE__ */ jsx(
1197
+ Button,
1198
+ {
1199
+ ref: cancelRef,
1200
+ variant: "secondary",
1201
+ size: "sm",
1202
+ onClick: onClose,
1203
+ className: "h-6 px-2 text-2xs",
1204
+ ...cancelAgentProps,
1205
+ children: cancelLabel
1206
+ }
1207
+ ),
1208
+ /* @__PURE__ */ jsx(
1209
+ Button,
1210
+ {
1211
+ ref: spawnRef,
1212
+ size: "sm",
1213
+ disabled: busy,
1214
+ onClick: spawn,
1215
+ className: "h-6 px-2 text-2xs",
1216
+ "data-testid": "orchestrator-add-agent-submit",
1217
+ ...spawnAgentProps,
1218
+ children: spawnLabel
1219
+ }
1220
+ )
1221
+ ] })
1222
+ ] });
1223
+ }
1224
+ function ControlButton({
1225
+ agentId,
1226
+ description,
1227
+ icon,
1228
+ label,
1229
+ onClick,
1230
+ disabled,
1231
+ tone = "neutral",
1232
+ testId
1233
+ }) {
1234
+ const { ref, agentProps } = useAgentElement({
1235
+ id: agentId,
1236
+ role: "button",
1237
+ label,
1238
+ group: "orchestrator-inspector",
1239
+ description
1240
+ });
1241
+ return /* @__PURE__ */ jsx(
1242
+ "button",
1243
+ {
1244
+ ref,
1245
+ type: "button",
1246
+ disabled,
1247
+ onClick,
1248
+ "aria-label": label,
1249
+ title: label,
1250
+ className: `flex items-center justify-center p-1.5 transition-colors disabled:opacity-50 ${tone === "danger" ? "text-muted hover:text-danger" : "text-muted hover:text-txt"}`,
1251
+ "data-testid": testId,
1252
+ ...agentProps,
1253
+ children: icon
1254
+ }
1255
+ );
1256
+ }
1257
+ function RecoveryActionButton({
1258
+ agentId,
1259
+ description,
1260
+ icon,
1261
+ label,
1262
+ onClick,
1263
+ disabled,
1264
+ testId
1265
+ }) {
1266
+ const { ref, agentProps } = useAgentElement({
1267
+ id: agentId,
1268
+ role: "button",
1269
+ label,
1270
+ group: "orchestrator-operator-detail",
1271
+ description
1272
+ });
1273
+ return /* @__PURE__ */ jsxs(
1274
+ "button",
1275
+ {
1276
+ ref,
1277
+ type: "button",
1278
+ disabled,
1279
+ onClick,
1280
+ className: "inline-flex h-7 items-center gap-1.5 px-1 text-2xs font-semibold text-muted transition-colors hover:text-txt disabled:opacity-50",
1281
+ "data-testid": testId,
1282
+ ...agentProps,
1283
+ children: [
1284
+ icon,
1285
+ label
1286
+ ]
1287
+ }
1288
+ );
1289
+ }
1290
+ function TaskInspector({
1291
+ detail,
1292
+ className,
1293
+ style,
1294
+ onClose,
1295
+ busy,
1296
+ addAgentOpen,
1297
+ onPause,
1298
+ onResume,
1299
+ onArchive,
1300
+ onReopen,
1301
+ onDelete,
1302
+ onFork,
1303
+ onRestart,
1304
+ onRestartWithEditedPlan,
1305
+ onValidate,
1306
+ onSetPriority,
1307
+ onToggleAddAgent,
1308
+ onAddAgent,
1309
+ onInspectSession,
1310
+ onStopAgent,
1311
+ onCopyLink,
1312
+ t,
1313
+ locale
1314
+ }) {
1315
+ const plan = normalizePlan(detail.currentPlan);
1316
+ const sessions = [...detail.sessions].sort(
1317
+ (a, b) => b.lastActivityAt - a.lastActivityAt
1318
+ );
1319
+ const latestChangeSet = sessions.map((session) => readSessionChangeSet(session.metadata)).find((value) => value !== void 0);
1320
+ const artifacts = [...detail.artifacts].reverse().slice(0, 12);
1321
+ const latestPlanRevisionId = detail.planRevisions.length > 0 ? detail.planRevisions[detail.planRevisions.length - 1]?.id : void 0;
1322
+ const archived = detail.status === "archived";
1323
+ const terminal = TERMINAL_TASK_STATUSES.has(detail.status);
1324
+ const providerPolicyLine = detail.providerPolicy ? [
1325
+ detail.providerPolicy.preferredFramework,
1326
+ detail.providerPolicy.providerSource,
1327
+ detail.providerPolicy.model
1328
+ ].filter((part) => Boolean(part)).join(" \xB7 ") : "";
1329
+ const closeDetailsLabel = t("orchestrator.action.closeDetails", {
1330
+ defaultValue: "Close details"
1331
+ });
1332
+ const setPriorityLabel = t("orchestrator.action.setPriority", {
1333
+ defaultValue: "Set priority"
1334
+ });
1335
+ const { ref: closeRef, agentProps: closeAgentProps } = useAgentElement({
1336
+ id: "inspector-close",
1337
+ role: "button",
1338
+ label: closeDetailsLabel,
1339
+ group: "orchestrator-inspector",
1340
+ description: "Close the task details panel"
1341
+ });
1342
+ const { ref: priorityRef, agentProps: priorityAgentProps } = useAgentElement({
1343
+ id: "inspector-priority",
1344
+ role: "select",
1345
+ label: setPriorityLabel,
1346
+ group: "orchestrator-inspector",
1347
+ description: "Set the priority of this task",
1348
+ options: ["low", "normal", "high", "urgent"],
1349
+ getValue: () => detail.priority,
1350
+ onFill: (value) => {
1351
+ const next = paramPriority(value);
1352
+ if (next && next !== detail.priority) onSetPriority(next);
1353
+ }
1354
+ });
1355
+ return /* @__PURE__ */ jsxs(
1356
+ "div",
1357
+ {
1358
+ className: `shrink-0 flex-col gap-4 overflow-y-auto bg-bg p-3 ${className ?? "flex w-80"}`,
1359
+ style,
1360
+ "data-testid": "orchestrator-inspector",
1361
+ children: [
1362
+ onClose ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1363
+ /* @__PURE__ */ jsx("h3", { className: "text-xs font-medium text-muted", children: t("orchestrator.inspector.title", { defaultValue: "Details" }) }),
1364
+ /* @__PURE__ */ jsx(
1365
+ "button",
1366
+ {
1367
+ ref: closeRef,
1368
+ type: "button",
1369
+ onClick: onClose,
1370
+ className: "-mr-1 p-1 text-muted transition-colors hover:text-txt",
1371
+ "aria-label": closeDetailsLabel,
1372
+ "data-testid": "orchestrator-close-inspector",
1373
+ ...closeAgentProps,
1374
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
1375
+ }
1376
+ )
1377
+ ] }) : null,
1378
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1", children: [
1379
+ detail.status === "validating" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1380
+ /* @__PURE__ */ jsx(
1381
+ ControlButton,
1382
+ {
1383
+ agentId: "inspector-approve",
1384
+ description: "Approve the task validation",
1385
+ icon: /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" }),
1386
+ label: t("orchestrator.action.approve", {
1387
+ defaultValue: "Approve"
1388
+ }),
1389
+ onClick: () => onValidate(true),
1390
+ disabled: busy,
1391
+ testId: "orchestrator-approve"
1392
+ }
1393
+ ),
1394
+ /* @__PURE__ */ jsx(
1395
+ ControlButton,
1396
+ {
1397
+ agentId: "inspector-reject",
1398
+ description: "Reject the task validation",
1399
+ icon: /* @__PURE__ */ jsx(X, { className: "h-3 w-3" }),
1400
+ label: t("orchestrator.action.reject", {
1401
+ defaultValue: "Reject"
1402
+ }),
1403
+ onClick: () => onValidate(false),
1404
+ disabled: busy,
1405
+ tone: "danger",
1406
+ testId: "orchestrator-reject"
1407
+ }
1408
+ )
1409
+ ] }) : null,
1410
+ archived ? /* @__PURE__ */ jsx(
1411
+ ControlButton,
1412
+ {
1413
+ agentId: "inspector-reopen",
1414
+ description: "Reopen this archived task",
1415
+ icon: /* @__PURE__ */ jsx(RotateCcw, { className: "h-3 w-3" }),
1416
+ label: t("orchestrator.action.reopen", { defaultValue: "Reopen" }),
1417
+ onClick: onReopen,
1418
+ disabled: busy,
1419
+ testId: "orchestrator-reopen"
1420
+ }
1421
+ ) : terminal ? null : detail.paused ? /* @__PURE__ */ jsx(
1422
+ ControlButton,
1423
+ {
1424
+ agentId: "inspector-resume",
1425
+ description: "Resume this paused task",
1426
+ icon: /* @__PURE__ */ jsx(Play, { className: "h-3 w-3" }),
1427
+ label: t("orchestrator.action.resume", { defaultValue: "Resume" }),
1428
+ onClick: onResume,
1429
+ disabled: busy,
1430
+ testId: "orchestrator-inspector-resume"
1431
+ }
1432
+ ) : /* @__PURE__ */ jsx(
1433
+ ControlButton,
1434
+ {
1435
+ agentId: "inspector-pause",
1436
+ description: "Pause this task",
1437
+ icon: /* @__PURE__ */ jsx(Pause, { className: "h-3 w-3" }),
1438
+ label: t("orchestrator.action.pause", { defaultValue: "Pause" }),
1439
+ onClick: onPause,
1440
+ disabled: busy,
1441
+ testId: "orchestrator-inspector-pause"
1442
+ }
1443
+ ),
1444
+ archived ? null : /* @__PURE__ */ jsx(
1445
+ ControlButton,
1446
+ {
1447
+ agentId: "inspector-archive",
1448
+ description: "Archive this task",
1449
+ icon: /* @__PURE__ */ jsx(Archive, { className: "h-3 w-3" }),
1450
+ label: t("orchestrator.action.archive", {
1451
+ defaultValue: "Archive"
1452
+ }),
1453
+ onClick: onArchive,
1454
+ disabled: busy,
1455
+ testId: "orchestrator-inspector-archive"
1456
+ }
1457
+ ),
1458
+ terminal ? null : /* @__PURE__ */ jsx(
1459
+ ControlButton,
1460
+ {
1461
+ agentId: "inspector-fork",
1462
+ description: "Fork this task into a new task",
1463
+ icon: /* @__PURE__ */ jsx(GitFork, { className: "h-3 w-3" }),
1464
+ label: t("orchestrator.action.fork", { defaultValue: "Fork" }),
1465
+ onClick: onFork,
1466
+ disabled: busy,
1467
+ testId: "orchestrator-fork"
1468
+ }
1469
+ ),
1470
+ terminal ? null : /* @__PURE__ */ jsx(
1471
+ ControlButton,
1472
+ {
1473
+ agentId: "inspector-restart",
1474
+ description: "Restart this task with a fresh worker",
1475
+ icon: /* @__PURE__ */ jsx(RotateCcw, { className: "h-3 w-3" }),
1476
+ label: t("orchestrator.action.restart", {
1477
+ defaultValue: "Restart"
1478
+ }),
1479
+ onClick: onRestart,
1480
+ disabled: busy,
1481
+ testId: "orchestrator-inspector-restart"
1482
+ }
1483
+ ),
1484
+ terminal ? null : /* @__PURE__ */ jsx(
1485
+ ControlButton,
1486
+ {
1487
+ agentId: "inspector-add-agent",
1488
+ description: "Open the add-agent form for this task",
1489
+ icon: /* @__PURE__ */ jsx(UserPlus, { className: "h-3 w-3" }),
1490
+ label: t("orchestrator.action.addAgent", {
1491
+ defaultValue: "Add agent"
1492
+ }),
1493
+ onClick: onToggleAddAgent,
1494
+ disabled: busy,
1495
+ testId: "orchestrator-add-agent"
1496
+ }
1497
+ ),
1498
+ /* @__PURE__ */ jsx(
1499
+ ControlButton,
1500
+ {
1501
+ agentId: "inspector-copy-link",
1502
+ description: "Copy a deep link to this task",
1503
+ icon: /* @__PURE__ */ jsx(Copy, { className: "h-3 w-3" }),
1504
+ label: t("orchestrator.action.copyLink", {
1505
+ defaultValue: "Copy link"
1506
+ }),
1507
+ onClick: onCopyLink,
1508
+ disabled: busy,
1509
+ testId: "orchestrator-copy-link"
1510
+ }
1511
+ ),
1512
+ terminal ? null : /* @__PURE__ */ jsxs(
1513
+ "select",
1514
+ {
1515
+ ref: priorityRef,
1516
+ "aria-label": setPriorityLabel,
1517
+ value: detail.priority,
1518
+ disabled: busy,
1519
+ onChange: (event) => {
1520
+ const next = paramPriority(event.target.value);
1521
+ if (next && next !== detail.priority) onSetPriority(next);
1522
+ },
1523
+ className: "border-border/35 border-b bg-transparent px-1 py-1 text-2xs text-muted transition-colors hover:border-accent/60 hover:text-txt disabled:opacity-50",
1524
+ "data-testid": "orchestrator-priority-select",
1525
+ ...priorityAgentProps,
1526
+ children: [
1527
+ /* @__PURE__ */ jsx("option", { value: "low", children: labelPriority("low", t) }),
1528
+ /* @__PURE__ */ jsx("option", { value: "normal", children: labelPriority("normal", t) }),
1529
+ /* @__PURE__ */ jsx("option", { value: "high", children: labelPriority("high", t) }),
1530
+ /* @__PURE__ */ jsx("option", { value: "urgent", children: labelPriority("urgent", t) })
1531
+ ]
1532
+ }
1533
+ ),
1534
+ /* @__PURE__ */ jsxs(AlertDialog, { children: [
1535
+ /* @__PURE__ */ jsx(AlertDialogTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
1536
+ ControlButton,
1537
+ {
1538
+ agentId: "inspector-delete",
1539
+ description: "Delete this task",
1540
+ icon: /* @__PURE__ */ jsx(Trash2, { className: "h-3 w-3" }),
1541
+ label: t("orchestrator.action.delete", {
1542
+ defaultValue: "Delete"
1543
+ }),
1544
+ onClick: () => {
1545
+ },
1546
+ disabled: busy,
1547
+ tone: "danger",
1548
+ testId: "orchestrator-delete"
1549
+ }
1550
+ ) }),
1551
+ /* @__PURE__ */ jsxs(AlertDialogContent, { children: [
1552
+ /* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
1553
+ /* @__PURE__ */ jsx(AlertDialogTitle, { children: t("orchestrator.confirmDeleteTitle", {
1554
+ defaultValue: "Delete task?"
1555
+ }) }),
1556
+ /* @__PURE__ */ jsx(AlertDialogDescription, { children: t("orchestrator.confirmDelete", {
1557
+ defaultValue: "Delete this task and its transcript? This can't be undone."
1558
+ }) })
1559
+ ] }),
1560
+ /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [
1561
+ /* @__PURE__ */ jsx(AlertDialogCancel, { children: t("orchestrator.action.cancel", { defaultValue: "Cancel" }) }),
1562
+ /* @__PURE__ */ jsx(
1563
+ AlertDialogAction,
1564
+ {
1565
+ onClick: onDelete,
1566
+ className: "bg-red-600 hover:bg-red-700",
1567
+ children: t("orchestrator.action.delete", { defaultValue: "Delete" })
1568
+ }
1569
+ )
1570
+ ] })
1571
+ ] })
1572
+ ] })
1573
+ ] }),
1574
+ addAgentOpen && !terminal ? /* @__PURE__ */ jsx(
1575
+ AddAgentForm,
1576
+ {
1577
+ busy,
1578
+ onClose: onToggleAddAgent,
1579
+ onSubmit: onAddAgent,
1580
+ t
1581
+ }
1582
+ ) : null,
1583
+ /* @__PURE__ */ jsxs(
1584
+ InspectorSection,
1585
+ {
1586
+ title: t("orchestrator.goal", { defaultValue: "Goal" }),
1587
+ children: [
1588
+ /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap text-xs-tight text-txt", children: detail.goal || detail.originalRequest }),
1589
+ detail.parentTaskId ? /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-2xs text-muted", children: t("orchestrator.forkedFrom", {
1590
+ defaultValue: "Forked from {{id}}",
1591
+ id: detail.parentTaskId
1592
+ }) }) : null
1593
+ ]
1594
+ }
1595
+ ),
1596
+ /* @__PURE__ */ jsx(
1597
+ InspectorSection,
1598
+ {
1599
+ title: t("orchestrator.subAgents", { defaultValue: "Sub-agents" }),
1600
+ children: sessions.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs-tight text-muted", children: t("orchestrator.noSubAgents", {
1601
+ defaultValue: "No sub-agents spawned yet."
1602
+ }) }) : /* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: sessions.map((session) => /* @__PURE__ */ jsx(
1603
+ SubAgentCard,
1604
+ {
1605
+ session,
1606
+ busy,
1607
+ onInspect: onInspectSession,
1608
+ onStop: onStopAgent,
1609
+ t,
1610
+ locale
1611
+ },
1612
+ session.id
1613
+ )) })
1614
+ }
1615
+ ),
1616
+ latestChangeSet ? /* @__PURE__ */ jsx(
1617
+ InspectorSection,
1618
+ {
1619
+ title: t("orchestrator.changes", { defaultValue: "Changes" }),
1620
+ children: /* @__PURE__ */ jsx(DiffReviewPanel, { changeSet: latestChangeSet })
1621
+ }
1622
+ ) : null,
1623
+ plan ? /* @__PURE__ */ jsx(PlanSection, { plan, t }) : null,
1624
+ detail.currentPlan && !terminal ? /* @__PURE__ */ jsx(
1625
+ EditedPlanRestartSection,
1626
+ {
1627
+ plan: detail.currentPlan,
1628
+ latestPlanRevisionId,
1629
+ busy,
1630
+ onSubmit: onRestartWithEditedPlan,
1631
+ t
1632
+ }
1633
+ ) : null,
1634
+ detail.acceptanceCriteria.length > 0 ? /* @__PURE__ */ jsx(AcceptanceSection, { criteria: detail.acceptanceCriteria, t }) : null,
1635
+ artifacts.length > 0 ? /* @__PURE__ */ jsx(ArtifactSection, { artifacts, t }) : null,
1636
+ /* @__PURE__ */ jsx(UsageSection, { usage: detail.usage, t, locale }),
1637
+ providerPolicyLine ? /* @__PURE__ */ jsx(
1638
+ InspectorSection,
1639
+ {
1640
+ title: t("orchestrator.providerPolicy", {
1641
+ defaultValue: "Provider policy"
1642
+ }),
1643
+ children: /* @__PURE__ */ jsx("p", { className: "text-xs-tight text-txt", children: providerPolicyLine })
1644
+ }
1645
+ ) : null
1646
+ ]
1647
+ }
1648
+ );
1649
+ }
1650
+ function compactText(value, max = 6e3) {
1651
+ if (value.length <= max) return value;
1652
+ return `${value.slice(0, max).trimEnd()}
1653
+
1654
+ \u2026 ${(value.length - max).toLocaleString()} characters truncated`;
1655
+ }
1656
+ function hasRecordEntries(value) {
1657
+ return Boolean(value && Object.keys(value).length > 0);
1658
+ }
1659
+ function readSessionChangeSet(metadata) {
1660
+ const raw = metadata.lastChangeSet;
1661
+ if (!raw || typeof raw !== "object") return void 0;
1662
+ const candidate = raw;
1663
+ if (!Array.isArray(candidate.changedFiles)) return void 0;
1664
+ if (typeof candidate.diff !== "string") return void 0;
1665
+ if (typeof candidate.diffStat !== "string") return void 0;
1666
+ if (typeof candidate.truncated !== "boolean") return void 0;
1667
+ if (typeof candidate.capturedAt !== "number") return void 0;
1668
+ return candidate;
1669
+ }
1670
+ function JsonBlock({
1671
+ value,
1672
+ emptyLabel
1673
+ }) {
1674
+ const empty = value === null || value === void 0 || Array.isArray(value) && value.length === 0 || typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0;
1675
+ if (empty) {
1676
+ return /* @__PURE__ */ jsx("p", { className: "text-xs-tight text-muted", children: emptyLabel });
1677
+ }
1678
+ const text = typeof value === "string" ? value : JSON.stringify(value, null, 2);
1679
+ return /* @__PURE__ */ jsx(
1680
+ "pre",
1681
+ {
1682
+ className: "max-h-72 overflow-auto bg-bg/60 px-2.5 py-1.5 font-mono text-2xs leading-relaxed text-muted",
1683
+ "data-testid": "orchestrator-detail-json",
1684
+ children: compactText(text)
1685
+ }
1686
+ );
1687
+ }
1688
+ function DetailRow({ label, value }) {
1689
+ if (value === null || value === void 0 || value === "") return null;
1690
+ return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[5.25rem_minmax(0,1fr)] gap-2 text-xs-tight", children: [
1691
+ /* @__PURE__ */ jsx("span", { className: "text-muted", children: label }),
1692
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 break-words text-txt", children: value })
1693
+ ] });
1694
+ }
1695
+ function OperatorTabs({
1696
+ active,
1697
+ onSelect,
1698
+ t
1699
+ }) {
1700
+ const tabs = [
1701
+ {
1702
+ id: "input",
1703
+ label: t("orchestrator.detail.tabs.input", { defaultValue: "Input" })
1704
+ },
1705
+ {
1706
+ id: "output",
1707
+ label: t("orchestrator.detail.tabs.output", { defaultValue: "Output" })
1708
+ },
1709
+ {
1710
+ id: "events",
1711
+ label: t("orchestrator.detail.tabs.events", { defaultValue: "Events" })
1712
+ },
1713
+ {
1714
+ id: "usage",
1715
+ label: t("orchestrator.detail.tabs.usage", { defaultValue: "Usage" })
1716
+ }
1717
+ ];
1718
+ return /* @__PURE__ */ jsx(
1719
+ "div",
1720
+ {
1721
+ className: "flex gap-2",
1722
+ role: "tablist",
1723
+ "aria-label": t("orchestrator.detail.tabsLabel", {
1724
+ defaultValue: "Detail tabs"
1725
+ }),
1726
+ children: tabs.map((tab) => /* @__PURE__ */ jsx(
1727
+ "button",
1728
+ {
1729
+ type: "button",
1730
+ role: "tab",
1731
+ "aria-selected": active === tab.id,
1732
+ onClick: () => onSelect(tab.id),
1733
+ className: `flex-1 px-1 py-1 text-xs font-medium transition-colors ${active === tab.id ? "text-accent" : "text-muted hover:text-txt"}`,
1734
+ children: tab.label
1735
+ },
1736
+ tab.id
1737
+ ))
1738
+ }
1739
+ );
1740
+ }
1741
+ function sessionUsage(session) {
1742
+ return {
1743
+ inputTokens: session.inputTokens,
1744
+ outputTokens: session.outputTokens,
1745
+ reasoningTokens: session.reasoningTokens,
1746
+ cacheTokens: session.cacheTokens,
1747
+ totalTokens: session.totalTokens,
1748
+ costUsd: session.costUsd,
1749
+ state: session.usageState,
1750
+ byProvider: [
1751
+ {
1752
+ provider: session.providerSource ?? session.framework,
1753
+ model: session.model ?? void 0,
1754
+ inputTokens: session.inputTokens,
1755
+ outputTokens: session.outputTokens,
1756
+ reasoningTokens: session.reasoningTokens,
1757
+ cacheTokens: session.cacheTokens,
1758
+ totalTokens: session.totalTokens,
1759
+ costUsd: session.costUsd,
1760
+ state: session.usageState
1761
+ }
1762
+ ]
1763
+ };
1764
+ }
1765
+ function blockEventIds(block) {
1766
+ if (block.kind === "tool") return block.tool.eventIds;
1767
+ if (block.kind === "reasoning") return block.eventIds;
1768
+ if (block.kind === "notice") return [block.eventId];
1769
+ return [];
1770
+ }
1771
+ function blockMessageIds(block) {
1772
+ if (block.kind === "user" || block.kind === "agent") return block.messageIds;
1773
+ return [];
1774
+ }
1775
+ function blockSelection(block) {
1776
+ return {
1777
+ kind: "block",
1778
+ blockKey: block.key,
1779
+ blockKind: block.kind,
1780
+ eventIds: blockEventIds(block),
1781
+ messageIds: blockMessageIds(block)
1782
+ };
1783
+ }
1784
+ function blockMatchesSelection(block, selection) {
1785
+ if (block.key === selection.blockKey) return true;
1786
+ if (block.kind !== selection.blockKind) return false;
1787
+ const eventIds = blockEventIds(block);
1788
+ if (selection.eventIds.length > 0 && selection.eventIds.some((id) => eventIds.includes(id))) {
1789
+ return true;
1790
+ }
1791
+ const messageIds = blockMessageIds(block);
1792
+ return selection.messageIds.length > 0 && selection.messageIds.some((id) => messageIds.includes(id));
1793
+ }
1794
+ function blockSelectionKey(selection) {
1795
+ if (selection.kind === "session") return `session:${selection.sessionId}`;
1796
+ return [
1797
+ "block",
1798
+ selection.blockKey,
1799
+ selection.blockKind,
1800
+ selection.eventIds.join(","),
1801
+ selection.messageIds.join(",")
1802
+ ].join(":");
1803
+ }
1804
+ function blockTitle(block, t) {
1805
+ if (block.kind === "tool") return block.tool.title;
1806
+ if (block.kind === "agent") return block.senderName;
1807
+ if (block.kind === "user")
1808
+ return t("orchestrator.detail.userTurn", { defaultValue: "User turn" });
1809
+ if (block.kind === "reasoning")
1810
+ return t("orchestrator.detail.reasoning", { defaultValue: "Reasoning" });
1811
+ return block.eventType.replace(/_/g, " ");
1812
+ }
1813
+ function eventError(events, t) {
1814
+ const error = events.find((event) => event.eventType === "error");
1815
+ if (!error) return null;
1816
+ const message = typeof error.data?.message === "string" ? error.data.message : typeof error.data?.error === "string" ? error.data.error : error.summary;
1817
+ return message.trim() || t("orchestrator.detail.errorFallback", { defaultValue: "Error" });
1818
+ }
1819
+ function blockError(block, events, t) {
1820
+ const fromEvent = eventError(events, t);
1821
+ if (fromEvent) return fromEvent;
1822
+ if (!block) return null;
1823
+ if (block.kind === "agent" && block.tone === "error") {
1824
+ return compactText(block.content, 600);
1825
+ }
1826
+ if (block.kind === "notice" && block.eventType === "error") {
1827
+ return block.text;
1828
+ }
1829
+ if (block.kind === "tool" && block.tool.status === "failed") {
1830
+ if (block.tool.output) return compactText(block.tool.output, 600);
1831
+ if (typeof block.tool.exitCode === "number") {
1832
+ return t("orchestrator.detail.toolExited", {
1833
+ defaultValue: `Tool exited with code ${block.tool.exitCode}.`,
1834
+ code: block.tool.exitCode
1835
+ });
1836
+ }
1837
+ return block.tool.rawStatus ?? t("orchestrator.detail.toolFailed", { defaultValue: "Tool failed." });
1838
+ }
1839
+ return null;
1840
+ }
1841
+ function sessionError(session, t) {
1842
+ if (session.status !== "error" && session.status !== "errored") return null;
1843
+ return session.completionSummary ?? session.activeTool ?? t("orchestrator.detail.sessionFailed", {
1844
+ defaultValue: "Session failed."
1845
+ });
1846
+ }
1847
+ function ErrorFirstBanner({ text }) {
1848
+ if (!text) return null;
1849
+ return /* @__PURE__ */ jsx("div", { className: "rounded-md bg-red-500/10 px-2.5 py-2 text-xs-tight text-red-500", children: text });
1850
+ }
1851
+ function OperatorDrawerShell({
1852
+ title,
1853
+ subtitle,
1854
+ closeLabel,
1855
+ className,
1856
+ style,
1857
+ onClose,
1858
+ children
1859
+ }) {
1860
+ return /* @__PURE__ */ jsxs(
1861
+ "div",
1862
+ {
1863
+ className: `shrink-0 flex-col gap-2.5 overflow-y-auto bg-bg p-3 ${className ?? "flex w-80"}`,
1864
+ style,
1865
+ "data-testid": "orchestrator-operator-detail",
1866
+ children: [
1867
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
1868
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
1869
+ /* @__PURE__ */ jsx("h3", { className: "truncate text-xs font-medium text-muted", children: title }),
1870
+ /* @__PURE__ */ jsx("p", { className: "mt-0.5 truncate text-xs-tight font-medium text-txt", children: subtitle })
1871
+ ] }),
1872
+ /* @__PURE__ */ jsx(
1873
+ "button",
1874
+ {
1875
+ type: "button",
1876
+ onClick: onClose,
1877
+ className: "-mr-1 p-1 text-muted transition-colors hover:text-txt",
1878
+ "aria-label": closeLabel,
1879
+ "data-testid": "orchestrator-close-operator-detail",
1880
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
1881
+ }
1882
+ )
1883
+ ] }),
1884
+ children
1885
+ ]
1886
+ }
1887
+ );
1888
+ }
1889
+ function EventList({
1890
+ events,
1891
+ messages,
1892
+ locale,
1893
+ t
1894
+ }) {
1895
+ if (events.length === 0 && messages.length === 0) {
1896
+ return /* @__PURE__ */ jsx("p", { className: "text-xs-tight text-muted", children: t("orchestrator.detail.noEvents", {
1897
+ defaultValue: "No events captured."
1898
+ }) });
1899
+ }
1900
+ const timeline = [
1901
+ ...messages.map((message) => ({
1902
+ kind: "message",
1903
+ id: message.id,
1904
+ timestamp: message.timestamp,
1905
+ record: message
1906
+ })),
1907
+ ...events.map((event) => ({
1908
+ kind: "event",
1909
+ id: event.id,
1910
+ timestamp: event.timestamp,
1911
+ record: event
1912
+ }))
1913
+ ].sort((a, b) => a.timestamp - b.timestamp);
1914
+ return /* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: timeline.map((item) => {
1915
+ if (item.kind === "message") {
1916
+ const message = item.record;
1917
+ return /* @__PURE__ */ jsxs("div", { className: "py-1", children: [
1918
+ /* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center gap-2 text-2xs text-muted", children: [
1919
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-txt", children: message.senderKind }),
1920
+ /* @__PURE__ */ jsx("span", { children: message.direction }),
1921
+ /* @__PURE__ */ jsx("span", { className: "ml-auto tabular-nums", children: formatClockTime(message.timestamp, locale) })
1922
+ ] }),
1923
+ /* @__PURE__ */ jsx(
1924
+ JsonBlock,
1925
+ {
1926
+ value: message,
1927
+ emptyLabel: t("orchestrator.detail.noMessagePayload", {
1928
+ defaultValue: "No message payload."
1929
+ })
1930
+ }
1931
+ )
1932
+ ] }, `message-${message.id}`);
1933
+ }
1934
+ const event = item.record;
1935
+ return /* @__PURE__ */ jsxs("div", { className: "py-1", children: [
1936
+ /* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center gap-2 text-2xs text-muted", children: [
1937
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-txt", children: event.eventType.replace(/_/g, " ") }),
1938
+ /* @__PURE__ */ jsx("span", { className: "ml-auto tabular-nums", children: formatClockTime(event.timestamp, locale) })
1939
+ ] }),
1940
+ event.summary ? /* @__PURE__ */ jsx("p", { className: "mb-1 text-xs-tight text-txt", children: event.summary }) : null,
1941
+ /* @__PURE__ */ jsx(
1942
+ JsonBlock,
1943
+ {
1944
+ value: event.data,
1945
+ emptyLabel: t("orchestrator.detail.noEventData", {
1946
+ defaultValue: "No event data."
1947
+ })
1948
+ }
1949
+ )
1950
+ ] }, `event-${event.id}`);
1951
+ }) });
1952
+ }
1953
+ function OperatorDetailDrawer({
1954
+ selection,
1955
+ block,
1956
+ session,
1957
+ events,
1958
+ messages,
1959
+ taskUsage,
1960
+ busy,
1961
+ className,
1962
+ style,
1963
+ onClose,
1964
+ onRetry,
1965
+ onRerun,
1966
+ t,
1967
+ locale
1968
+ }) {
1969
+ const [tab, setTab] = useState("input");
1970
+ const closeDetailsLabel = t("orchestrator.detail.close", {
1971
+ defaultValue: "Close details"
1972
+ });
1973
+ const label = (key, defaultValue) => t(`orchestrator.detail.${key}`, { defaultValue });
1974
+ if (selection.kind === "session" && !session) {
1975
+ return /* @__PURE__ */ jsx(
1976
+ OperatorDrawerShell,
1977
+ {
1978
+ title: t("orchestrator.detail.session", { defaultValue: "Session" }),
1979
+ subtitle: t("orchestrator.detail.noLongerAvailable", {
1980
+ defaultValue: "No longer available"
1981
+ }),
1982
+ closeLabel: closeDetailsLabel,
1983
+ className,
1984
+ style,
1985
+ onClose,
1986
+ children: /* @__PURE__ */ jsx("p", { className: "text-xs-tight text-muted", children: t("orchestrator.detail.sessionDataChanged", {
1987
+ defaultValue: "Session data changed."
1988
+ }) })
1989
+ }
1990
+ );
1991
+ }
1992
+ if (selection.kind === "block" && !block) {
1993
+ return /* @__PURE__ */ jsx(
1994
+ OperatorDrawerShell,
1995
+ {
1996
+ title: t("orchestrator.detail.event", { defaultValue: "Event" }),
1997
+ subtitle: t("orchestrator.detail.noLongerAvailable", {
1998
+ defaultValue: "No longer available"
1999
+ }),
2000
+ closeLabel: closeDetailsLabel,
2001
+ className,
2002
+ style,
2003
+ onClose,
2004
+ children: /* @__PURE__ */ jsx("p", { className: "text-xs-tight text-muted", children: t("orchestrator.detail.timelineDataChanged", {
2005
+ defaultValue: "Timeline data changed."
2006
+ }) })
2007
+ }
2008
+ );
2009
+ }
2010
+ const isSession = selection.kind === "session";
2011
+ const title = isSession ? t("orchestrator.detail.sessionDetail", { defaultValue: "Session detail" }) : t("orchestrator.detail.timelineDetail", {
2012
+ defaultValue: "Timeline detail"
2013
+ });
2014
+ const subtitle = isSession ? session?.label ?? t("orchestrator.detail.session", { defaultValue: "Session" }) : block ? blockTitle(block, t) : t("orchestrator.detail.event", { defaultValue: "Event" });
2015
+ const activeUsage = session ? sessionUsage(session) : taskUsage;
2016
+ const toolUsageFallbackLabel = session ? label(
2017
+ "perToolUsageUnavailable",
2018
+ "Per-tool usage is not emitted yet; showing the owning session total."
2019
+ ) : label(
2020
+ "perToolUsageTaskFallback",
2021
+ "Per-tool usage is not emitted yet; showing the task total."
2022
+ );
2023
+ const errorText = isSession && session ? sessionError(session, t) : block ? blockError(block, events, t) : null;
2024
+ const retryMessage = !isSession ? messages[0] : null;
2025
+ const rerunEvent = !isSession ? events[0] : null;
2026
+ const retryLabel = label("retry", "Retry");
2027
+ const rerunLabel = label("rerun", "Rerun");
2028
+ const recoveryActions = [];
2029
+ if (isSession && session) {
2030
+ recoveryActions.push(
2031
+ /* @__PURE__ */ jsx(
2032
+ RecoveryActionButton,
2033
+ {
2034
+ agentId: "operator-retry-session",
2035
+ description: "Retry this session's work in a new worker",
2036
+ icon: /* @__PURE__ */ jsx(RotateCcw, { className: "h-3 w-3" }),
2037
+ label: retryLabel,
2038
+ onClick: () => onRetry({
2039
+ sessionId: session.sessionId,
2040
+ mode: "new-session",
2041
+ instruction: `Retry work from session ${session.label ?? session.sessionId}.`
2042
+ }),
2043
+ disabled: busy,
2044
+ testId: "orchestrator-detail-retry"
2045
+ },
2046
+ "retry-session"
2047
+ )
2048
+ );
2049
+ } else if (retryMessage) {
2050
+ recoveryActions.push(
2051
+ /* @__PURE__ */ jsx(
2052
+ RecoveryActionButton,
2053
+ {
2054
+ agentId: "operator-retry-message",
2055
+ description: "Retry this selected turn in a new worker",
2056
+ icon: /* @__PURE__ */ jsx(RotateCcw, { className: "h-3 w-3" }),
2057
+ label: retryLabel,
2058
+ onClick: () => onRetry({
2059
+ messageId: retryMessage.id,
2060
+ sessionId: retryMessage.sessionId ?? void 0,
2061
+ mode: "new-session",
2062
+ instruction: "Retry this selected turn."
2063
+ }),
2064
+ disabled: busy,
2065
+ testId: "orchestrator-detail-retry"
2066
+ },
2067
+ "retry-message"
2068
+ )
2069
+ );
2070
+ }
2071
+ if (rerunEvent) {
2072
+ recoveryActions.push(
2073
+ /* @__PURE__ */ jsx(
2074
+ RecoveryActionButton,
2075
+ {
2076
+ agentId: "operator-rerun-event",
2077
+ description: "Rerun from this selected event without rewriting history",
2078
+ icon: /* @__PURE__ */ jsx(ChevronsUp, { className: "h-3 w-3" }),
2079
+ label: rerunLabel,
2080
+ onClick: () => onRerun({
2081
+ eventId: rerunEvent.id,
2082
+ instruction: `Rerun from ${rerunEvent.eventType.replace(/_/g, " ")}.`,
2083
+ stopActive: false,
2084
+ preserveHistory: true
2085
+ }),
2086
+ disabled: busy,
2087
+ testId: "orchestrator-detail-rerun"
2088
+ },
2089
+ "rerun-event"
2090
+ )
2091
+ );
2092
+ }
2093
+ let body;
2094
+ if (tab === "input") {
2095
+ if (isSession && session) {
2096
+ body = /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2097
+ /* @__PURE__ */ jsx(
2098
+ DetailRow,
2099
+ {
2100
+ label: label("status", "Status"),
2101
+ value: session.status.replace(/_/g, " ")
2102
+ }
2103
+ ),
2104
+ /* @__PURE__ */ jsx(
2105
+ DetailRow,
2106
+ {
2107
+ label: label("framework", "Framework"),
2108
+ value: session.framework
2109
+ }
2110
+ ),
2111
+ /* @__PURE__ */ jsx(
2112
+ DetailRow,
2113
+ {
2114
+ label: label("provider", "Provider"),
2115
+ value: session.providerSource
2116
+ }
2117
+ ),
2118
+ /* @__PURE__ */ jsx(DetailRow, { label: label("model", "Model"), value: session.model }),
2119
+ /* @__PURE__ */ jsx(
2120
+ DetailRow,
2121
+ {
2122
+ label: label("workdir", "Workdir"),
2123
+ value: session.workdir
2124
+ }
2125
+ ),
2126
+ /* @__PURE__ */ jsx(DetailRow, { label: label("repo", "Repo"), value: session.repo }),
2127
+ /* @__PURE__ */ jsx(InspectorSection, { title: label("originalTask", "Original task"), children: /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap text-xs-tight text-txt", children: session.originalTask }) }),
2128
+ hasRecordEntries(session.metadata) ? /* @__PURE__ */ jsx(InspectorSection, { title: label("metadata", "Metadata"), children: /* @__PURE__ */ jsx(
2129
+ JsonBlock,
2130
+ {
2131
+ value: session.metadata,
2132
+ emptyLabel: label("noMetadata", "No metadata.")
2133
+ }
2134
+ ) }) : null
2135
+ ] });
2136
+ } else if (block?.kind === "tool") {
2137
+ const input = block.tool.rawInput ?? {};
2138
+ body = /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2139
+ /* @__PURE__ */ jsx(DetailRow, { label: label("toolId", "Tool id"), value: block.tool.id }),
2140
+ /* @__PURE__ */ jsx(
2141
+ DetailRow,
2142
+ {
2143
+ label: label("kind", "Kind"),
2144
+ value: block.tool.kind || "tool"
2145
+ }
2146
+ ),
2147
+ /* @__PURE__ */ jsx(
2148
+ DetailRow,
2149
+ {
2150
+ label: label("status", "Status"),
2151
+ value: block.tool.rawStatus ?? block.tool.status
2152
+ }
2153
+ ),
2154
+ /* @__PURE__ */ jsx(
2155
+ DetailRow,
2156
+ {
2157
+ label: label("file", "File"),
2158
+ value: block.tool.filePath
2159
+ }
2160
+ ),
2161
+ /* @__PURE__ */ jsx(
2162
+ DetailRow,
2163
+ {
2164
+ label: label("command", "Command"),
2165
+ value: block.tool.command
2166
+ }
2167
+ ),
2168
+ /* @__PURE__ */ jsx(DetailRow, { label: label("query", "Query"), value: block.tool.query }),
2169
+ /* @__PURE__ */ jsx(
2170
+ JsonBlock,
2171
+ {
2172
+ value: input,
2173
+ emptyLabel: label("noToolInput", "No tool input captured.")
2174
+ }
2175
+ )
2176
+ ] });
2177
+ } else if (block?.kind === "user") {
2178
+ body = /* @__PURE__ */ jsx("pre", { className: "whitespace-pre-wrap bg-bg/60 px-2.5 py-1.5 text-xs-tight text-txt", children: block.content });
2179
+ } else if (block?.kind === "agent") {
2180
+ body = /* @__PURE__ */ jsx(
2181
+ JsonBlock,
2182
+ {
2183
+ value: messages.map((message) => message.metadata),
2184
+ emptyLabel: label("noInputMetadata", "No input metadata captured.")
2185
+ }
2186
+ );
2187
+ } else {
2188
+ body = /* @__PURE__ */ jsx(
2189
+ JsonBlock,
2190
+ {
2191
+ value: events.map((event) => event.data),
2192
+ emptyLabel: label("noInput", "No input captured.")
2193
+ }
2194
+ );
2195
+ }
2196
+ } else if (tab === "output") {
2197
+ if (isSession && session) {
2198
+ body = /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2199
+ /* @__PURE__ */ jsx(
2200
+ DetailRow,
2201
+ {
2202
+ label: label("activeTool", "Active tool"),
2203
+ value: session.activeTool
2204
+ }
2205
+ ),
2206
+ /* @__PURE__ */ jsx(
2207
+ DetailRow,
2208
+ {
2209
+ label: label("decisions", "Decisions"),
2210
+ value: t("orchestrator.detail.decisionCounts", {
2211
+ defaultValue: `${session.decisionCount} total \xB7 ${session.autoResolvedCount} auto`,
2212
+ count: session.decisionCount,
2213
+ auto: session.autoResolvedCount
2214
+ })
2215
+ }
2216
+ ),
2217
+ /* @__PURE__ */ jsx(
2218
+ DetailRow,
2219
+ {
2220
+ label: label("lastInput", "Last input"),
2221
+ value: session.lastInputSentAt ? formatClockTime(session.lastInputSentAt, locale) : null
2222
+ }
2223
+ ),
2224
+ /* @__PURE__ */ jsx(InspectorSection, { title: label("completion", "Completion"), children: session.completionSummary ? /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap text-xs-tight text-txt", children: session.completionSummary }) : /* @__PURE__ */ jsx("p", { className: "text-xs-tight text-muted", children: label("noCompletion", "No completion yet.") }) })
2225
+ ] });
2226
+ } else if (block?.kind === "tool") {
2227
+ body = /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2228
+ /* @__PURE__ */ jsx(
2229
+ DetailRow,
2230
+ {
2231
+ label: label("exit", "Exit"),
2232
+ value: block.tool.exitCode
2233
+ }
2234
+ ),
2235
+ /* @__PURE__ */ jsx(
2236
+ DetailRow,
2237
+ {
2238
+ label: label("duration", "Duration"),
2239
+ value: block.tool.durationMs ? formatDuration(block.tool.durationMs) : null
2240
+ }
2241
+ ),
2242
+ /* @__PURE__ */ jsx(ToolBody, { tool: block.tool }),
2243
+ /* @__PURE__ */ jsx(
2244
+ JsonBlock,
2245
+ {
2246
+ value: block.tool.rawOutput,
2247
+ emptyLabel: label("noRawOutput", "No raw output payload captured.")
2248
+ }
2249
+ )
2250
+ ] });
2251
+ } else if (block?.kind === "agent" || block?.kind === "user") {
2252
+ body = /* @__PURE__ */ jsx("pre", { className: "whitespace-pre-wrap bg-bg/60 px-2.5 py-1.5 text-xs-tight text-txt", children: compactText(block.content) });
2253
+ } else if (block?.kind === "reasoning") {
2254
+ body = /* @__PURE__ */ jsx("pre", { className: "whitespace-pre-wrap bg-bg/60 px-2.5 py-1.5 text-xs-tight text-txt", children: compactText(block.text) });
2255
+ } else if (block) {
2256
+ body = /* @__PURE__ */ jsx("p", { className: "text-xs-tight text-txt", children: block.text });
2257
+ } else {
2258
+ body = null;
2259
+ }
2260
+ } else if (tab === "events") {
2261
+ body = /* @__PURE__ */ jsx(EventList, { events, messages, locale, t });
2262
+ } else {
2263
+ body = /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2264
+ block?.kind === "tool" ? /* @__PURE__ */ jsx("p", { className: "text-xs-tight text-muted", children: toolUsageFallbackLabel }) : null,
2265
+ /* @__PURE__ */ jsx(UsageSection, { usage: activeUsage, t, locale })
2266
+ ] });
2267
+ }
2268
+ return /* @__PURE__ */ jsxs(
2269
+ OperatorDrawerShell,
2270
+ {
2271
+ title,
2272
+ subtitle,
2273
+ closeLabel: closeDetailsLabel,
2274
+ className,
2275
+ style,
2276
+ onClose,
2277
+ children: [
2278
+ /* @__PURE__ */ jsx(ErrorFirstBanner, { text: errorText }),
2279
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
2280
+ session ? /* @__PURE__ */ jsxs(Fragment, { children: [
2281
+ /* @__PURE__ */ jsx(
2282
+ DetailRow,
2283
+ {
2284
+ label: label("session", "Session"),
2285
+ value: session.sessionId
2286
+ }
2287
+ ),
2288
+ /* @__PURE__ */ jsx(
2289
+ DetailRow,
2290
+ {
2291
+ label: label("activity", "Activity"),
2292
+ value: formatClockTime(session.lastActivityAt, locale)
2293
+ }
2294
+ )
2295
+ ] }) : null,
2296
+ block ? /* @__PURE__ */ jsxs(Fragment, { children: [
2297
+ /* @__PURE__ */ jsx(DetailRow, { label: label("kind", "Kind"), value: block.kind }),
2298
+ /* @__PURE__ */ jsx(
2299
+ DetailRow,
2300
+ {
2301
+ label: label("time", "Time"),
2302
+ value: formatClockTime(block.at, locale)
2303
+ }
2304
+ )
2305
+ ] }) : null
2306
+ ] }),
2307
+ recoveryActions.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", "data-testid": "orchestrator-detail-recovery", children: [
2308
+ /* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-muted", children: label("recovery", "Recovery") }),
2309
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: recoveryActions })
2310
+ ] }) : null,
2311
+ /* @__PURE__ */ jsx(OperatorTabs, { active: tab, onSelect: setTab, t }),
2312
+ /* @__PURE__ */ jsx("div", { className: "min-h-0", children: body })
2313
+ ]
2314
+ }
2315
+ );
2316
+ }
2317
+ function readInitialTaskId() {
2318
+ if (typeof window === "undefined") return null;
2319
+ const params = new URLSearchParams(window.location.search);
2320
+ return params.get("task") ?? params.get("taskId");
2321
+ }
2322
+ const MOBILE_QUERY = "(max-width: 767px)";
2323
+ function useIsMobile() {
2324
+ const [isMobile, setIsMobile] = useState(() => {
2325
+ if (typeof window === "undefined" || !window.matchMedia) return false;
2326
+ return window.matchMedia(MOBILE_QUERY).matches;
2327
+ });
2328
+ useEffect(() => {
2329
+ if (typeof window === "undefined" || !window.matchMedia) return;
2330
+ const mql = window.matchMedia(MOBILE_QUERY);
2331
+ const onChange = (event) => setIsMobile(event.matches);
2332
+ setIsMobile(mql.matches);
2333
+ mql.addEventListener("change", onChange);
2334
+ return () => mql.removeEventListener("change", onChange);
2335
+ }, []);
2336
+ return isMobile;
2337
+ }
2338
+ const INSPECTOR_DRAWER_STYLE = {
2339
+ position: "absolute",
2340
+ insetBlock: 0,
2341
+ right: 0,
2342
+ zIndex: 30,
2343
+ width: "86%",
2344
+ maxWidth: "22rem",
2345
+ boxShadow: "0 10px 30px rgba(0, 0, 0, 0.45)"
2346
+ };
2347
+ const HIDDEN_STYLE = { display: "none" };
2348
+ function TimelineHeader({
2349
+ detail,
2350
+ isMobile,
2351
+ onBack,
2352
+ onOpenInspector,
2353
+ t
2354
+ }) {
2355
+ const statusDot = /* @__PURE__ */ jsx(
2356
+ StatusGlyph,
2357
+ {
2358
+ status: detail.status,
2359
+ paused: detail.paused,
2360
+ t,
2361
+ size: "h-4 w-4"
2362
+ }
2363
+ );
2364
+ const title = /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-sm font-semibold text-txt", children: detail.title });
2365
+ const pausedLabel = t("orchestrator.status.paused", {
2366
+ defaultValue: "Paused"
2367
+ });
2368
+ const pausedBadge = detail.paused ? /* @__PURE__ */ jsx(
2369
+ "span",
2370
+ {
2371
+ className: "inline-flex shrink-0 text-warn",
2372
+ title: pausedLabel,
2373
+ "aria-label": pausedLabel,
2374
+ role: "img",
2375
+ children: /* @__PURE__ */ jsx(Pause, { className: "h-3.5 w-3.5", "aria-hidden": true })
2376
+ }
2377
+ ) : null;
2378
+ const detailsLabel = t("orchestrator.action.details", {
2379
+ defaultValue: "Details"
2380
+ });
2381
+ const backLabel = t("orchestrator.action.backToList", {
2382
+ defaultValue: "Back to tasks"
2383
+ });
2384
+ const { ref: backRef, agentProps: backAgentProps } = useAgentElement({
2385
+ id: "timeline-back",
2386
+ role: "button",
2387
+ label: backLabel,
2388
+ group: "orchestrator-timeline",
2389
+ description: "Go back to the task list"
2390
+ });
2391
+ const { ref: detailsRef, agentProps: detailsAgentProps } = useAgentElement({
2392
+ id: "timeline-open-inspector",
2393
+ role: "button",
2394
+ label: detailsLabel,
2395
+ group: "orchestrator-timeline",
2396
+ description: "Open the task details panel"
2397
+ });
2398
+ if (isMobile) {
2399
+ return /* @__PURE__ */ jsxs("div", { className: "px-3 py-2", children: [
2400
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2401
+ /* @__PURE__ */ jsx(
2402
+ "button",
2403
+ {
2404
+ ref: backRef,
2405
+ type: "button",
2406
+ onClick: onBack,
2407
+ className: "-ml-1 shrink-0 p-1 text-muted transition-colors hover:text-txt",
2408
+ "aria-label": backLabel,
2409
+ "data-testid": "orchestrator-back",
2410
+ ...backAgentProps,
2411
+ children: /* @__PURE__ */ jsx(ArrowLeft, { className: "h-4 w-4" })
2412
+ }
2413
+ ),
2414
+ statusDot,
2415
+ title,
2416
+ /* @__PURE__ */ jsx(
2417
+ "button",
2418
+ {
2419
+ ref: detailsRef,
2420
+ type: "button",
2421
+ onClick: onOpenInspector,
2422
+ className: "shrink-0 p-1 text-muted transition-colors hover:text-txt",
2423
+ "aria-label": detailsLabel,
2424
+ title: detailsLabel,
2425
+ "data-testid": "orchestrator-open-inspector",
2426
+ ...detailsAgentProps,
2427
+ children: /* @__PURE__ */ jsx(PanelRightOpen, { className: "h-4 w-4", "aria-hidden": true })
2428
+ }
2429
+ )
2430
+ ] }),
2431
+ pausedBadge ? /* @__PURE__ */ jsx("div", { className: "mt-1.5 flex items-center gap-1.5", children: pausedBadge }) : null
2432
+ ] });
2433
+ }
2434
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 px-4 py-2.5", children: [
2435
+ /* @__PURE__ */ jsx(BackChip, { label: backLabel, onClick: onBack, testId: "orchestrator-back" }),
2436
+ statusDot,
2437
+ title,
2438
+ pausedBadge,
2439
+ /* @__PURE__ */ jsx(
2440
+ "button",
2441
+ {
2442
+ ref: detailsRef,
2443
+ type: "button",
2444
+ onClick: onOpenInspector,
2445
+ className: "shrink-0 p-1 text-muted transition-colors hover:text-txt",
2446
+ "aria-label": detailsLabel,
2447
+ title: detailsLabel,
2448
+ "data-testid": "orchestrator-open-inspector",
2449
+ ...detailsAgentProps,
2450
+ children: /* @__PURE__ */ jsx(PanelRightOpen, { className: "h-4 w-4", "aria-hidden": true })
2451
+ }
2452
+ )
2453
+ ] });
2454
+ }
2455
+ function OrchestratorWorkbench() {
2456
+ const {
2457
+ t: appT,
2458
+ uiLanguage,
2459
+ copyToClipboard,
2460
+ agentStatus
2461
+ } = useAppSelectorShallow((s) => ({
2462
+ t: s.t,
2463
+ uiLanguage: s.uiLanguage,
2464
+ copyToClipboard: s.copyToClipboard,
2465
+ agentStatus: s.agentStatus
2466
+ }));
2467
+ const t = appT ?? fallbackTranslate;
2468
+ const locale = typeof uiLanguage === "string" ? uiLanguage : void 0;
2469
+ const mainAgentName = typeof agentStatus?.agentName === "string" ? agentStatus.agentName : void 0;
2470
+ const [status, setStatus] = useState(
2471
+ null
2472
+ );
2473
+ const [tasks, setTasks] = useState([]);
2474
+ const [selectedId, setSelectedId] = useState(
2475
+ readInitialTaskId
2476
+ );
2477
+ const [detail, setDetail] = useState(
2478
+ null
2479
+ );
2480
+ const [messages, setMessages] = useState([]);
2481
+ const [events, setEvents] = useState([]);
2482
+ const [timelineCursor, setTimelineCursor] = useState(null);
2483
+ const [search, setSearch] = useState("");
2484
+ const [statusFilter, setStatusFilter] = useState("all");
2485
+ const [showArchived, setShowArchived] = useState(false);
2486
+ const [addAgentOpen, setAddAgentOpen] = useState(false);
2487
+ const [inspectorOpen, setInspectorOpen] = useState(false);
2488
+ const [detailDrawer, setDetailDrawer] = useState(null);
2489
+ const [loading, setLoading] = useState(true);
2490
+ const [mutating, setMutating] = useState(false);
2491
+ const [loadError, setLoadError] = useState(null);
2492
+ const [backendAbsent, setBackendAbsent] = useState(false);
2493
+ const [actionError, setActionError] = useState(null);
2494
+ const isMobile = useIsMobile();
2495
+ const deferredSearch = useDeferredValue(search.trim());
2496
+ const detailReqRef = useRef(0);
2497
+ const selectedIdRef = useRef(selectedId);
2498
+ selectedIdRef.current = selectedId;
2499
+ const listRef = useRef(null);
2500
+ const stickToBottomRef = useRef(true);
2501
+ const handleListScroll = useCallback((event) => {
2502
+ const el = event.currentTarget;
2503
+ stickToBottomRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < 80;
2504
+ }, []);
2505
+ const fetchTasksAndStatus = useCallback(
2506
+ async (silent) => {
2507
+ if (!silent) setLoading(true);
2508
+ try {
2509
+ const [nextStatus, nextTasks] = await Promise.all([
2510
+ client.getOrchestratorStatus(),
2511
+ client.listCodingAgentTaskThreads({
2512
+ includeArchived: showArchived,
2513
+ status: statusFilter === "all" ? void 0 : statusFilter,
2514
+ search: deferredSearch || void 0,
2515
+ limit: TASK_LIST_LIMIT
2516
+ })
2517
+ ]);
2518
+ setStatus(nextStatus);
2519
+ setTasks(nextTasks);
2520
+ setLoadError(null);
2521
+ setBackendAbsent(false);
2522
+ } catch (error) {
2523
+ if (!silent) {
2524
+ if (isOrchestratorBackendAbsent(error)) {
2525
+ setBackendAbsent(true);
2526
+ setLoadError(null);
2527
+ } else {
2528
+ setBackendAbsent(false);
2529
+ setLoadError(
2530
+ getClientErrorMessage(
2531
+ error,
2532
+ t("orchestrator.loadFailed", {
2533
+ defaultValue: "Failed to load orchestrator state."
2534
+ })
2535
+ )
2536
+ );
2537
+ }
2538
+ }
2539
+ } finally {
2540
+ if (!silent) setLoading(false);
2541
+ }
2542
+ },
2543
+ [deferredSearch, showArchived, statusFilter, t]
2544
+ );
2545
+ const fetchDetail = useCallback(async (id, reset) => {
2546
+ const token = ++detailReqRef.current;
2547
+ const [nextDetail, timelinePage] = await Promise.all([
2548
+ client.getCodingAgentTaskThread(id),
2549
+ client.listOrchestratorTaskTimeline(id, { limit: TIMELINE_PAGE_LIMIT })
2550
+ ]);
2551
+ if (token !== detailReqRef.current || id !== selectedIdRef.current) return;
2552
+ const timeline = splitTimelineItems(timelinePage.items);
2553
+ setDetail(nextDetail);
2554
+ if (reset) {
2555
+ setMessages(mergeById([], timeline.messages));
2556
+ setEvents(mergeById([], timeline.events));
2557
+ setTimelineCursor(timelinePage.nextCursor);
2558
+ } else {
2559
+ setMessages((prev) => mergeById(prev, timeline.messages));
2560
+ setEvents((prev) => mergeById(prev, timeline.events));
2561
+ setTimelineCursor((prev) => prev ?? timelinePage.nextCursor);
2562
+ }
2563
+ }, []);
2564
+ useEffect(() => {
2565
+ void fetchTasksAndStatus(false);
2566
+ const timer = window.setInterval(
2567
+ () => void fetchTasksAndStatus(true),
2568
+ POLL_INTERVAL_MS
2569
+ );
2570
+ return () => window.clearInterval(timer);
2571
+ }, [fetchTasksAndStatus]);
2572
+ const detailPollMs = detail !== null && (detail.activeSessionCount > 0 || detail.status === "active" || detail.status === "validating") ? ACTIVE_POLL_INTERVAL_MS : POLL_INTERVAL_MS;
2573
+ useEffect(() => {
2574
+ setInspectorOpen(false);
2575
+ setAddAgentOpen(false);
2576
+ setDetailDrawer(null);
2577
+ stickToBottomRef.current = true;
2578
+ if (!selectedId) {
2579
+ setDetail(null);
2580
+ setMessages([]);
2581
+ setEvents([]);
2582
+ setTimelineCursor(null);
2583
+ return;
2584
+ }
2585
+ void fetchDetail(selectedId, true).catch((err) => {
2586
+ console.error("[OrchestratorWorkbench] fetchDetail (initial)", { err });
2587
+ });
2588
+ }, [selectedId, fetchDetail]);
2589
+ useEffect(() => {
2590
+ if (!selectedId) return;
2591
+ const timer = window.setInterval(
2592
+ () => void fetchDetail(selectedId, false).catch((err) => {
2593
+ console.error("[OrchestratorWorkbench] fetchDetail (poll)", { err });
2594
+ }),
2595
+ detailPollMs
2596
+ );
2597
+ return () => window.clearInterval(timer);
2598
+ }, [selectedId, detailPollMs, fetchDetail]);
2599
+ const refetchTimerRef = useRef(null);
2600
+ const scheduleRefetch = useCallback(() => {
2601
+ if (refetchTimerRef.current != null) return;
2602
+ refetchTimerRef.current = window.setTimeout(() => {
2603
+ refetchTimerRef.current = null;
2604
+ const current = selectedIdRef.current;
2605
+ if (current)
2606
+ void fetchDetail(current, false).catch((err) => {
2607
+ console.error("[OrchestratorWorkbench] fetchDetail (debounced)", {
2608
+ err
2609
+ });
2610
+ });
2611
+ }, 150);
2612
+ }, [fetchDetail]);
2613
+ useEffect(() => {
2614
+ if (!selectedId) return;
2615
+ const unsubscribe = client.streamOrchestratorTask(
2616
+ selectedId,
2617
+ scheduleRefetch
2618
+ );
2619
+ return () => {
2620
+ unsubscribe();
2621
+ if (refetchTimerRef.current != null) {
2622
+ window.clearTimeout(refetchTimerRef.current);
2623
+ refetchTimerRef.current = null;
2624
+ }
2625
+ };
2626
+ }, [selectedId, scheduleRefetch]);
2627
+ const runMutation = useCallback(
2628
+ async (fn) => {
2629
+ setMutating(true);
2630
+ setActionError(null);
2631
+ try {
2632
+ await fn();
2633
+ await fetchTasksAndStatus(true);
2634
+ const current = selectedIdRef.current;
2635
+ if (current)
2636
+ await fetchDetail(current, false).catch((err) => {
2637
+ console.error("[OrchestratorWorkbench] fetchDetail (mutation)", {
2638
+ err
2639
+ });
2640
+ });
2641
+ } catch (error) {
2642
+ setActionError(
2643
+ getClientErrorMessage(
2644
+ error,
2645
+ t("orchestrator.actionFailed", { defaultValue: "Action failed." })
2646
+ )
2647
+ );
2648
+ } finally {
2649
+ setMutating(false);
2650
+ }
2651
+ },
2652
+ [fetchTasksAndStatus, fetchDetail, t]
2653
+ );
2654
+ const loadOlderTimeline = useCallback(async () => {
2655
+ const current = selectedIdRef.current;
2656
+ if (!current || !timelineCursor) return;
2657
+ const page = await client.listOrchestratorTaskTimeline(current, {
2658
+ cursor: timelineCursor,
2659
+ limit: TIMELINE_PAGE_LIMIT
2660
+ });
2661
+ if (current !== selectedIdRef.current) return;
2662
+ const timeline = splitTimelineItems(page.items);
2663
+ setMessages((prev) => mergeById(prev, timeline.messages));
2664
+ setEvents((prev) => mergeById(prev, timeline.events));
2665
+ setTimelineCursor(page.nextCursor);
2666
+ }, [timelineCursor]);
2667
+ const handleStopActive = useCallback(() => {
2668
+ const current = detail;
2669
+ if (!current) return;
2670
+ const targets = current.sessions.filter(
2671
+ (session) => session.sessionId && session.stoppedAt == null && session.status !== "completed"
2672
+ );
2673
+ if (targets.length === 0) return;
2674
+ void runMutation(async () => {
2675
+ for (const session of targets) {
2676
+ await client.stopOrchestratorAgent(current.id, session.sessionId);
2677
+ }
2678
+ });
2679
+ }, [detail, runMutation]);
2680
+ const escStateRef = useRef({
2681
+ addAgentOpen,
2682
+ inspectorOpen,
2683
+ detailDrawer,
2684
+ stop: handleStopActive
2685
+ });
2686
+ escStateRef.current = {
2687
+ addAgentOpen,
2688
+ inspectorOpen,
2689
+ detailDrawer,
2690
+ stop: handleStopActive
2691
+ };
2692
+ useEffect(() => {
2693
+ const onKey = (event) => {
2694
+ if (event.key !== "Escape") return;
2695
+ const s = escStateRef.current;
2696
+ if (s.addAgentOpen) {
2697
+ setAddAgentOpen(false);
2698
+ return;
2699
+ }
2700
+ if (s.inspectorOpen) {
2701
+ setInspectorOpen(false);
2702
+ return;
2703
+ }
2704
+ if (s.detailDrawer) {
2705
+ setDetailDrawer(null);
2706
+ return;
2707
+ }
2708
+ s.stop();
2709
+ };
2710
+ document.addEventListener("keydown", onKey);
2711
+ return () => document.removeEventListener("keydown", onKey);
2712
+ }, []);
2713
+ const handleCopyLink = useCallback(() => {
2714
+ const current = selectedIdRef.current;
2715
+ if (!current || !copyToClipboard || typeof window === "undefined") return;
2716
+ const url = `${window.location.origin}/orchestrator?task=${encodeURIComponent(current)}`;
2717
+ void copyToClipboard(url);
2718
+ }, [copyToClipboard]);
2719
+ const sessionLabelById = useMemo(() => {
2720
+ const map = /* @__PURE__ */ new Map();
2721
+ for (const session of detail?.sessions ?? []) {
2722
+ const label = session.label?.trim();
2723
+ if (session.sessionId && label) map.set(session.sessionId, label);
2724
+ }
2725
+ return map;
2726
+ }, [detail?.sessions]);
2727
+ const finishedSessionIds = useMemo(() => {
2728
+ const ids = /* @__PURE__ */ new Set();
2729
+ for (const session of detail?.sessions ?? []) {
2730
+ if (session.sessionId && (session.stoppedAt != null || session.status === "completed")) {
2731
+ ids.add(session.sessionId);
2732
+ }
2733
+ }
2734
+ return ids;
2735
+ }, [detail?.sessions]);
2736
+ const conversation = useMemo(
2737
+ () => buildConversation(
2738
+ messages,
2739
+ events,
2740
+ (message) => resolveSenderName(message, sessionLabelById, mainAgentName, t),
2741
+ finishedSessionIds
2742
+ ),
2743
+ [messages, events, sessionLabelById, mainAgentName, finishedSessionIds, t]
2744
+ );
2745
+ const selectedBlock = useMemo(() => {
2746
+ if (detailDrawer?.kind !== "block") return null;
2747
+ return conversation.find(
2748
+ (block) => blockMatchesSelection(block, detailDrawer)
2749
+ ) ?? null;
2750
+ }, [conversation, detailDrawer]);
2751
+ const selectedSession = useMemo(() => {
2752
+ if (detailDrawer?.kind !== "session" || !detail) return null;
2753
+ return detail.sessions.find(
2754
+ (session) => session.sessionId === detailDrawer.sessionId
2755
+ ) ?? null;
2756
+ }, [detail, detailDrawer]);
2757
+ const selectedBlockEvents = useMemo(() => {
2758
+ if (!detailDrawer) return [];
2759
+ if (detailDrawer.kind === "session") {
2760
+ return events.filter(
2761
+ (event) => event.sessionId === detailDrawer.sessionId
2762
+ );
2763
+ }
2764
+ const ids = new Set(detailDrawer.eventIds);
2765
+ return events.filter((event) => ids.has(event.id));
2766
+ }, [detailDrawer, events]);
2767
+ const selectedBlockMessages = useMemo(() => {
2768
+ if (!detailDrawer) return [];
2769
+ if (detailDrawer.kind === "session") {
2770
+ return messages.filter(
2771
+ (message) => message.sessionId === detailDrawer.sessionId
2772
+ );
2773
+ }
2774
+ const ids = new Set(detailDrawer.messageIds);
2775
+ return messages.filter((message) => ids.has(message.id));
2776
+ }, [detailDrawer, messages]);
2777
+ const handleSelectBlock = useCallback(
2778
+ (block) => {
2779
+ setDetailDrawer(blockSelection(block));
2780
+ if (isMobile) setInspectorOpen(true);
2781
+ },
2782
+ [isMobile]
2783
+ );
2784
+ const handleInspectSession = useCallback(
2785
+ (sessionId) => {
2786
+ setDetailDrawer({ kind: "session", sessionId });
2787
+ if (isMobile) setInspectorOpen(true);
2788
+ },
2789
+ [isMobile]
2790
+ );
2791
+ useEffect(() => {
2792
+ const el = listRef.current;
2793
+ if (el && stickToBottomRef.current) el.scrollTop = el.scrollHeight;
2794
+ }, [conversation]);
2795
+ const viewState = JSON.stringify({
2796
+ selectedId,
2797
+ taskCount: status?.taskCount ?? tasks.length,
2798
+ activeTaskCount: status?.activeTaskCount ?? 0,
2799
+ statusFilter,
2800
+ showArchived
2801
+ });
2802
+ const searchLabel = t("orchestrator.searchPlaceholder", {
2803
+ defaultValue: "Search tasks"
2804
+ });
2805
+ const showArchivedLabel = t("orchestrator.showArchived", {
2806
+ defaultValue: "Show archived"
2807
+ });
2808
+ const loadOlderLabel = t("orchestrator.loadOlder", {
2809
+ defaultValue: "Load older"
2810
+ });
2811
+ const stopLabel = t("orchestrator.action.stop", { defaultValue: "Stop" });
2812
+ const { ref: searchRef, agentProps: searchAgentProps } = useAgentElement({
2813
+ id: "rail-search",
2814
+ role: "text-input",
2815
+ label: searchLabel,
2816
+ group: "orchestrator-rail",
2817
+ description: "Filter the task list by title or request text",
2818
+ getValue: () => search,
2819
+ onFill: (value) => setSearch(value)
2820
+ });
2821
+ const { ref: showArchivedRef, agentProps: showArchivedAgentProps } = useAgentElement({
2822
+ id: "rail-show-archived",
2823
+ role: "toggle",
2824
+ label: showArchivedLabel,
2825
+ group: "orchestrator-rail",
2826
+ status: showArchived ? "active" : "inactive",
2827
+ description: "Toggle showing archived tasks in the list",
2828
+ onActivate: () => setShowArchived((value) => !value)
2829
+ });
2830
+ const { ref: loadOlderRef, agentProps: loadOlderAgentProps } = useAgentElement({
2831
+ id: "timeline-load-older",
2832
+ role: "button",
2833
+ label: loadOlderLabel,
2834
+ group: "orchestrator-timeline",
2835
+ description: "Load older entries in the task timeline"
2836
+ });
2837
+ const { ref: stopActiveRef, agentProps: stopActiveAgentProps } = useAgentElement({
2838
+ id: "timeline-stop-active",
2839
+ role: "button",
2840
+ label: stopLabel,
2841
+ group: "orchestrator-timeline",
2842
+ description: "Stop the running sub-agents on this task"
2843
+ });
2844
+ return /* @__PURE__ */ jsxs(
2845
+ "div",
2846
+ {
2847
+ className: "relative flex h-full min-h-0 w-full flex-col bg-bg text-txt",
2848
+ "data-testid": "orchestrator-workbench",
2849
+ children: [
2850
+ /* @__PURE__ */ jsx("span", { "data-view-state": viewState, hidden: true }),
2851
+ /* @__PURE__ */ jsx(
2852
+ WorkbenchHeader,
2853
+ {
2854
+ status,
2855
+ busy: mutating,
2856
+ isMobile,
2857
+ onPauseAll: () => runMutation(() => client.pauseAllOrchestratorTasks()),
2858
+ onResumeAll: () => runMutation(() => client.resumeAllOrchestratorTasks()),
2859
+ t,
2860
+ locale
2861
+ }
2862
+ ),
2863
+ backendAbsent ? /* @__PURE__ */ jsx("div", { className: "px-4 py-1.5 text-2xs text-muted", children: t("orchestrator.backendAbsent", {
2864
+ defaultValue: "Connect a cloud or desktop agent to run tasks."
2865
+ }) }) : null,
2866
+ loadError ? /* @__PURE__ */ jsx("div", { className: "bg-danger/10 px-3 py-1.5 text-xs text-danger", children: loadError }) : null,
2867
+ actionError ? /* @__PURE__ */ jsx("div", { className: "bg-danger/10 px-3 py-1.5 text-xs text-danger", children: actionError }) : null,
2868
+ /* @__PURE__ */ jsxs("div", { className: "relative flex min-h-0 flex-1 flex-col overflow-y-auto", children: [
2869
+ !selectedId ? /* @__PURE__ */ jsxs(
2870
+ "div",
2871
+ {
2872
+ className: "relative flex flex-1 flex-col gap-3 px-4 pb-28 pt-4",
2873
+ "data-testid": "orchestrator-rail",
2874
+ children: [
2875
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
2876
+ /* @__PURE__ */ jsx(
2877
+ TaskSearchInput,
2878
+ {
2879
+ value: search,
2880
+ onChange: setSearch,
2881
+ placeholder: searchLabel,
2882
+ inputRef: searchRef,
2883
+ testId: "orchestrator-search",
2884
+ className: "min-w-[12rem] flex-1",
2885
+ agentProps: searchAgentProps
2886
+ }
2887
+ ),
2888
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2889
+ /* @__PURE__ */ jsx("div", { className: "w-40", children: /* @__PURE__ */ jsx(
2890
+ FilterSelect,
2891
+ {
2892
+ status,
2893
+ active: statusFilter,
2894
+ onSelect: setStatusFilter,
2895
+ t
2896
+ }
2897
+ ) }),
2898
+ /* @__PURE__ */ jsxs(
2899
+ "button",
2900
+ {
2901
+ ref: showArchivedRef,
2902
+ type: "button",
2903
+ onClick: () => setShowArchived((value) => !value),
2904
+ "aria-pressed": showArchived,
2905
+ className: `inline-flex h-9 items-center gap-2 px-2 text-xs font-medium transition-colors ${showArchived ? "text-accent" : "text-muted hover:text-txt"}`,
2906
+ "data-testid": "orchestrator-show-archived",
2907
+ ...showArchivedAgentProps,
2908
+ children: [
2909
+ /* @__PURE__ */ jsx(Archive, { className: "h-3.5 w-3.5" }),
2910
+ showArchivedLabel
2911
+ ]
2912
+ }
2913
+ )
2914
+ ] })
2915
+ ] }),
2916
+ tasks.length === 0 ? loading ? /* @__PURE__ */ jsx("p", { className: "p-2 text-sm text-muted", children: t("orchestrator.loadingTasks", {
2917
+ defaultValue: "Loading"
2918
+ }) }) : /* @__PURE__ */ jsx(
2919
+ TaskEmptyState,
2920
+ {
2921
+ title: t("orchestrator.empty.title", {
2922
+ defaultValue: "None"
2923
+ }),
2924
+ hint: t("orchestrator.empty.hint", {
2925
+ defaultValue: "Ask me to start a coding task and it will appear here."
2926
+ })
2927
+ }
2928
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
2929
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2.5", children: tasks.map((thread) => /* @__PURE__ */ jsx(
2930
+ TaskCard,
2931
+ {
2932
+ id: thread.id,
2933
+ title: thread.title,
2934
+ subtitle: thread.summary || thread.originalRequest,
2935
+ status: thread.status,
2936
+ chips: orchestratorTaskChips(thread, t, locale),
2937
+ onOpen: (id) => setSelectedId(id),
2938
+ t
2939
+ },
2940
+ thread.id
2941
+ )) }),
2942
+ tasks.length < 4 ? /* @__PURE__ */ jsx(SparseWatermark, { icon: Layers }) : null
2943
+ ] })
2944
+ ]
2945
+ }
2946
+ ) : null,
2947
+ /* @__PURE__ */ jsx(
2948
+ "main",
2949
+ {
2950
+ className: "flex min-h-0 min-w-0 flex-1 flex-col bg-bg",
2951
+ "data-testid": "orchestrator-timeline",
2952
+ style: selectedId ? void 0 : HIDDEN_STYLE,
2953
+ children: detail ? /* @__PURE__ */ jsxs(Fragment, { children: [
2954
+ /* @__PURE__ */ jsx(
2955
+ TimelineHeader,
2956
+ {
2957
+ detail,
2958
+ isMobile,
2959
+ onBack: () => setSelectedId(null),
2960
+ onOpenInspector: () => {
2961
+ setDetailDrawer(null);
2962
+ setInspectorOpen(true);
2963
+ },
2964
+ t
2965
+ }
2966
+ ),
2967
+ /* @__PURE__ */ jsxs(
2968
+ "div",
2969
+ {
2970
+ ref: listRef,
2971
+ onScroll: handleListScroll,
2972
+ className: "min-h-0 flex-1 space-y-3 overflow-y-auto px-4 py-4",
2973
+ "data-testid": "orchestrator-message-list",
2974
+ children: [
2975
+ timelineCursor ? /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxs(
2976
+ "button",
2977
+ {
2978
+ ref: loadOlderRef,
2979
+ type: "button",
2980
+ onClick: () => void loadOlderTimeline(),
2981
+ className: "flex items-center gap-1 px-1 py-0.5 text-2xs text-muted transition-colors hover:text-txt",
2982
+ "data-testid": "orchestrator-load-older",
2983
+ "aria-label": loadOlderLabel,
2984
+ ...loadOlderAgentProps,
2985
+ children: [
2986
+ /* @__PURE__ */ jsx(ArrowDownToLine, { className: "h-3 w-3" }),
2987
+ loadOlderLabel
2988
+ ]
2989
+ }
2990
+ ) }) : null,
2991
+ conversation.length === 0 ? /* @__PURE__ */ jsx("p", { className: "p-4 text-center text-xs text-muted", children: t("orchestrator.noMessages", {
2992
+ defaultValue: "No messages yet."
2993
+ }) }) : conversation.map((block) => {
2994
+ const selected = detailDrawer?.kind === "block" && blockMatchesSelection(block, detailDrawer);
2995
+ return /* @__PURE__ */ jsxs(
2996
+ "div",
2997
+ {
2998
+ className: `group flex gap-1.5 transition-colors ${selected ? "text-accent" : "hover:bg-bg-hover/30"}`,
2999
+ "data-testid": "orchestrator-conversation-block",
3000
+ children: [
3001
+ /* @__PURE__ */ jsx(
3002
+ "button",
3003
+ {
3004
+ type: "button",
3005
+ onClick: () => handleSelectBlock(block),
3006
+ className: "mt-1.5 flex h-6 w-6 shrink-0 items-center justify-center text-muted opacity-0 transition-colors hover:text-txt focus:opacity-100 group-hover:opacity-100 group-focus-within:opacity-100",
3007
+ "aria-label": t("orchestrator.action.inspectBlock", {
3008
+ defaultValue: `Inspect ${blockTitle(block, t)}`
3009
+ }),
3010
+ title: t("orchestrator.action.inspectBlock", {
3011
+ defaultValue: `Inspect ${blockTitle(block, t)}`
3012
+ }),
3013
+ "data-testid": "orchestrator-inspect-block",
3014
+ children: /* @__PURE__ */ jsx(PanelRightOpen, { className: "h-3.5 w-3.5" })
3015
+ }
3016
+ ),
3017
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx(
3018
+ ConversationBlockView,
3019
+ {
3020
+ block,
3021
+ locale,
3022
+ onInspect: () => handleSelectBlock(block)
3023
+ }
3024
+ ) })
3025
+ ]
3026
+ },
3027
+ block.key
3028
+ );
3029
+ })
3030
+ ]
3031
+ }
3032
+ ),
3033
+ detail.activeSessionCount > 0 ? /* @__PURE__ */ jsxs(
3034
+ "div",
3035
+ {
3036
+ className: "flex items-center justify-between gap-2 bg-warn/5 px-3 py-1.5",
3037
+ "data-testid": "orchestrator-running-bar",
3038
+ children: [
3039
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 text-2xs font-medium text-warn", children: [
3040
+ /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 animate-pulse rounded-full bg-warn" }),
3041
+ t("orchestrator.agentsWorking", {
3042
+ defaultValue: "Agent working\u2026"
3043
+ })
3044
+ ] }),
3045
+ /* @__PURE__ */ jsxs(
3046
+ "button",
3047
+ {
3048
+ ref: stopActiveRef,
3049
+ type: "button",
3050
+ onClick: handleStopActive,
3051
+ disabled: mutating,
3052
+ className: "flex items-center gap-1 px-1 py-0.5 text-2xs text-txt transition-colors hover:text-danger disabled:opacity-50",
3053
+ "data-testid": "orchestrator-stop-active",
3054
+ "aria-label": stopLabel,
3055
+ ...stopActiveAgentProps,
3056
+ children: [
3057
+ /* @__PURE__ */ jsx(CircleStop, { className: "h-3 w-3" }),
3058
+ stopLabel,
3059
+ /* @__PURE__ */ jsx("kbd", { className: "ml-0.5 px-1 text-[0.9em] text-muted", children: "Esc" })
3060
+ ]
3061
+ }
3062
+ )
3063
+ ]
3064
+ }
3065
+ ) : null,
3066
+ /* @__PURE__ */ jsx("div", { className: "pb-24" })
3067
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3068
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 px-4 py-2.5", children: [
3069
+ /* @__PURE__ */ jsx(
3070
+ BackChip,
3071
+ {
3072
+ label: t("orchestrator.action.backToList", {
3073
+ defaultValue: "Tasks"
3074
+ }),
3075
+ onClick: () => setSelectedId(null),
3076
+ testId: "orchestrator-back-loading"
3077
+ }
3078
+ ),
3079
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-muted", children: t("orchestrator.loadingTask", {
3080
+ defaultValue: "Loading task\u2026"
3081
+ }) })
3082
+ ] }),
3083
+ /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center p-6", children: /* @__PURE__ */ jsx("p", { className: "text-xs text-muted", children: t("orchestrator.loadingTask", {
3084
+ defaultValue: "Loading task\u2026"
3085
+ }) }) })
3086
+ ] })
3087
+ }
3088
+ ),
3089
+ detail && isMobile && inspectorOpen ? /* @__PURE__ */ jsx(
3090
+ "button",
3091
+ {
3092
+ type: "button",
3093
+ "aria-label": t("orchestrator.action.closeDetails", {
3094
+ defaultValue: "Close details"
3095
+ }),
3096
+ onClick: () => {
3097
+ setInspectorOpen(false);
3098
+ setDetailDrawer(null);
3099
+ },
3100
+ className: "absolute inset-0 z-20 bg-black/40",
3101
+ "data-testid": "orchestrator-inspector-backdrop"
3102
+ }
3103
+ ) : null,
3104
+ detail && detailDrawer ? /* @__PURE__ */ jsx(
3105
+ OperatorDetailDrawer,
3106
+ {
3107
+ selection: detailDrawer,
3108
+ block: selectedBlock,
3109
+ session: selectedSession,
3110
+ events: selectedBlockEvents,
3111
+ messages: selectedBlockMessages,
3112
+ taskUsage: detail.usage,
3113
+ busy: mutating,
3114
+ className: "flex",
3115
+ style: isMobile ? inspectorOpen ? INSPECTOR_DRAWER_STYLE : HIDDEN_STYLE : void 0,
3116
+ onClose: () => {
3117
+ setDetailDrawer(null);
3118
+ if (isMobile) setInspectorOpen(false);
3119
+ },
3120
+ onRetry: (input) => runMutation(
3121
+ () => client.retryOrchestratorTaskTurn(detail.id, input)
3122
+ ),
3123
+ onRerun: (input) => runMutation(
3124
+ () => client.rerunOrchestratorTaskFromEvent(detail.id, input)
3125
+ ),
3126
+ t,
3127
+ locale
3128
+ },
3129
+ blockSelectionKey(detailDrawer)
3130
+ ) : detail ? /* @__PURE__ */ jsx(
3131
+ TaskInspector,
3132
+ {
3133
+ detail,
3134
+ className: "flex",
3135
+ style: isMobile ? inspectorOpen ? INSPECTOR_DRAWER_STYLE : HIDDEN_STYLE : void 0,
3136
+ onClose: isMobile ? () => setInspectorOpen(false) : void 0,
3137
+ busy: mutating,
3138
+ addAgentOpen,
3139
+ onPause: () => runMutation(() => client.pauseOrchestratorTask(detail.id)),
3140
+ onResume: () => runMutation(() => client.resumeOrchestratorTask(detail.id)),
3141
+ onArchive: () => runMutation(async () => {
3142
+ await client.archiveCodingAgentTaskThread(detail.id);
3143
+ if (!showArchived) setSelectedId(null);
3144
+ }),
3145
+ onReopen: () => runMutation(() => client.reopenCodingAgentTaskThread(detail.id)),
3146
+ onDelete: () => runMutation(async () => {
3147
+ await client.deleteOrchestratorTask(detail.id);
3148
+ setSelectedId(null);
3149
+ }),
3150
+ onFork: () => runMutation(async () => {
3151
+ const forked = await client.forkOrchestratorTask(detail.id);
3152
+ if (forked) setSelectedId(forked.id);
3153
+ }),
3154
+ onRestart: () => {
3155
+ const confirmed = typeof window === "undefined" || window.confirm(
3156
+ t("orchestrator.confirmRestart", {
3157
+ defaultValue: "Restart this task with a fresh worker? Active agents will be stopped first."
3158
+ })
3159
+ );
3160
+ if (!confirmed) return;
3161
+ runMutation(
3162
+ () => client.restartOrchestratorTask(detail.id, { stopActive: true })
3163
+ );
3164
+ },
3165
+ onRestartWithEditedPlan: (input) => runMutation(
3166
+ () => client.restartOrchestratorTaskWithEditedPlan(detail.id, input)
3167
+ ),
3168
+ onValidate: (passed) => runMutation(
3169
+ () => client.validateOrchestratorTask(detail.id, {
3170
+ passed,
3171
+ humanOverride: true
3172
+ })
3173
+ ),
3174
+ onSetPriority: (priority) => runMutation(
3175
+ () => client.updateOrchestratorTask(detail.id, { priority })
3176
+ ),
3177
+ onToggleAddAgent: () => setAddAgentOpen((prev) => !prev),
3178
+ onAddAgent: (input) => runMutation(async () => {
3179
+ await client.addOrchestratorAgent(detail.id, input);
3180
+ setAddAgentOpen(false);
3181
+ }),
3182
+ onInspectSession: handleInspectSession,
3183
+ onStopAgent: (sessionId) => runMutation(
3184
+ () => client.stopOrchestratorAgent(detail.id, sessionId)
3185
+ ),
3186
+ onCopyLink: handleCopyLink,
3187
+ t,
3188
+ locale
3189
+ }
3190
+ ) : null
3191
+ ] })
3192
+ ]
3193
+ }
3194
+ );
3195
+ }
3196
+ export {
3197
+ OrchestratorWorkbench,
3198
+ TaskInspector
3199
+ };
3200
+ //# sourceMappingURL=OrchestratorWorkbench.js.map