@mcp-b/react-components 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/{AgentChat-B9LD_WF1.d.ts → AgentChat-CRmsKg9B.d.ts} +10 -6
  2. package/dist/{BrowserLiveView-ZktFZiMD.d.ts → BrowserLiveView-BV5qgFgI.d.ts} +11 -10
  3. package/dist/{BrowserReplay-6G-mnwcn.d.ts → BrowserReplay-xWvRjRAC.d.ts} +2 -1
  4. package/dist/{Menu-B5vDqZe_.d.ts → Menu-BLhnNnRK.d.ts} +3 -0
  5. package/dist/{ModelSelector-DXtpcRmT.js → ModelSelector-BB-xB-Es.js} +16 -13
  6. package/dist/{PromptInput-D6-G24p7.d.ts → PromptInput-IyM6ZETj.d.ts} +13 -45
  7. package/dist/ThinkChat-B3wYahor.js +190 -0
  8. package/dist/{ThinkChatActivity-B2UD95My.d.ts → ThinkChatActivity-F4KFPcJA.d.ts} +4 -4
  9. package/dist/ThinkChatContext-C3Eb1Xpi.d.ts +16 -0
  10. package/dist/{ThinkChatToolParts-DnUiZgCC.d.ts → ThinkChatToolParts-Cv4o7j6F.d.ts} +13 -5
  11. package/dist/components/AgentChat.d.ts +1 -1
  12. package/dist/components/AgentChat.js +57 -23
  13. package/dist/components/AgentConnection.js +4 -20
  14. package/dist/components/Badge.js +1 -15
  15. package/dist/components/BrowserLiveView.d.ts +1 -1
  16. package/dist/components/BrowserLiveView.js +14 -10
  17. package/dist/components/BrowserReplay.d.ts +1 -1
  18. package/dist/components/BrowserRunPanel.js +91 -55
  19. package/dist/components/ChatGptSignInScreen.js +1 -1
  20. package/dist/components/CodexModelSelector.d.ts +63 -0
  21. package/dist/components/CodexModelSelector.js +148 -0
  22. package/dist/components/Commit.d.ts +2 -2
  23. package/dist/components/Commit.js +10 -22
  24. package/dist/components/Conversation.d.ts +2 -6
  25. package/dist/components/Conversation.js +2 -3
  26. package/dist/components/EnvironmentVariables.d.ts +2 -7
  27. package/dist/components/EnvironmentVariables.js +10 -33
  28. package/dist/components/Marker.js +1 -15
  29. package/dist/components/McpServerPicker.d.ts +31 -6
  30. package/dist/components/McpServerPicker.js +129 -67
  31. package/dist/components/Menu.d.ts +1 -1
  32. package/dist/components/Menu.js +2 -2
  33. package/dist/components/Message.d.ts +3 -2
  34. package/dist/components/Message.js +10 -3
  35. package/dist/components/ModelSelector.d.ts +11 -8
  36. package/dist/components/ModelSelector.js +1 -1
  37. package/dist/components/PromptInput.d.ts +2 -2
  38. package/dist/components/PromptInput.js +49 -98
  39. package/dist/components/Queue.d.ts +1 -12
  40. package/dist/components/Reasoning.js +2 -15
  41. package/dist/components/Stepper.js +1 -7
  42. package/dist/components/ThinkChat.d.ts +30 -30
  43. package/dist/components/ThinkChat.js +1 -152
  44. package/dist/components/ThinkChatActivity.d.ts +2 -2
  45. package/dist/components/ThinkChatActivity.js +3 -10
  46. package/dist/components/ThinkChatContext.d.ts +2 -2
  47. package/dist/components/ThinkChatContext.js +9 -2
  48. package/dist/components/ThinkChatToolPanels.d.ts +1 -1
  49. package/dist/components/ThinkChatToolPanels.js +19 -7
  50. package/dist/components/ThinkChatToolParts.d.ts +1 -1
  51. package/dist/components/ThinkExecutionWorkbench.d.ts +10 -7
  52. package/dist/components/ThinkExecutionWorkbench.js +382 -359
  53. package/dist/components/ThinkFullApp.d.ts +16 -13
  54. package/dist/components/ThinkFullApp.js +28 -18
  55. package/dist/components/ThinkThreadPicker.d.ts +1 -31
  56. package/dist/components/ThinkThreadPicker.js +32 -171
  57. package/dist/components/ThinkThreadShell.d.ts +35 -0
  58. package/dist/components/ThinkThreadShell.js +146 -0
  59. package/dist/components/WorkspaceExplorer.js +3 -1
  60. package/dist/merge-refs-DF_7w2x_.js +18 -0
  61. package/dist/styles/agent-chat.css +4 -1
  62. package/dist/styles/browser-run-panel.css +57 -110
  63. package/dist/styles/codex-model-selector.css +197 -0
  64. package/dist/styles/index.css +4 -1
  65. package/dist/styles/mcp-server-picker.css +62 -15
  66. package/dist/styles/message.css +1 -1
  67. package/dist/styles/model-selector.css +20 -8
  68. package/dist/styles/prompt-input.css +5 -0
  69. package/dist/styles/streamdown.css +2 -48
  70. package/dist/styles/think-execution-workbench.css +337 -0
  71. package/dist/styles/think-full-app.css +15 -390
  72. package/dist/styles/think-resize-handle.css +33 -0
  73. package/dist/styles/think-thread-picker.css +0 -238
  74. package/dist/styles/think-thread-shell.css +219 -0
  75. package/dist/styles/workspace-explorer.css +8 -2
  76. package/dist/summarize-tool-input-BUJsENSn.js +12 -0
  77. package/package.json +2 -2
  78. package/dist/ThinkChatContext-BLAAb8J5.d.ts +0 -28
  79. package/dist/components/McpServerConnections.d.ts +0 -41
  80. package/dist/components/McpServerConnections.js +0 -188
  81. package/dist/components/useStickToBottom.d.ts +0 -33
  82. package/dist/components/useStickToBottom.js +0 -107
  83. package/dist/styles/mcp-server-connections.css +0 -145
@@ -1,7 +1,9 @@
1
1
  import { t as cx } from "../class-names-9UtZwb0J.js";
2
- import { A as SearchIcon, D as PlayIcon, L as XIcon, N as SquareIcon, T as PauseIcon, k as RefreshIcon, u as DotIcon } from "../icons-D2Q5n5C1.js";
2
+ import { L as XIcon, M as SpinnerIcon, P as TerminalIcon, T as PauseIcon, a as CheckIcon, f as EllipsisIcon, h as FileIcon, k as RefreshIcon, t as ArrowDownIcon, y as InfoIcon } from "../icons-D2Q5n5C1.js";
3
+ import { Activity, ActivityContent, ActivityItem, ActivityItemGroup, ActivityOutput, ActivityPanel, ActivityTrigger } from "./Activity.js";
3
4
  import { Button } from "./Button.js";
4
- import { Badge } from "./Badge.js";
5
+ import { Tooltip, TooltipContent, TooltipTrigger } from "./Tooltip.js";
6
+ import { MenuContent, MenuItem, MenuRoot, MenuSeparator, MenuTrigger } from "./Menu.js";
5
7
  import * as React from "react";
6
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
9
  //#region src/components/ThinkExecutionWorkbench.tsx
@@ -11,35 +13,99 @@ function formatTimestamp(value) {
11
13
  const date = value instanceof Date ? value : new Date(value);
12
14
  return Number.isNaN(date.getTime()) ? "Not recorded" : date.toLocaleString();
13
15
  }
16
+ function formatShortTimestamp(value) {
17
+ if (value == null) return "Not recorded";
18
+ const date = value instanceof Date ? value : new Date(value);
19
+ return Number.isNaN(date.getTime()) ? "Not recorded" : date.toLocaleString(void 0, {
20
+ month: "short",
21
+ day: "numeric",
22
+ hour: "numeric",
23
+ minute: "2-digit"
24
+ });
25
+ }
14
26
  function formatJson(value) {
15
27
  return JSON.stringify(value, null, 2);
16
28
  }
17
29
  function getErrorMessage(error) {
18
30
  return error instanceof Error ? error.message : String(error);
19
31
  }
20
- function submissionStatusColor(status) {
21
- if (status === "completed") return "success";
22
- if (status === "error") return "destructive";
23
- if (status === "aborted" || status === "skipped") return "warning";
24
- if (status === "running") return "primary";
25
- return "neutral";
32
+ function TooltipIcon({ children, className, label, status, tone }) {
33
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsx("span", {
34
+ "aria-label": label,
35
+ className: cx("think-execution-workbench__icon", className),
36
+ "data-status": status,
37
+ "data-tone": tone,
38
+ role: "img",
39
+ children
40
+ }) }), /* @__PURE__ */ jsx(TooltipContent, {
41
+ side: "top",
42
+ sideOffset: 6,
43
+ children: label
44
+ })] });
26
45
  }
27
- function workflowStatusColor(status) {
28
- const value = String(status);
29
- if (value === "complete") return "success";
30
- if (value === "errored" || value === "terminated") return "destructive";
31
- if (value === "paused" || value === "waiting") return "warning";
32
- if (value === "running") return "primary";
46
+ function runId(run) {
47
+ return run.kind === "submission" ? run.record.submissionId : run.record.workflowId;
48
+ }
49
+ function runTitle(run) {
50
+ return run.kind === "submission" ? run.record.submissionId : run.record.workflowName;
51
+ }
52
+ function runSubtitle(run) {
53
+ return run.kind === "workflow" ? run.record.workflowId : void 0;
54
+ }
55
+ function runCreatedAt(run) {
56
+ return run.record.createdAt;
57
+ }
58
+ function runProgressedAt(run) {
59
+ return run.kind === "submission" ? run.record.startedAt : run.record.updatedAt;
60
+ }
61
+ function runProgressedLabel(run) {
62
+ return run.kind === "submission" ? "Started" : "Updated";
63
+ }
64
+ function runCompletedAt(run) {
65
+ return run.record.completedAt;
66
+ }
67
+ function runStatus(run) {
68
+ return String(run.record.status);
69
+ }
70
+ function formatStatus(status) {
71
+ return status.charAt(0).toUpperCase() + status.slice(1);
72
+ }
73
+ function runListTimestamp(run) {
74
+ const progressedAt = runProgressedAt(run);
75
+ return `${progressedAt ? runProgressedLabel(run) : "Created"} ${formatShortTimestamp(progressedAt ?? runCreatedAt(run))}`;
76
+ }
77
+ function runAccessibleName(run) {
78
+ const subtitle = runSubtitle(run);
79
+ return `${runTitle(run)}${subtitle ? ` ${subtitle}` : ""}. ${formatStatus(runStatus(run))}. ${runListTimestamp(run)}`;
80
+ }
81
+ function runStatusTone(run) {
82
+ const status = runStatus(run);
83
+ if (status === "completed" || status === "complete") return "success";
84
+ if (status === "error" || status === "errored" || status === "terminated") return "danger";
85
+ if (status === "aborted" || status === "skipped" || status === "paused" || status === "waiting") return "warning";
86
+ if (status === "running") return "primary";
33
87
  return "neutral";
34
88
  }
35
- function StatusBadge({ children, color }) {
36
- return /* @__PURE__ */ jsxs(Badge, {
89
+ function RunStatus({ run }) {
90
+ const status = runStatus(run);
91
+ const icon = status === "completed" || status === "complete" ? /* @__PURE__ */ jsx(CheckIcon, {}) : status === "error" || status === "errored" || status === "terminated" ? /* @__PURE__ */ jsx(XIcon, {}) : status === "aborted" || status === "skipped" || status === "paused" || status === "waiting" ? /* @__PURE__ */ jsx(PauseIcon, {}) : status === "running" ? /* @__PURE__ */ jsx(SpinnerIcon, {}) : /* @__PURE__ */ jsx(InfoIcon, {});
92
+ return /* @__PURE__ */ jsx(TooltipIcon, {
37
93
  className: "think-execution-workbench__status",
38
- color,
39
- size: "sm",
40
- children: [/* @__PURE__ */ jsx(DotIcon, {}), /* @__PURE__ */ jsx("span", { children })]
94
+ label: `Status: ${status}`,
95
+ status,
96
+ tone: runStatusTone(run),
97
+ children: icon
41
98
  });
42
99
  }
100
+ function runMetadata(run) {
101
+ return run.record.metadata;
102
+ }
103
+ function runError(run) {
104
+ return run.kind === "submission" ? run.record.error : run.record.error ? `${run.record.error.name}: ${run.record.error.message}` : void 0;
105
+ }
106
+ function isSameRun(first, second) {
107
+ return first?.kind === second.kind && runId(first) === runId(second);
108
+ }
43
109
  function isSubmissionCancelable(status) {
44
110
  return status === "pending" || status === "running";
45
111
  }
@@ -47,47 +113,260 @@ function isWorkflowTerminal(status) {
47
113
  const value = String(status);
48
114
  return value === "complete" || value === "errored" || value === "terminated";
49
115
  }
116
+ function RunActivity({ run }) {
117
+ const progressedAt = runProgressedAt(run);
118
+ const completedAt = runCompletedAt(run);
119
+ const error = runError(run);
120
+ const metadata = runMetadata(run);
121
+ return /* @__PURE__ */ jsxs(Activity, {
122
+ defaultOpen: true,
123
+ children: [/* @__PURE__ */ jsx(ActivityTrigger, {
124
+ icon: /* @__PURE__ */ jsx(TerminalIcon, {}),
125
+ label: "Activity"
126
+ }), /* @__PURE__ */ jsxs(ActivityContent, { children: [
127
+ /* @__PURE__ */ jsx(ActivityItem, {
128
+ icon: /* @__PURE__ */ jsx(ArrowDownIcon, {}),
129
+ children: `Created ${formatTimestamp(runCreatedAt(run))}`
130
+ }),
131
+ progressedAt ? /* @__PURE__ */ jsx(ActivityItem, {
132
+ icon: /* @__PURE__ */ jsx(RefreshIcon, {}),
133
+ children: `${runProgressedLabel(run)} ${formatTimestamp(progressedAt)}`
134
+ }) : null,
135
+ completedAt ? /* @__PURE__ */ jsx(ActivityItem, {
136
+ icon: /* @__PURE__ */ jsx(CheckIcon, {}),
137
+ children: `Finished ${formatTimestamp(completedAt)}`
138
+ }) : null,
139
+ error ? /* @__PURE__ */ jsx(ActivityItemGroup, {
140
+ icon: /* @__PURE__ */ jsx(XIcon, {}),
141
+ label: "Failure",
142
+ children: /* @__PURE__ */ jsx(ActivityPanel, {
143
+ label: "Error",
144
+ text: error,
145
+ status: "error",
146
+ error: true
147
+ })
148
+ }) : null,
149
+ metadata ? /* @__PURE__ */ jsx(ActivityItemGroup, {
150
+ icon: /* @__PURE__ */ jsx(InfoIcon, {}),
151
+ label: "Metadata",
152
+ children: /* @__PURE__ */ jsx(ActivityOutput, { children: formatJson(metadata) })
153
+ }) : null,
154
+ /* @__PURE__ */ jsx(ActivityItemGroup, {
155
+ icon: /* @__PURE__ */ jsx(FileIcon, {}),
156
+ label: "Raw record",
157
+ children: /* @__PURE__ */ jsx(ActivityOutput, { children: formatJson(run.record) })
158
+ })
159
+ ] })]
160
+ });
161
+ }
162
+ function RunDetail({ run }) {
163
+ if (!run) return /* @__PURE__ */ jsx("div", {
164
+ className: "think-execution-workbench__empty-detail",
165
+ role: "status",
166
+ children: /* @__PURE__ */ jsx("p", { children: "Select a run to view its activity." })
167
+ });
168
+ const id = runId(run);
169
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("header", {
170
+ className: "think-execution-workbench__detail-header",
171
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h3", { children: runTitle(run) }), run.kind === "workflow" ? /* @__PURE__ */ jsx("code", { children: id }) : null] }), /* @__PURE__ */ jsxs("div", {
172
+ className: "think-execution-workbench__detail-status",
173
+ children: [/* @__PURE__ */ jsx(RunStatus, { run }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("strong", { children: formatStatus(runStatus(run)) }), /* @__PURE__ */ jsx("span", { children: runListTimestamp(run) })] })]
174
+ })]
175
+ }), /* @__PURE__ */ jsx(RunActivity, { run })] });
176
+ }
177
+ function RunActions({ busy, onAction, run, source }) {
178
+ const id = runId(run);
179
+ const action = (key, callback) => () => onAction(key, callback);
180
+ return /* @__PURE__ */ jsxs(MenuRoot, { children: [/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsx(MenuTrigger, {
181
+ unstyled: true,
182
+ render: /* @__PURE__ */ jsx(Button, {
183
+ type: "button",
184
+ variant: "ghost",
185
+ color: "neutral",
186
+ size: "icon-xs",
187
+ className: "think-execution-workbench__run-actions",
188
+ "aria-label": `Actions for ${run.kind} ${id}`,
189
+ disabled: busy,
190
+ children: /* @__PURE__ */ jsx(EllipsisIcon, {})
191
+ })
192
+ }) }), /* @__PURE__ */ jsx(TooltipContent, {
193
+ side: "top",
194
+ sideOffset: 6,
195
+ children: "Run actions"
196
+ })] }), /* @__PURE__ */ jsx(MenuContent, {
197
+ align: "end",
198
+ children: run.kind === "submission" ? /* @__PURE__ */ jsxs(Fragment, { children: [
199
+ isSubmissionCancelable(run.record.status) ? /* @__PURE__ */ jsx(MenuItem, {
200
+ disabled: busy,
201
+ onClick: action(`cancel-submission:${id}`, () => source.cancelSubmission(id)),
202
+ children: "Cancel"
203
+ }) : null,
204
+ /* @__PURE__ */ jsx(MenuSeparator, {}),
205
+ /* @__PURE__ */ jsx(MenuItem, {
206
+ disabled: busy,
207
+ variant: "destructive",
208
+ onClick: action(`delete-submission:${id}`, () => source.deleteSubmission(id)),
209
+ children: "Delete"
210
+ })
211
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
212
+ /* @__PURE__ */ jsx(MenuItem, {
213
+ disabled: busy,
214
+ onClick: action(`sync-workflow:${id}`, () => source.getWorkflowStatus(run.record.workflowName, id)),
215
+ children: "Refresh status"
216
+ }),
217
+ runStatus(run) === "running" ? /* @__PURE__ */ jsx(MenuItem, {
218
+ disabled: busy,
219
+ onClick: action(`pause-workflow:${id}`, () => source.pauseWorkflow(id)),
220
+ children: "Pause"
221
+ }) : null,
222
+ runStatus(run) === "paused" ? /* @__PURE__ */ jsx(MenuItem, {
223
+ disabled: busy,
224
+ onClick: action(`resume-workflow:${id}`, () => source.resumeWorkflow(id)),
225
+ children: "Resume"
226
+ }) : null,
227
+ isWorkflowTerminal(run.record.status) ? /* @__PURE__ */ jsx(MenuItem, {
228
+ disabled: busy,
229
+ onClick: action(`restart-workflow:${id}`, () => source.restartWorkflow(id)),
230
+ children: "Restart"
231
+ }) : null,
232
+ runStatus(run) === "running" ? /* @__PURE__ */ jsx(MenuItem, {
233
+ disabled: busy,
234
+ variant: "destructive",
235
+ onClick: action(`terminate-workflow:${id}`, () => source.terminateWorkflow(id)),
236
+ children: "Terminate"
237
+ }) : null,
238
+ /* @__PURE__ */ jsx(MenuSeparator, {}),
239
+ /* @__PURE__ */ jsx(MenuItem, {
240
+ disabled: busy,
241
+ variant: "destructive",
242
+ onClick: action(`delete-workflow:${id}`, () => source.deleteWorkflow(id)),
243
+ children: "Delete"
244
+ })
245
+ ] })
246
+ })] });
247
+ }
248
+ /**
249
+ * Unified execution history for native Think submissions and Agents workflows.
250
+ * The workbench reads and acts through the whole upstream agent stub; it does
251
+ * not normalize execution records or own workflow authoring.
252
+ */
50
253
  function ThinkExecutionWorkbench({ backButton, className, pageSize = executionPanelPageSize, ref, source, title = "Execution", ...props }) {
51
- const inspectionId = React.useId();
52
- const submissionsId = React.useId();
53
- const executionsId = React.useId();
254
+ const runsId = React.useId();
255
+ const activeSourceRef = React.useRef(source);
256
+ const sourceGenerationRef = React.useRef(0);
257
+ const operationSequenceRef = React.useRef(0);
54
258
  const [busyAction, setBusyAction] = React.useState("refresh");
55
259
  const [error, setError] = React.useState(null);
56
- const [submissions, setSubmissions] = React.useState([]);
57
- const [workflows, setWorkflows] = React.useState(null);
58
- const [selectedSubmission, setSelectedSubmission] = React.useState(null);
59
- const [selectedWorkflow, setSelectedWorkflow] = React.useState(null);
60
- const loadData = React.useCallback(async () => {
61
- const [nextSubmissions, nextWorkflows] = await Promise.all([source.listSubmissions({ limit: pageSize }), source.getWorkflows({
260
+ const [executionData, setExecutionData] = React.useState(null);
261
+ const [selection, setSelection] = React.useState(null);
262
+ const activeExecutionData = executionData?.source === source ? executionData : null;
263
+ const selectedRun = selection?.source === source ? selection.run : null;
264
+ const runs = React.useMemo(() => [...activeExecutionData?.submissions.map((record) => ({
265
+ kind: "submission",
266
+ record
267
+ })) ?? [], ...activeExecutionData?.workflows.workflows.map((record) => ({
268
+ kind: "workflow",
269
+ record
270
+ })) ?? []].sort((first, second) => Number(runCreatedAt(second)) - Number(runCreatedAt(first))), [activeExecutionData]);
271
+ const isCurrentOperation = React.useCallback((operation) => activeSourceRef.current === operation.source && sourceGenerationRef.current === operation.generation && operationSequenceRef.current === operation.sequence, []);
272
+ const beginOperation = React.useCallback(() => {
273
+ if (activeSourceRef.current !== source) return null;
274
+ return {
275
+ generation: sourceGenerationRef.current,
276
+ sequence: ++operationSequenceRef.current,
277
+ source
278
+ };
279
+ }, [source]);
280
+ const loadData = React.useCallback(async (operation) => {
281
+ const [nextSubmissions, nextWorkflows] = await Promise.all([operation.source.listSubmissions({ limit: pageSize }), operation.source.getWorkflows({
62
282
  limit: pageSize,
63
283
  orderBy: "desc"
64
284
  })]);
65
- setSubmissions(nextSubmissions);
66
- setWorkflows(nextWorkflows);
67
- }, [pageSize, source]);
285
+ if (!isCurrentOperation(operation)) return;
286
+ setExecutionData({
287
+ source: operation.source,
288
+ submissions: nextSubmissions,
289
+ workflows: nextWorkflows
290
+ });
291
+ }, [isCurrentOperation, pageSize]);
68
292
  const refresh = React.useCallback(async () => {
293
+ const operation = beginOperation();
294
+ if (!operation) return;
69
295
  setBusyAction("refresh");
70
296
  setError(null);
71
297
  try {
72
- await loadData();
298
+ await loadData(operation);
73
299
  } catch (nextError) {
300
+ if (!isCurrentOperation(operation)) return;
74
301
  setError(getErrorMessage(nextError));
75
302
  } finally {
76
- setBusyAction(null);
303
+ if (isCurrentOperation(operation)) setBusyAction(null);
77
304
  }
78
- }, [loadData]);
305
+ }, [
306
+ beginOperation,
307
+ isCurrentOperation,
308
+ loadData
309
+ ]);
79
310
  const runAction = React.useCallback(async (key, action) => {
311
+ const operation = beginOperation();
312
+ if (!operation) return;
80
313
  setBusyAction(key);
81
314
  setError(null);
82
315
  try {
83
- await action();
84
- await loadData();
316
+ await action(operation);
317
+ if (!isCurrentOperation(operation)) return;
318
+ await loadData(operation);
85
319
  } catch (nextError) {
320
+ if (!isCurrentOperation(operation)) return;
86
321
  setError(getErrorMessage(nextError));
87
322
  } finally {
88
- setBusyAction(null);
323
+ if (isCurrentOperation(operation)) setBusyAction(null);
89
324
  }
90
- }, [loadData]);
325
+ }, [
326
+ beginOperation,
327
+ isCurrentOperation,
328
+ loadData
329
+ ]);
330
+ const selectRun = React.useCallback((run) => {
331
+ runAction(`select-${run.kind}:${runId(run)}`, async (operation) => {
332
+ if (run.kind === "submission") {
333
+ const inspection = await operation.source.inspectSubmission(run.record.submissionId);
334
+ if (!isCurrentOperation(operation)) return;
335
+ setSelection({
336
+ source: operation.source,
337
+ run: inspection ? {
338
+ kind: "submission",
339
+ record: inspection
340
+ } : run
341
+ });
342
+ return;
343
+ }
344
+ const workflow = await operation.source.getWorkflow(run.record.workflowId);
345
+ if (!isCurrentOperation(operation)) return;
346
+ setSelection({
347
+ source: operation.source,
348
+ run: workflow ? {
349
+ kind: "workflow",
350
+ record: workflow
351
+ } : run
352
+ });
353
+ });
354
+ }, [isCurrentOperation, runAction]);
355
+ React.useLayoutEffect(() => {
356
+ activeSourceRef.current = source;
357
+ sourceGenerationRef.current += 1;
358
+ operationSequenceRef.current += 1;
359
+ setExecutionData(null);
360
+ setSelection(null);
361
+ setBusyAction("refresh");
362
+ setError(null);
363
+ return () => {
364
+ if (activeSourceRef.current === source) {
365
+ sourceGenerationRef.current += 1;
366
+ operationSequenceRef.current += 1;
367
+ }
368
+ };
369
+ }, [source]);
91
370
  React.useEffect(() => {
92
371
  refresh();
93
372
  }, [refresh]);
@@ -104,17 +383,21 @@ function ThinkExecutionWorkbench({ backButton, className, pageSize = executionPa
104
383
  className: "think-execution-workbench__title",
105
384
  children: title
106
385
  })]
107
- }), /* @__PURE__ */ jsxs(Button, {
386
+ }), /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsx(Button, {
108
387
  type: "button",
109
- variant: "outline",
388
+ variant: "ghost",
110
389
  color: "neutral",
111
- size: "sm",
390
+ size: "icon-xs",
112
391
  className: "think-execution-workbench__refresh",
113
- "aria-label": "Refresh execution records",
392
+ "aria-label": "Refresh runs",
114
393
  disabled: busyAction !== null,
115
394
  onClick: () => void refresh(),
116
- children: [/* @__PURE__ */ jsx(RefreshIcon, {}), /* @__PURE__ */ jsx("span", { children: "Refresh" })]
117
- })]
395
+ children: /* @__PURE__ */ jsx(RefreshIcon, {})
396
+ }) }), /* @__PURE__ */ jsx(TooltipContent, {
397
+ side: "bottom",
398
+ sideOffset: 6,
399
+ children: "Refresh runs"
400
+ })] })]
118
401
  }),
119
402
  error ? /* @__PURE__ */ jsx("div", {
120
403
  className: "think-execution-workbench__error",
@@ -122,325 +405,65 @@ function ThinkExecutionWorkbench({ backButton, className, pageSize = executionPa
122
405
  children: error
123
406
  }) : null,
124
407
  /* @__PURE__ */ jsxs("div", {
125
- className: "think-execution-workbench__sections",
408
+ className: "think-execution-workbench__content",
126
409
  "aria-busy": busyAction !== null,
127
- children: [/* @__PURE__ */ jsxs("div", {
128
- className: "think-execution-workbench__lists",
129
- children: [/* @__PURE__ */ jsxs("section", {
130
- className: "think-execution-workbench__section",
131
- "aria-labelledby": submissionsId,
132
- children: [/* @__PURE__ */ jsxs("div", {
133
- className: "think-execution-workbench__section-header",
134
- children: [/* @__PURE__ */ jsx("h3", {
135
- id: submissionsId,
136
- children: "Submissions"
137
- }), /* @__PURE__ */ jsx(Badge, {
138
- color: "neutral",
139
- size: "sm",
140
- children: submissions.length
141
- })]
142
- }), busyAction === "refresh" && submissions.length === 0 ? /* @__PURE__ */ jsx("p", {
143
- className: "think-execution-workbench__empty",
144
- role: "status",
145
- children: "Loading submissions..."
146
- }) : submissions.length === 0 ? /* @__PURE__ */ jsx("p", {
147
- className: "think-execution-workbench__empty",
148
- children: "No submissions."
149
- }) : /* @__PURE__ */ jsx("ul", {
150
- className: "think-execution-workbench__list",
151
- role: "list",
152
- children: submissions.map((submission) => /* @__PURE__ */ jsxs("li", {
153
- className: "think-execution-workbench__item",
154
- "data-selected": selectedSubmission?.submissionId === submission.submissionId ? "true" : void 0,
155
- children: [/* @__PURE__ */ jsxs("div", {
156
- className: "think-execution-workbench__row-main",
157
- children: [
158
- /* @__PURE__ */ jsxs("div", {
159
- className: "think-execution-workbench__item-topline",
160
- children: [/* @__PURE__ */ jsx("code", { children: submission.submissionId }), /* @__PURE__ */ jsx(StatusBadge, {
161
- color: submissionStatusColor(submission.status),
162
- children: submission.status
163
- })]
164
- }),
165
- /* @__PURE__ */ jsxs("p", {
166
- className: "think-execution-workbench__row-meta",
167
- children: [/* @__PURE__ */ jsxs("span", { children: ["Created ", formatTimestamp(submission.createdAt)] }), submission.idempotencyKey ? /* @__PURE__ */ jsxs("span", { children: ["Idempotency ", submission.idempotencyKey] }) : null]
168
- }),
169
- submission.error ? /* @__PURE__ */ jsx("p", {
170
- className: "think-execution-workbench__item-error",
171
- children: submission.error
172
- }) : null
173
- ]
174
- }), /* @__PURE__ */ jsxs("div", {
175
- className: "think-execution-workbench__actions",
176
- children: [
177
- /* @__PURE__ */ jsx(Button, {
178
- type: "button",
179
- variant: "ghost",
180
- color: "neutral",
181
- size: "icon-xs",
182
- "aria-label": `Inspect submission ${submission.submissionId}`,
183
- disabled: busyAction !== null,
184
- onClick: () => void runAction(`inspect-submission:${submission.submissionId}`, async () => {
185
- setSelectedWorkflow(null);
186
- setSelectedSubmission(await source.inspectSubmission(submission.submissionId));
187
- }),
188
- children: /* @__PURE__ */ jsx(SearchIcon, {})
189
- }),
190
- isSubmissionCancelable(submission.status) ? /* @__PURE__ */ jsx(Button, {
191
- type: "button",
192
- variant: "ghost",
193
- color: "neutral",
194
- size: "icon-xs",
195
- "aria-label": `Cancel submission ${submission.submissionId}`,
196
- disabled: busyAction !== null,
197
- onClick: () => void runAction(`cancel-submission:${submission.submissionId}`, () => source.cancelSubmission(submission.submissionId)),
198
- children: /* @__PURE__ */ jsx(SquareIcon, {})
199
- }) : null,
200
- /* @__PURE__ */ jsx(Button, {
201
- type: "button",
202
- variant: "ghost",
203
- color: "destructive",
204
- size: "icon-xs",
205
- "aria-label": `Delete submission ${submission.submissionId}`,
206
- disabled: busyAction !== null,
207
- onClick: () => void runAction(`delete-submission:${submission.submissionId}`, () => source.deleteSubmission(submission.submissionId)),
208
- children: /* @__PURE__ */ jsx(XIcon, {})
209
- })
210
- ]
211
- })]
212
- }, submission.submissionId))
213
- })]
214
- }), /* @__PURE__ */ jsxs("section", {
215
- className: "think-execution-workbench__section",
216
- "aria-labelledby": executionsId,
217
- children: [/* @__PURE__ */ jsxs("div", {
218
- className: "think-execution-workbench__section-header",
219
- children: [/* @__PURE__ */ jsx("h3", {
220
- id: executionsId,
221
- children: "Tracked workflows"
222
- }), /* @__PURE__ */ jsx(Badge, {
223
- color: "neutral",
224
- size: "sm",
225
- children: workflows?.total ?? 0
226
- })]
227
- }), busyAction === "refresh" && workflows === null ? /* @__PURE__ */ jsx("p", {
228
- className: "think-execution-workbench__empty",
229
- role: "status",
230
- children: "Loading workflows..."
231
- }) : !workflows || workflows.workflows.length === 0 ? /* @__PURE__ */ jsx("p", {
232
- className: "think-execution-workbench__empty",
233
- children: "No tracked workflows."
234
- }) : /* @__PURE__ */ jsx("ul", {
235
- className: "think-execution-workbench__list",
236
- role: "list",
237
- children: workflows.workflows.map((workflow) => {
238
- const workflowStatus = String(workflow.status);
239
- return /* @__PURE__ */ jsxs("li", {
240
- className: "think-execution-workbench__item",
241
- "data-selected": selectedWorkflow?.workflowId === workflow.workflowId ? "true" : void 0,
242
- children: [/* @__PURE__ */ jsxs("div", {
243
- className: "think-execution-workbench__row-main",
244
- children: [
245
- /* @__PURE__ */ jsxs("div", {
246
- className: "think-execution-workbench__item-topline",
247
- children: [/* @__PURE__ */ jsx("code", { children: workflow.workflowName }), /* @__PURE__ */ jsx(StatusBadge, {
248
- color: workflowStatusColor(workflow.status),
249
- children: workflowStatus
250
- })]
251
- }),
252
- /* @__PURE__ */ jsxs("p", {
253
- className: "think-execution-workbench__row-meta",
254
- children: [/* @__PURE__ */ jsx("span", { children: workflow.workflowId }), /* @__PURE__ */ jsxs("span", { children: ["Updated ", formatTimestamp(workflow.updatedAt)] })]
255
- }),
256
- workflow.error ? /* @__PURE__ */ jsxs("p", {
257
- className: "think-execution-workbench__item-error",
258
- children: [
259
- workflow.error.name,
260
- ": ",
261
- workflow.error.message
262
- ]
263
- }) : null
264
- ]
265
- }), /* @__PURE__ */ jsxs("div", {
266
- className: "think-execution-workbench__actions",
267
- children: [
268
- /* @__PURE__ */ jsx(Button, {
269
- type: "button",
270
- variant: "ghost",
271
- color: "neutral",
272
- size: "icon-xs",
273
- "aria-label": `Inspect workflow ${workflow.workflowId}`,
274
- disabled: busyAction !== null,
275
- onClick: () => void runAction(`inspect-workflow:${workflow.workflowId}`, async () => {
276
- setSelectedSubmission(null);
277
- setSelectedWorkflow(await source.getWorkflow(workflow.workflowId) ?? workflow);
278
- }),
279
- children: /* @__PURE__ */ jsx(SearchIcon, {})
280
- }),
281
- /* @__PURE__ */ jsx(Button, {
282
- type: "button",
283
- variant: "ghost",
284
- color: "neutral",
285
- size: "icon-xs",
286
- "aria-label": `Sync workflow ${workflow.workflowId} status`,
287
- disabled: busyAction !== null,
288
- onClick: () => void runAction(`sync-workflow:${workflow.workflowId}`, () => source.getWorkflowStatus(workflow.workflowName, workflow.workflowId)),
289
- children: /* @__PURE__ */ jsx(RefreshIcon, {})
290
- }),
291
- workflowStatus === "running" ? /* @__PURE__ */ jsx(Button, {
292
- type: "button",
293
- variant: "ghost",
294
- color: "neutral",
295
- size: "icon-xs",
296
- "aria-label": `Pause workflow ${workflow.workflowId}`,
297
- disabled: busyAction !== null,
298
- onClick: () => void runAction(`pause-workflow:${workflow.workflowId}`, () => source.pauseWorkflow(workflow.workflowId)),
299
- children: /* @__PURE__ */ jsx(PauseIcon, {})
300
- }) : null,
301
- workflowStatus === "paused" ? /* @__PURE__ */ jsx(Button, {
302
- type: "button",
303
- variant: "ghost",
304
- color: "neutral",
305
- size: "icon-xs",
306
- "aria-label": `Resume workflow ${workflow.workflowId}`,
307
- disabled: busyAction !== null,
308
- onClick: () => void runAction(`resume-workflow:${workflow.workflowId}`, () => source.resumeWorkflow(workflow.workflowId)),
309
- children: /* @__PURE__ */ jsx(PlayIcon, {})
310
- }) : null,
311
- isWorkflowTerminal(workflow.status) ? /* @__PURE__ */ jsx(Button, {
312
- type: "button",
313
- variant: "ghost",
314
- color: "neutral",
315
- size: "icon-xs",
316
- "aria-label": `Restart workflow ${workflow.workflowId}`,
317
- disabled: busyAction !== null,
318
- onClick: () => void runAction(`restart-workflow:${workflow.workflowId}`, () => source.restartWorkflow(workflow.workflowId)),
319
- children: /* @__PURE__ */ jsx(RefreshIcon, {})
320
- }) : null,
321
- workflowStatus === "running" ? /* @__PURE__ */ jsx(Button, {
322
- type: "button",
323
- variant: "ghost",
324
- color: "destructive",
325
- size: "icon-xs",
326
- "aria-label": `Terminate workflow ${workflow.workflowId}`,
327
- disabled: busyAction !== null,
328
- onClick: () => void runAction(`terminate-workflow:${workflow.workflowId}`, () => source.terminateWorkflow(workflow.workflowId)),
329
- children: /* @__PURE__ */ jsx(SquareIcon, {})
330
- }) : null,
331
- /* @__PURE__ */ jsx(Button, {
332
- type: "button",
333
- variant: "ghost",
334
- color: "destructive",
335
- size: "icon-xs",
336
- "aria-label": `Delete workflow ${workflow.workflowId}`,
337
- disabled: busyAction !== null,
338
- onClick: () => void runAction(`delete-workflow:${workflow.workflowId}`, () => source.deleteWorkflow(workflow.workflowId)),
339
- children: /* @__PURE__ */ jsx(XIcon, {})
340
- })
341
- ]
410
+ children: [/* @__PURE__ */ jsxs("section", {
411
+ className: "think-execution-workbench__run-list",
412
+ "aria-labelledby": runsId,
413
+ children: [/* @__PURE__ */ jsx("div", {
414
+ className: "think-execution-workbench__list-header",
415
+ children: /* @__PURE__ */ jsx("h3", {
416
+ id: runsId,
417
+ children: "Runs"
418
+ })
419
+ }), busyAction === "refresh" && runs.length === 0 ? /* @__PURE__ */ jsx("p", {
420
+ className: "think-execution-workbench__empty",
421
+ role: "status",
422
+ children: "Loading runs..."
423
+ }) : runs.length === 0 ? /* @__PURE__ */ jsx("p", {
424
+ className: "think-execution-workbench__empty",
425
+ children: "No runs yet."
426
+ }) : /* @__PURE__ */ jsx("ul", {
427
+ className: "think-execution-workbench__list",
428
+ role: "list",
429
+ children: runs.map((run) => {
430
+ const id = runId(run);
431
+ const selected = isSameRun(selectedRun, run);
432
+ return /* @__PURE__ */ jsxs("li", {
433
+ "data-selected": selected || void 0,
434
+ children: [/* @__PURE__ */ jsxs("button", {
435
+ type: "button",
436
+ className: "think-execution-workbench__run",
437
+ "aria-label": runAccessibleName(run),
438
+ "aria-current": selected ? "page" : void 0,
439
+ disabled: busyAction !== null,
440
+ onClick: () => selectRun(run),
441
+ children: [/* @__PURE__ */ jsx(RunStatus, { run }), /* @__PURE__ */ jsxs("span", {
442
+ className: "think-execution-workbench__run-copy",
443
+ children: [/* @__PURE__ */ jsxs("span", {
444
+ className: "think-execution-workbench__run-heading",
445
+ children: [/* @__PURE__ */ jsx("span", {
446
+ className: "think-execution-workbench__run-title",
447
+ children: runTitle(run)
448
+ }), runSubtitle(run) ? /* @__PURE__ */ jsx("code", { children: runSubtitle(run) }) : null]
449
+ }), /* @__PURE__ */ jsx("span", {
450
+ className: "think-execution-workbench__run-summary",
451
+ children: runListTimestamp(run)
452
+ })]
342
453
  })]
343
- }, workflow.workflowId);
344
- })
345
- })]
454
+ }), /* @__PURE__ */ jsx(RunActions, {
455
+ busy: busyAction !== null,
456
+ run,
457
+ source,
458
+ onAction: (key, action) => void runAction(key, () => action())
459
+ })]
460
+ }, `${run.kind}:${id}`);
461
+ })
346
462
  })]
347
- }), /* @__PURE__ */ jsx("div", {
463
+ }), /* @__PURE__ */ jsx("section", {
348
464
  className: "think-execution-workbench__detail",
349
- "aria-labelledby": inspectionId,
350
- children: selectedSubmission ? /* @__PURE__ */ jsxs(Fragment, { children: [
351
- /* @__PURE__ */ jsxs("div", {
352
- className: "think-execution-workbench__detail-header",
353
- children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
354
- className: "think-execution-workbench__eyebrow",
355
- children: "Submission inspection"
356
- }), /* @__PURE__ */ jsx("h3", {
357
- id: inspectionId,
358
- children: /* @__PURE__ */ jsx("code", { children: selectedSubmission.submissionId })
359
- })] }), /* @__PURE__ */ jsx(StatusBadge, {
360
- color: submissionStatusColor(selectedSubmission.status),
361
- children: selectedSubmission.status
362
- })]
363
- }),
364
- /* @__PURE__ */ jsxs("dl", {
365
- className: "think-execution-workbench__meta think-execution-workbench__meta--detail",
366
- children: [
367
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Created" }), /* @__PURE__ */ jsx("dd", { children: formatTimestamp(selectedSubmission.createdAt) })] }),
368
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Started" }), /* @__PURE__ */ jsx("dd", { children: formatTimestamp(selectedSubmission.startedAt) })] }),
369
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Completed" }), /* @__PURE__ */ jsx("dd", { children: formatTimestamp(selectedSubmission.completedAt) })] }),
370
- selectedSubmission.requestId ? /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Request" }), /* @__PURE__ */ jsx("dd", { children: selectedSubmission.requestId })] }) : null,
371
- selectedSubmission.idempotencyKey ? /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Idempotency" }), /* @__PURE__ */ jsx("dd", { children: selectedSubmission.idempotencyKey })] }) : null
372
- ]
373
- }),
374
- selectedSubmission.error ? /* @__PURE__ */ jsx("p", {
375
- className: "think-execution-workbench__item-error",
376
- children: selectedSubmission.error
377
- }) : null,
378
- selectedSubmission.metadata ? /* @__PURE__ */ jsxs("div", {
379
- className: "think-execution-workbench__inspection",
380
- children: [/* @__PURE__ */ jsx("h4", { children: "Metadata" }), /* @__PURE__ */ jsx("pre", {
381
- tabIndex: 0,
382
- children: formatJson(selectedSubmission.metadata)
383
- })]
384
- }) : null,
385
- /* @__PURE__ */ jsxs("div", {
386
- className: "think-execution-workbench__inspection",
387
- children: [/* @__PURE__ */ jsx("h4", { children: "Raw record" }), /* @__PURE__ */ jsx("pre", {
388
- tabIndex: 0,
389
- children: formatJson(selectedSubmission)
390
- })]
391
- })
392
- ] }) : selectedWorkflow ? /* @__PURE__ */ jsxs(Fragment, { children: [
393
- /* @__PURE__ */ jsxs("div", {
394
- className: "think-execution-workbench__detail-header",
395
- children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
396
- className: "think-execution-workbench__eyebrow",
397
- children: "Workflow inspection"
398
- }), /* @__PURE__ */ jsx("h3", {
399
- id: inspectionId,
400
- children: /* @__PURE__ */ jsx("code", { children: selectedWorkflow.workflowName })
401
- })] }), /* @__PURE__ */ jsx(StatusBadge, {
402
- color: workflowStatusColor(selectedWorkflow.status),
403
- children: String(selectedWorkflow.status)
404
- })]
405
- }),
406
- /* @__PURE__ */ jsxs("dl", {
407
- className: "think-execution-workbench__meta think-execution-workbench__meta--detail",
408
- children: [
409
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Instance" }), /* @__PURE__ */ jsx("dd", { children: selectedWorkflow.workflowId })] }),
410
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Created" }), /* @__PURE__ */ jsx("dd", { children: formatTimestamp(selectedWorkflow.createdAt) })] }),
411
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Updated" }), /* @__PURE__ */ jsx("dd", { children: formatTimestamp(selectedWorkflow.updatedAt) })] }),
412
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Completed" }), /* @__PURE__ */ jsx("dd", { children: formatTimestamp(selectedWorkflow.completedAt) })] })
413
- ]
414
- }),
415
- selectedWorkflow.error ? /* @__PURE__ */ jsxs("p", {
416
- className: "think-execution-workbench__item-error",
417
- children: [
418
- selectedWorkflow.error.name,
419
- ": ",
420
- selectedWorkflow.error.message
421
- ]
422
- }) : null,
423
- selectedWorkflow.metadata ? /* @__PURE__ */ jsxs("div", {
424
- className: "think-execution-workbench__inspection",
425
- children: [/* @__PURE__ */ jsx("h4", { children: "Metadata" }), /* @__PURE__ */ jsx("pre", {
426
- tabIndex: 0,
427
- children: formatJson(selectedWorkflow.metadata)
428
- })]
429
- }) : null,
430
- /* @__PURE__ */ jsxs("div", {
431
- className: "think-execution-workbench__inspection",
432
- children: [/* @__PURE__ */ jsx("h4", { children: "Raw record" }), /* @__PURE__ */ jsx("pre", {
433
- tabIndex: 0,
434
- children: formatJson(selectedWorkflow)
435
- })]
436
- })
437
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("h3", {
438
- id: inspectionId,
439
- children: "Inspection"
440
- }), /* @__PURE__ */ jsx("p", {
441
- className: "think-execution-workbench__empty think-execution-workbench__empty--detail",
442
- children: "Inspect a submission or workflow."
443
- })] })
465
+ "aria-label": "Run detail",
466
+ children: /* @__PURE__ */ jsx(RunDetail, { run: selectedRun })
444
467
  })]
445
468
  })
446
469
  ]