@synergenius/flow-weaver-pack-weaver 0.9.59 → 0.9.77

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 (217) hide show
  1. package/dist/ai-chat-provider.d.ts +12 -0
  2. package/dist/ai-chat-provider.d.ts.map +1 -1
  3. package/dist/ai-chat-provider.js +351 -335
  4. package/dist/ai-chat-provider.js.map +1 -1
  5. package/dist/bot/agent-loop.d.ts +20 -0
  6. package/dist/bot/agent-loop.d.ts.map +1 -0
  7. package/dist/bot/agent-loop.js +331 -0
  8. package/dist/bot/agent-loop.js.map +1 -0
  9. package/dist/bot/ai-router.d.ts +19 -0
  10. package/dist/bot/ai-router.d.ts.map +1 -0
  11. package/dist/bot/ai-router.js +104 -0
  12. package/dist/bot/ai-router.js.map +1 -0
  13. package/dist/bot/assistant-tools.d.ts.map +1 -1
  14. package/dist/bot/assistant-tools.js +49 -33
  15. package/dist/bot/assistant-tools.js.map +1 -1
  16. package/dist/bot/async-mutex.d.ts +13 -0
  17. package/dist/bot/async-mutex.d.ts.map +1 -0
  18. package/dist/bot/async-mutex.js +37 -0
  19. package/dist/bot/async-mutex.js.map +1 -0
  20. package/dist/bot/bot-manager.d.ts +2 -2
  21. package/dist/bot/bot-manager.d.ts.map +1 -1
  22. package/dist/bot/bot-manager.js +3 -3
  23. package/dist/bot/bot-manager.js.map +1 -1
  24. package/dist/bot/bot-registry.js +2 -2
  25. package/dist/bot/bot-registry.js.map +1 -1
  26. package/dist/bot/conversation-store.d.ts +1 -0
  27. package/dist/bot/conversation-store.d.ts.map +1 -1
  28. package/dist/bot/conversation-store.js.map +1 -1
  29. package/dist/bot/dashboard.d.ts.map +1 -1
  30. package/dist/bot/dashboard.js +17 -8
  31. package/dist/bot/dashboard.js.map +1 -1
  32. package/dist/bot/improve-loop.js.map +1 -1
  33. package/dist/bot/index.d.ts +2 -4
  34. package/dist/bot/index.d.ts.map +1 -1
  35. package/dist/bot/index.js +1 -2
  36. package/dist/bot/index.js.map +1 -1
  37. package/dist/bot/instance-manager.d.ts +31 -0
  38. package/dist/bot/instance-manager.d.ts.map +1 -0
  39. package/dist/bot/instance-manager.js +115 -0
  40. package/dist/bot/instance-manager.js.map +1 -0
  41. package/dist/bot/orchestrator.d.ts +36 -0
  42. package/dist/bot/orchestrator.d.ts.map +1 -0
  43. package/dist/bot/orchestrator.js +176 -0
  44. package/dist/bot/orchestrator.js.map +1 -0
  45. package/dist/bot/profile-store.d.ts +36 -0
  46. package/dist/bot/profile-store.d.ts.map +1 -0
  47. package/dist/bot/profile-store.js +208 -0
  48. package/dist/bot/profile-store.js.map +1 -0
  49. package/dist/bot/profile-types.d.ts +126 -0
  50. package/dist/bot/profile-types.d.ts.map +1 -0
  51. package/dist/bot/profile-types.js +7 -0
  52. package/dist/bot/profile-types.js.map +1 -0
  53. package/dist/bot/run-store.d.ts.map +1 -1
  54. package/dist/bot/run-store.js +8 -0
  55. package/dist/bot/run-store.js.map +1 -1
  56. package/dist/bot/runner.d.ts +4 -0
  57. package/dist/bot/runner.d.ts.map +1 -1
  58. package/dist/bot/runner.js +5 -1
  59. package/dist/bot/runner.js.map +1 -1
  60. package/dist/bot/swarm-controller.d.ts +109 -0
  61. package/dist/bot/swarm-controller.d.ts.map +1 -0
  62. package/dist/bot/swarm-controller.js +640 -0
  63. package/dist/bot/swarm-controller.js.map +1 -0
  64. package/dist/bot/swarm-event-log.d.ts +28 -0
  65. package/dist/bot/swarm-event-log.d.ts.map +1 -0
  66. package/dist/bot/swarm-event-log.js +54 -0
  67. package/dist/bot/swarm-event-log.js.map +1 -0
  68. package/dist/bot/task-prompt-builder.d.ts +22 -0
  69. package/dist/bot/task-prompt-builder.d.ts.map +1 -0
  70. package/dist/bot/task-prompt-builder.js +240 -0
  71. package/dist/bot/task-prompt-builder.js.map +1 -0
  72. package/dist/bot/task-store.d.ts +21 -0
  73. package/dist/bot/task-store.d.ts.map +1 -0
  74. package/dist/bot/task-store.js +364 -0
  75. package/dist/bot/task-store.js.map +1 -0
  76. package/dist/bot/task-types.d.ts +79 -0
  77. package/dist/bot/task-types.d.ts.map +1 -0
  78. package/dist/bot/task-types.js +6 -0
  79. package/dist/bot/task-types.js.map +1 -0
  80. package/dist/bot/types.d.ts +8 -0
  81. package/dist/bot/types.d.ts.map +1 -1
  82. package/dist/cli-handlers.d.ts.map +1 -1
  83. package/dist/cli-handlers.js +79 -54
  84. package/dist/cli-handlers.js.map +1 -1
  85. package/dist/cli.d.ts +3 -0
  86. package/dist/cli.d.ts.map +1 -0
  87. package/dist/cli.js +749 -0
  88. package/dist/cli.js.map +1 -0
  89. package/dist/docs/docs/weaver-bot-usage.md +35 -18
  90. package/dist/docs/docs/weaver-config.md +20 -0
  91. package/dist/docs/docs/weaver-task-queue.md +31 -19
  92. package/dist/docs/weaver-config.md +15 -9
  93. package/dist/index.d.ts +2 -2
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +1 -1
  96. package/dist/index.js.map +1 -1
  97. package/dist/mcp-tools.d.ts +17 -0
  98. package/dist/mcp-tools.d.ts.map +1 -1
  99. package/dist/mcp-tools.js +98 -279
  100. package/dist/mcp-tools.js.map +1 -1
  101. package/dist/node-types/bot-report.d.ts.map +1 -1
  102. package/dist/node-types/bot-report.js +6 -24
  103. package/dist/node-types/bot-report.js.map +1 -1
  104. package/dist/node-types/orchestrator-dispatch.d.ts +17 -0
  105. package/dist/node-types/orchestrator-dispatch.d.ts.map +1 -0
  106. package/dist/node-types/orchestrator-dispatch.js +63 -0
  107. package/dist/node-types/orchestrator-dispatch.js.map +1 -0
  108. package/dist/node-types/orchestrator-load-state.d.ts +16 -0
  109. package/dist/node-types/orchestrator-load-state.d.ts.map +1 -0
  110. package/dist/node-types/orchestrator-load-state.js +60 -0
  111. package/dist/node-types/orchestrator-load-state.js.map +1 -0
  112. package/dist/node-types/orchestrator-route.d.ts +16 -0
  113. package/dist/node-types/orchestrator-route.d.ts.map +1 -0
  114. package/dist/node-types/orchestrator-route.js +28 -0
  115. package/dist/node-types/orchestrator-route.js.map +1 -0
  116. package/dist/node-types/receive-task.d.ts +2 -3
  117. package/dist/node-types/receive-task.d.ts.map +1 -1
  118. package/dist/node-types/receive-task.js +3 -48
  119. package/dist/node-types/receive-task.js.map +1 -1
  120. package/dist/templates/weaver-template.d.ts +11 -0
  121. package/dist/templates/weaver-template.d.ts.map +1 -0
  122. package/dist/templates/weaver-template.js +53 -0
  123. package/dist/templates/weaver-template.js.map +1 -0
  124. package/dist/ui/bot-activity.js +2 -2
  125. package/dist/ui/bot-constants.d.ts +14 -0
  126. package/dist/ui/bot-constants.d.ts.map +1 -0
  127. package/dist/ui/bot-constants.js +189 -0
  128. package/dist/ui/bot-constants.js.map +1 -0
  129. package/dist/ui/bot-panel.js +207 -245
  130. package/dist/ui/bot-slot-card.js +141 -0
  131. package/dist/ui/budget-bar.js +59 -0
  132. package/dist/ui/chat-task-result.js +178 -0
  133. package/dist/ui/decision-log.js +136 -0
  134. package/dist/ui/profile-card.js +158 -0
  135. package/dist/ui/profile-editor.js +597 -0
  136. package/dist/ui/swarm-controls.js +245 -0
  137. package/dist/ui/swarm-dashboard.js +3012 -0
  138. package/dist/ui/task-create-form.js +98 -0
  139. package/dist/ui/task-detail-view.js +1044 -0
  140. package/dist/ui/task-pool-list.js +156 -0
  141. package/dist/workflows/orchestrator.d.ts +21 -0
  142. package/dist/workflows/orchestrator.d.ts.map +1 -0
  143. package/dist/workflows/orchestrator.js +281 -0
  144. package/dist/workflows/orchestrator.js.map +1 -0
  145. package/dist/workflows/weaver-bot-session.d.ts +65 -0
  146. package/dist/workflows/weaver-bot-session.d.ts.map +1 -0
  147. package/dist/workflows/weaver-bot-session.js +68 -0
  148. package/dist/workflows/weaver-bot-session.js.map +1 -0
  149. package/dist/workflows/weaver.d.ts +24 -0
  150. package/dist/workflows/weaver.d.ts.map +1 -0
  151. package/dist/workflows/weaver.js +28 -0
  152. package/dist/workflows/weaver.js.map +1 -0
  153. package/flowweaver.manifest.json +547 -133
  154. package/package.json +1 -1
  155. package/src/ai-chat-provider.ts +378 -371
  156. package/src/bot/ai-router.ts +132 -0
  157. package/src/bot/assistant-tools.ts +47 -29
  158. package/src/bot/async-mutex.ts +37 -0
  159. package/src/bot/bot-manager.ts +3 -3
  160. package/src/bot/bot-registry.ts +2 -2
  161. package/src/bot/conversation-store.ts +2 -1
  162. package/src/bot/dashboard.ts +17 -8
  163. package/src/bot/improve-loop.ts +6 -6
  164. package/src/bot/index.ts +2 -4
  165. package/src/bot/instance-manager.ts +128 -0
  166. package/src/bot/orchestrator.ts +244 -0
  167. package/src/bot/profile-store.ts +225 -0
  168. package/src/bot/profile-types.ts +141 -0
  169. package/src/bot/run-store.ts +8 -0
  170. package/src/bot/runner.ts +9 -1
  171. package/src/bot/swarm-controller.ts +780 -0
  172. package/src/bot/swarm-event-log.ts +57 -0
  173. package/src/bot/task-prompt-builder.ts +309 -0
  174. package/src/bot/task-store.ts +407 -0
  175. package/src/bot/task-types.ts +100 -0
  176. package/src/bot/types.ts +8 -0
  177. package/src/cli-handlers.ts +78 -53
  178. package/src/docs/weaver-bot-usage.md +35 -18
  179. package/src/docs/weaver-config.md +20 -0
  180. package/src/docs/weaver-task-queue.md +31 -19
  181. package/src/index.ts +5 -4
  182. package/src/mcp-tools.ts +129 -372
  183. package/src/node-types/bot-report.ts +6 -24
  184. package/src/node-types/orchestrator-dispatch.ts +71 -0
  185. package/src/node-types/orchestrator-load-state.ts +66 -0
  186. package/src/node-types/orchestrator-route.ts +33 -0
  187. package/src/node-types/receive-task.ts +3 -57
  188. package/src/ui/bot-activity.tsx +2 -2
  189. package/src/ui/bot-constants.ts +192 -0
  190. package/src/ui/bot-panel.tsx +213 -247
  191. package/src/ui/bot-slot-card.tsx +139 -0
  192. package/src/ui/budget-bar.tsx +30 -0
  193. package/src/ui/chat-task-result.tsx +236 -0
  194. package/src/ui/decision-log.tsx +148 -0
  195. package/src/ui/profile-card.tsx +157 -0
  196. package/src/ui/profile-editor.tsx +384 -0
  197. package/src/ui/swarm-controls.tsx +260 -0
  198. package/src/ui/swarm-dashboard.tsx +647 -0
  199. package/src/ui/task-create-form.tsx +87 -0
  200. package/src/ui/task-detail-view.tsx +841 -0
  201. package/src/ui/task-pool-list.tsx +187 -0
  202. package/src/workflows/orchestrator.ts +302 -0
  203. package/dist/docs/weaver-bot-usage.md +0 -34
  204. package/dist/docs/weaver-genesis.md +0 -32
  205. package/dist/docs/weaver-task-queue.md +0 -34
  206. package/dist/ui/bot-workspace.js +0 -1015
  207. package/dist/ui/chat-bot-result.js +0 -71
  208. package/dist/ui/queue-input.js +0 -82
  209. package/dist/ui/session-bar.js +0 -174
  210. package/src/bot/error-guide.ts +0 -4
  211. package/src/bot/retry-utils.ts +0 -4
  212. package/src/bot/session-state.ts +0 -116
  213. package/src/bot/task-queue.ts +0 -262
  214. package/src/ui/bot-workspace.tsx +0 -442
  215. package/src/ui/chat-bot-result.tsx +0 -81
  216. package/src/ui/queue-input.tsx +0 -56
  217. package/src/ui/session-bar.tsx +0 -157
@@ -0,0 +1,3012 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/ui/swarm-dashboard.tsx
31
+ var swarm_dashboard_exports = {};
32
+ __export(swarm_dashboard_exports, {
33
+ SwarmDashboard: () => SwarmDashboard,
34
+ default: () => swarm_dashboard_default
35
+ });
36
+ module.exports = __toCommonJS(swarm_dashboard_exports);
37
+
38
+ // src/ui/swarm-controls.tsx
39
+ var React = require("react");
40
+ var { useState, useCallback } = React;
41
+ var {
42
+ Flex,
43
+ Button,
44
+ Typography,
45
+ IconButton,
46
+ StatusIcon,
47
+ toast,
48
+ usePackWorkspace
49
+ } = require("@fw/plugin-ui-kit");
50
+ function swarmStatusToIconStatus(status) {
51
+ switch (status) {
52
+ case "running":
53
+ return "running";
54
+ case "paused":
55
+ return "pending";
56
+ case "stopping":
57
+ return "failed";
58
+ case "idle":
59
+ default:
60
+ return "completed";
61
+ }
62
+ }
63
+ function statusLabel(status) {
64
+ switch (status) {
65
+ case "running":
66
+ return "Running";
67
+ case "paused":
68
+ return "Paused";
69
+ case "stopping":
70
+ return "Stopping";
71
+ case "idle":
72
+ default:
73
+ return "Idle";
74
+ }
75
+ }
76
+ function countActiveInstances(instances) {
77
+ const entries = Object.values(instances);
78
+ const active = entries.filter((i) => i.status === "executing").length;
79
+ return { active, total: entries.length };
80
+ }
81
+ function SwarmControls({ swarmStatus, onRefresh }) {
82
+ const ctx = usePackWorkspace();
83
+ const [starting, setStarting] = useState(false);
84
+ const [pausing, setPausing] = useState(false);
85
+ const [stopping, setStopping] = useState(false);
86
+ const status = swarmStatus?.status ?? "idle";
87
+ const instances = swarmStatus?.instances ?? {};
88
+ const { active, total } = countActiveInstances(instances);
89
+ const isIdle = status === "idle";
90
+ const isRunning = status === "running";
91
+ const isPaused = status === "paused";
92
+ const isStopping = status === "stopping";
93
+ const handleStart = useCallback(async () => {
94
+ setStarting(true);
95
+ try {
96
+ const result = await ctx.callTool("fw_weaver_swarm_start", {});
97
+ const data = result;
98
+ if (data?.error) {
99
+ toast(String(data.error), { type: "error" });
100
+ } else {
101
+ toast("Swarm started", { type: "success" });
102
+ }
103
+ onRefresh();
104
+ } catch (err) {
105
+ const msg = err instanceof Error ? err.message : "Failed to start swarm";
106
+ toast(msg, { type: "error" });
107
+ }
108
+ setStarting(false);
109
+ }, [ctx, onRefresh]);
110
+ const handlePause = useCallback(async () => {
111
+ setPausing(true);
112
+ try {
113
+ const result = await ctx.callTool("fw_weaver_swarm_pause", {});
114
+ const data = result;
115
+ if (data?.error) {
116
+ toast(String(data.error), { type: "error" });
117
+ } else {
118
+ toast("Swarm paused", { type: "info" });
119
+ }
120
+ onRefresh();
121
+ } catch (err) {
122
+ const msg = err instanceof Error ? err.message : "Failed to pause swarm";
123
+ toast(msg, { type: "error" });
124
+ }
125
+ setPausing(false);
126
+ }, [ctx, onRefresh]);
127
+ const handleResume = useCallback(async () => {
128
+ setStarting(true);
129
+ try {
130
+ const result = await ctx.callTool("fw_weaver_swarm_start", {});
131
+ const data = result;
132
+ if (data?.error) {
133
+ toast(String(data.error), { type: "error" });
134
+ } else {
135
+ toast("Swarm resumed", { type: "success" });
136
+ }
137
+ onRefresh();
138
+ } catch (err) {
139
+ const msg = err instanceof Error ? err.message : "Failed to resume swarm";
140
+ toast(msg, { type: "error" });
141
+ }
142
+ setStarting(false);
143
+ }, [ctx, onRefresh]);
144
+ const handleStop = useCallback(async () => {
145
+ setStopping(true);
146
+ const ok = await ctx.confirm("This will stop all running instances and abort active tasks.", {
147
+ title: "Stop Swarm",
148
+ confirmLabel: "Stop",
149
+ state: "danger"
150
+ });
151
+ if (!ok) {
152
+ setStopping(false);
153
+ return;
154
+ }
155
+ try {
156
+ const result = await ctx.callTool("fw_weaver_swarm_stop", {});
157
+ const data = result;
158
+ if (data?.error) {
159
+ toast(String(data.error), { type: "error" });
160
+ } else {
161
+ toast("Swarm stopped", { type: "info" });
162
+ }
163
+ onRefresh();
164
+ } catch (err) {
165
+ const msg = err instanceof Error ? err.message : "Failed to stop swarm";
166
+ toast(msg, { type: "error" });
167
+ }
168
+ setStopping(false);
169
+ }, [ctx, onRefresh]);
170
+ const anyLoading = starting || pausing || stopping;
171
+ return React.createElement(
172
+ Flex,
173
+ {
174
+ variant: "row-center-space-between-nowrap-10",
175
+ style: {
176
+ padding: "8px 16px",
177
+ borderBottom: "1px solid var(--color-border-default)",
178
+ flexShrink: 0,
179
+ backgroundColor: isRunning ? "var(--color-brand-main-alpha-10)" : isPaused ? "var(--color-status-caution-alpha-10)" : "transparent"
180
+ }
181
+ },
182
+ // Left: status icon + label
183
+ React.createElement(
184
+ Flex,
185
+ { variant: "row-center-start-nowrap-8", style: { flexShrink: 0 } },
186
+ React.createElement(StatusIcon, {
187
+ status: swarmStatusToIconStatus(status),
188
+ size: "sm"
189
+ }),
190
+ React.createElement(Typography, {
191
+ variant: "caption-bold"
192
+ }, statusLabel(status))
193
+ ),
194
+ // Center: bot count + stats
195
+ React.createElement(
196
+ Flex,
197
+ {
198
+ variant: "row-center-start-nowrap-12",
199
+ style: { flex: 1 }
200
+ },
201
+ total > 0 && React.createElement(Typography, {
202
+ variant: "caption-regular",
203
+ color: "color-text-medium"
204
+ }, `${active}/${total} bots active`),
205
+ swarmStatus && swarmStatus.tasksCompleted > 0 && React.createElement(Typography, {
206
+ variant: "caption-regular",
207
+ color: "color-text-subtle"
208
+ }, `${swarmStatus.tasksCompleted} done`),
209
+ swarmStatus && swarmStatus.tasksFailed > 0 && React.createElement(Typography, {
210
+ variant: "caption-regular",
211
+ color: "color-status-negative"
212
+ }, `${swarmStatus.tasksFailed} failed`)
213
+ ),
214
+ // Right: action buttons
215
+ React.createElement(
216
+ Flex,
217
+ { variant: "row-center-start-nowrap-4", style: { flexShrink: 0 } },
218
+ // Start / Resume button
219
+ (isIdle || isPaused) && React.createElement(Button, {
220
+ size: "xs",
221
+ variant: "outlined",
222
+ color: "primary",
223
+ onClick: isPaused ? handleResume : handleStart,
224
+ loading: starting,
225
+ disabled: anyLoading
226
+ }, isPaused ? "Resume" : "Start"),
227
+ // Pause button (only when running)
228
+ isRunning && React.createElement(Button, {
229
+ size: "xs",
230
+ variant: "outlined",
231
+ color: "warning",
232
+ onClick: handlePause,
233
+ loading: pausing,
234
+ disabled: anyLoading
235
+ }, "Pause"),
236
+ // Stop button (when running or paused)
237
+ (isRunning || isPaused) && React.createElement(Button, {
238
+ size: "xs",
239
+ variant: "outlined",
240
+ color: "danger",
241
+ onClick: handleStop,
242
+ loading: stopping,
243
+ disabled: anyLoading || isStopping
244
+ }, "Stop"),
245
+ // Refresh button (always available)
246
+ React.createElement(IconButton, {
247
+ icon: "reset",
248
+ size: "xs",
249
+ variant: "outlined",
250
+ onClick: onRefresh,
251
+ disabled: anyLoading
252
+ })
253
+ )
254
+ );
255
+ }
256
+ var swarm_controls_default = SwarmControls;
257
+ module.exports = SwarmControls;
258
+
259
+ // src/ui/budget-bar.tsx
260
+ var React2 = require("react");
261
+ var { Flex: Flex2, Typography: Typography2 } = require("@fw/plugin-ui-kit");
262
+ function BudgetBar({ label, used, limit, unit }) {
263
+ const pct = limit > 0 ? Math.min(100, used / limit * 100) : 0;
264
+ const color = pct > 90 ? "var(--color-status-negative)" : pct > 70 ? "var(--color-status-caution)" : "var(--color-brand-main)";
265
+ return React2.createElement(
266
+ Flex2,
267
+ { variant: "column-start-start-nowrap-2", style: { width: "100%" } },
268
+ React2.createElement(
269
+ Flex2,
270
+ { variant: "row-center-space-between-nowrap-4" },
271
+ React2.createElement(Typography2, { variant: "smallCaption-regular", color: "color-text-subtle" }, label),
272
+ React2.createElement(
273
+ Typography2,
274
+ { variant: "smallCaption-regular", color: "color-text-subtle" },
275
+ `${used.toLocaleString()} / ${limit.toLocaleString()} ${unit}`
276
+ )
277
+ ),
278
+ React2.createElement(
279
+ Flex2,
280
+ {
281
+ variant: "row-start-start-nowrap-0",
282
+ style: { width: "100%", height: "4px", borderRadius: "2px", backgroundColor: "var(--color-surface-raised)", overflow: "hidden" }
283
+ },
284
+ React2.createElement(Flex2, {
285
+ variant: "row-start-start-nowrap-0",
286
+ style: { width: `${pct}%`, height: "100%", borderRadius: "2px", backgroundColor: color, transition: "width 0.3s" }
287
+ })
288
+ )
289
+ );
290
+ }
291
+ var budget_bar_default = BudgetBar;
292
+ module.exports = BudgetBar;
293
+
294
+ // src/ui/bot-slot-card.tsx
295
+ var import_react = __toESM(require("react"), 1);
296
+ var import_plugin_ui_kit = require("@fw/plugin-ui-kit");
297
+ var statusToLabel = {
298
+ idle: "Idle",
299
+ executing: "Executing",
300
+ paused: "Paused",
301
+ stopped: "Stopped"
302
+ };
303
+ function formatTokens(n) {
304
+ if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
305
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
306
+ return String(n);
307
+ }
308
+ function formatCost(n) {
309
+ if (n < 0.01 && n > 0) return "<$0.01";
310
+ return `$${n.toFixed(2)}`;
311
+ }
312
+ function BotSlotCard({ bot, currentTaskTitle, profileName, botDisplayName, botIcon, botColor, onPause, onResume, onStop }) {
313
+ const { botId, botName, status, currentTaskId, tokensUsed, cost } = bot;
314
+ const isExecuting = status === "executing";
315
+ const isPaused = status === "paused";
316
+ const label = statusToLabel[status] ?? status;
317
+ const taskText = isExecuting ? currentTaskTitle || currentTaskId || "-" : "-";
318
+ return import_react.default.createElement(
319
+ import_plugin_ui_kit.Flex,
320
+ {
321
+ variant: "row-center-start-nowrap-8",
322
+ style: { padding: "6px 16px", minHeight: "38px", borderBottom: "1px solid var(--color-border-default)" }
323
+ },
324
+ // Instance name
325
+ import_react.default.createElement(import_plugin_ui_kit.Typography, {
326
+ variant: "smallCaption-regular",
327
+ color: "color-text-high",
328
+ style: { width: "120px", flexShrink: 0 }
329
+ }, profileName ? `${profileName} #${botId.split("-").pop() ?? "0"}` : botName),
330
+ // Bot (icon + name)
331
+ import_react.default.createElement(
332
+ import_plugin_ui_kit.Flex,
333
+ {
334
+ variant: "row-center-start-nowrap-4",
335
+ style: { width: "110px", flexShrink: 0, color: botColor ? `var(--${botColor})` : void 0 }
336
+ },
337
+ import_react.default.createElement(import_plugin_ui_kit.Icon, { name: botIcon || "smartToy", size: 12 }),
338
+ import_react.default.createElement(import_plugin_ui_kit.Typography, {
339
+ variant: "smallCaption-regular",
340
+ color: "color-text-subtle"
341
+ }, botDisplayName || "-")
342
+ ),
343
+ // Status
344
+ import_react.default.createElement(import_plugin_ui_kit.Typography, {
345
+ variant: "smallCaption-regular",
346
+ color: isExecuting ? "color-brand-main" : "color-text-subtle",
347
+ style: { width: "70px", flexShrink: 0 }
348
+ }, label),
349
+ // Current task
350
+ import_react.default.createElement(import_plugin_ui_kit.Typography, {
351
+ variant: "smallCaption-regular",
352
+ color: isExecuting ? "color-text-medium" : "color-text-subtle",
353
+ style: { flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }
354
+ }, taskText),
355
+ // Tokens
356
+ import_react.default.createElement(import_plugin_ui_kit.Typography, {
357
+ variant: "smallCaption-regular",
358
+ color: "color-text-subtle",
359
+ style: { width: "50px", flexShrink: 0, textAlign: "right" }
360
+ }, formatTokens(tokensUsed)),
361
+ // Cost
362
+ import_react.default.createElement(import_plugin_ui_kit.Typography, {
363
+ variant: "smallCaption-regular",
364
+ color: "color-text-subtle",
365
+ style: { width: "50px", flexShrink: 0, textAlign: "right" }
366
+ }, formatCost(cost)),
367
+ // Actions
368
+ import_react.default.createElement(
369
+ import_plugin_ui_kit.Flex,
370
+ {
371
+ variant: "row-center-end-nowrap-1",
372
+ style: { width: "50px", flexShrink: 0 }
373
+ },
374
+ isExecuting && onPause && import_react.default.createElement(import_plugin_ui_kit.IconButton, {
375
+ icon: "pause",
376
+ size: "xs",
377
+ variant: "clear",
378
+ onClick: () => onPause(botId),
379
+ title: "Pause"
380
+ }),
381
+ isPaused && onResume && import_react.default.createElement(import_plugin_ui_kit.IconButton, {
382
+ icon: "playArrow",
383
+ size: "xs",
384
+ variant: "clear",
385
+ onClick: () => onResume(botId),
386
+ title: "Resume"
387
+ }),
388
+ (isExecuting || isPaused) && onStop && import_react.default.createElement(import_plugin_ui_kit.IconButton, {
389
+ icon: "stop",
390
+ size: "xs",
391
+ variant: "clear",
392
+ color: "danger",
393
+ onClick: () => onStop(botId),
394
+ title: "Stop"
395
+ })
396
+ )
397
+ );
398
+ }
399
+ var bot_slot_card_default = BotSlotCard;
400
+
401
+ // src/ui/task-pool-list.tsx
402
+ var React4 = require("react");
403
+ var { useState: useState2 } = React4;
404
+ var { Flex: Flex4, Typography: Typography4, Icon: Icon2, StatusIcon: StatusIcon2, Tag, ScrollArea, Badge, EmptyState } = require("@fw/plugin-ui-kit");
405
+ var statusToIcon = {
406
+ "pending": "pending",
407
+ "in-progress": "running",
408
+ "done": "completed",
409
+ "failed": "failed",
410
+ "blocked": "pending",
411
+ "cancelled": "failed"
412
+ };
413
+ var rowBaseStyle = {
414
+ padding: "8px 12px",
415
+ cursor: "pointer",
416
+ borderBottom: "1px solid var(--color-border-default)"
417
+ };
418
+ var indentedRowStyle = {
419
+ ...rowBaseStyle,
420
+ paddingLeft: "32px"
421
+ };
422
+ function sortTasks(tasks) {
423
+ return [...tasks].sort((a, b) => {
424
+ if (b.priority !== a.priority) return b.priority - a.priority;
425
+ return a.createdAt.localeCompare(b.createdAt);
426
+ });
427
+ }
428
+ function buildHierarchy(tasks) {
429
+ const sorted = sortTasks(tasks);
430
+ const topLevel = sorted.filter((t) => !t.parentId);
431
+ const subtasksByParent = /* @__PURE__ */ new Map();
432
+ for (const t of sorted) {
433
+ if (t.parentId) {
434
+ const existing = subtasksByParent.get(t.parentId) || [];
435
+ existing.push(t);
436
+ subtasksByParent.set(t.parentId, existing);
437
+ }
438
+ }
439
+ const result = [];
440
+ for (const parent of topLevel) {
441
+ result.push({ task: parent, indent: false });
442
+ const children = subtasksByParent.get(parent.id);
443
+ if (children) {
444
+ for (const child of children) {
445
+ result.push({ task: child, indent: true });
446
+ }
447
+ }
448
+ }
449
+ const topLevelIds = new Set(topLevel.map((t) => t.id));
450
+ for (const t of sorted) {
451
+ if (t.parentId && !topLevelIds.has(t.parentId)) {
452
+ result.push({ task: t, indent: false });
453
+ }
454
+ }
455
+ return result;
456
+ }
457
+ function TaskRowItem({ task, indent, onClick }) {
458
+ const [hovered, setHovered] = useState2(false);
459
+ const style = {
460
+ ...indent ? indentedRowStyle : rowBaseStyle,
461
+ ...hovered ? { backgroundColor: "var(--color-surface-elevated)" } : void 0
462
+ };
463
+ return React4.createElement(
464
+ Flex4,
465
+ {
466
+ variant: "row-center-start-nowrap-8",
467
+ style,
468
+ onClick,
469
+ onMouseEnter: () => setHovered(true),
470
+ onMouseLeave: () => setHovered(false)
471
+ },
472
+ // Status icon
473
+ React4.createElement(StatusIcon2, {
474
+ status: statusToIcon[task.status] || "pending",
475
+ size: "sm"
476
+ }),
477
+ // Title
478
+ React4.createElement(
479
+ Flex4,
480
+ {
481
+ variant: "row-center-start-nowrap-0",
482
+ style: { flex: 1, minWidth: 0 }
483
+ },
484
+ React4.createElement(Typography4, {
485
+ variant: "caption-regular",
486
+ color: task.status === "cancelled" ? "color-text-subtle" : "color-text-high",
487
+ truncate: true,
488
+ style: task.status === "cancelled" ? { textDecoration: "line-through", opacity: 0.6 } : void 0
489
+ }, task.title)
490
+ ),
491
+ // Assigned profile as Tag
492
+ task.assignedProfile && React4.createElement(Tag, {
493
+ size: "small",
494
+ color: "info"
495
+ }, task.assignedProfile),
496
+ // Priority badge (only if > 0)
497
+ task.priority > 0 && React4.createElement(Badge, {
498
+ variant: task.priority >= 3 ? "warning" : "info"
499
+ }, `P${task.priority}`)
500
+ );
501
+ }
502
+ function TaskPoolList({ tasks, onTaskClick }) {
503
+ if (!tasks || tasks.length === 0) {
504
+ return React4.createElement(EmptyState, {
505
+ icon: "inbox",
506
+ message: "No tasks in the pool."
507
+ });
508
+ }
509
+ const rows = buildHierarchy(tasks);
510
+ return React4.createElement(
511
+ Flex4,
512
+ {
513
+ variant: "column-stretch-start-nowrap-0",
514
+ style: { height: "100%", overflow: "hidden" }
515
+ },
516
+ React4.createElement(
517
+ ScrollArea,
518
+ { style: { flex: 1 } },
519
+ rows.map(
520
+ ({ task, indent }) => React4.createElement(TaskRowItem, {
521
+ key: task.id,
522
+ task,
523
+ indent,
524
+ onClick: () => onTaskClick(task.id)
525
+ })
526
+ )
527
+ )
528
+ );
529
+ }
530
+ var task_pool_list_default = TaskPoolList;
531
+ module.exports = TaskPoolList;
532
+
533
+ // src/ui/task-create-form.tsx
534
+ var import_react2 = __toESM(require("react"), 1);
535
+ var import_plugin_ui_kit2 = require("@fw/plugin-ui-kit");
536
+ var { useState: useState3, useCallback: useCallback2 } = import_react2.default;
537
+ function parseToolResult(raw) {
538
+ if (typeof raw === "string") {
539
+ try {
540
+ return JSON.parse(raw);
541
+ } catch {
542
+ return raw;
543
+ }
544
+ }
545
+ return raw;
546
+ }
547
+ function TaskCreateForm({ onTaskCreated }) {
548
+ const ctx = (0, import_plugin_ui_kit2.usePackWorkspace)();
549
+ const { callTool } = ctx;
550
+ const [title, setTitle] = useState3("");
551
+ const [creating, setCreating] = useState3(false);
552
+ const handleCreate = useCallback2(async () => {
553
+ const trimmed = title.trim();
554
+ if (!trimmed) return;
555
+ setCreating(true);
556
+ try {
557
+ const raw = await callTool("fw_weaver_task_create", { title: trimmed });
558
+ const result = parseToolResult(raw);
559
+ (0, import_plugin_ui_kit2.toast)(`Task created: ${result?.task?.title ?? trimmed}`, { type: "success" });
560
+ setTitle("");
561
+ onTaskCreated?.();
562
+ } catch (err) {
563
+ (0, import_plugin_ui_kit2.toast)(err instanceof Error ? err.message : "Failed to create task", { type: "error" });
564
+ } finally {
565
+ setCreating(false);
566
+ }
567
+ }, [title, callTool, onTaskCreated]);
568
+ return import_react2.default.createElement(
569
+ import_plugin_ui_kit2.Flex,
570
+ {
571
+ variant: "row-center-start-nowrap-6"
572
+ },
573
+ import_react2.default.createElement(import_plugin_ui_kit2.Input, {
574
+ type: "text",
575
+ size: "small",
576
+ placeholder: "What needs to be done?",
577
+ value: title,
578
+ onChange: (v) => setTitle(v),
579
+ onEnter: handleCreate,
580
+ disabled: creating,
581
+ defaultBoxStyle: { flex: 1, minWidth: 0 },
582
+ inputBoxStyle: { maxWidth: "none" }
583
+ }),
584
+ import_react2.default.createElement(import_plugin_ui_kit2.IconButton, {
585
+ icon: "add",
586
+ size: "sm",
587
+ variant: "fill",
588
+ color: "primary",
589
+ onClick: handleCreate,
590
+ disabled: creating || !title.trim(),
591
+ loading: creating
592
+ })
593
+ );
594
+ }
595
+ var task_create_form_default = TaskCreateForm;
596
+
597
+ // src/ui/use-stream-timeline.ts
598
+ var React6 = require("react");
599
+ var { useMemo, useState: useState4, useEffect, useRef } = React6;
600
+ function useStreamTimeline(events, isDone) {
601
+ const [elapsed, setElapsed] = useState4(0);
602
+ const startTimeRef = useRef(null);
603
+ useEffect(() => {
604
+ if (events.length === 0) {
605
+ startTimeRef.current = null;
606
+ setElapsed(0);
607
+ } else if (startTimeRef.current === null) {
608
+ startTimeRef.current = events[0].timestamp;
609
+ }
610
+ }, [events.length]);
611
+ useEffect(() => {
612
+ if (isDone || !startTimeRef.current) return;
613
+ const tick = () => setElapsed(Date.now() - (startTimeRef.current ?? Date.now()));
614
+ tick();
615
+ const interval = setInterval(tick, 1e3);
616
+ return () => clearInterval(interval);
617
+ }, [isDone, events.length]);
618
+ const timeline = useMemo(() => {
619
+ const entries = [];
620
+ const nodeEntryIndex = /* @__PURE__ */ new Map();
621
+ const nodeStarts = /* @__PURE__ */ new Map();
622
+ let idCounter = 0;
623
+ for (const event of events) {
624
+ const d = event.data ?? {};
625
+ switch (event.type) {
626
+ case "bot-started":
627
+ entries.push({
628
+ id: `s-${idCounter++}`,
629
+ timestamp: new Date(event.timestamp),
630
+ type: "task-started",
631
+ label: "Task started",
632
+ detail: d.instruction
633
+ });
634
+ break;
635
+ case "node-start": {
636
+ const nodeId = d.nodeId;
637
+ if (nodeId) nodeStarts.set(nodeId, event.timestamp);
638
+ const idx = entries.length;
639
+ nodeEntryIndex.set(nodeId, idx);
640
+ entries.push({
641
+ id: `s-${idCounter++}`,
642
+ timestamp: new Date(event.timestamp),
643
+ type: "node-started",
644
+ nodeId,
645
+ label: d.label ?? d.nodeType ?? nodeId ?? "Node"
646
+ });
647
+ break;
648
+ }
649
+ case "node-complete": {
650
+ const nodeId = d.nodeId;
651
+ const startTs = nodeStarts.get(nodeId);
652
+ const duration = d.durationMs ?? (startTs ? event.timestamp - startTs : void 0);
653
+ if (nodeId) nodeStarts.delete(nodeId);
654
+ const rawOutputs = d.outputs;
655
+ const completed = {
656
+ id: `s-${idCounter++}`,
657
+ timestamp: new Date(startTs ?? event.timestamp),
658
+ type: "node-completed",
659
+ nodeId,
660
+ label: d.label ?? d.nodeType ?? nodeId ?? "Node",
661
+ duration,
662
+ outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : void 0
663
+ };
664
+ const existingIdx = nodeEntryIndex.get(nodeId);
665
+ if (existingIdx != null && entries[existingIdx]) {
666
+ entries[existingIdx] = completed;
667
+ nodeEntryIndex.delete(nodeId);
668
+ } else {
669
+ entries.push(completed);
670
+ }
671
+ break;
672
+ }
673
+ case "node-error": {
674
+ const nodeId = d.nodeId;
675
+ const startTs = nodeStarts.get(nodeId);
676
+ const duration = d.durationMs ?? (startTs ? event.timestamp - startTs : void 0);
677
+ if (nodeId) nodeStarts.delete(nodeId);
678
+ const rawOutputs = d.outputs;
679
+ const failed = {
680
+ id: `s-${idCounter++}`,
681
+ timestamp: new Date(startTs ?? event.timestamp),
682
+ type: "node-failed",
683
+ nodeId,
684
+ label: d.label ?? d.nodeType ?? nodeId ?? "Node",
685
+ detail: d.error,
686
+ duration,
687
+ outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : void 0
688
+ };
689
+ const existingIdx = nodeEntryIndex.get(nodeId);
690
+ if (existingIdx != null && entries[existingIdx]) {
691
+ entries[existingIdx] = failed;
692
+ nodeEntryIndex.delete(nodeId);
693
+ } else {
694
+ entries.push(failed);
695
+ }
696
+ break;
697
+ }
698
+ case "bot-completed":
699
+ entries.push({
700
+ id: `s-${idCounter++}`,
701
+ timestamp: new Date(event.timestamp),
702
+ type: "task-completed",
703
+ label: d.success ? "Task completed" : "Task finished",
704
+ detail: d.summary
705
+ });
706
+ break;
707
+ case "bot-failed":
708
+ entries.push({
709
+ id: `s-${idCounter++}`,
710
+ timestamp: new Date(event.timestamp),
711
+ type: "task-failed",
712
+ label: "Task failed",
713
+ detail: d.error
714
+ });
715
+ break;
716
+ // Skip audit, cost-update, done — they're used for metadata, not timeline
717
+ default:
718
+ break;
719
+ }
720
+ }
721
+ return entries;
722
+ }, [events]);
723
+ const { phase, instruction, cost, plan, awaitingApproval } = useMemo(() => {
724
+ let phase2 = "idle";
725
+ let instruction2 = null;
726
+ let cost2 = null;
727
+ let plan2 = null;
728
+ let awaitingApproval2 = false;
729
+ for (const event of events) {
730
+ const d = event.data ?? {};
731
+ if (event.type === "bot-started") {
732
+ phase2 = "planning";
733
+ instruction2 = d.instruction ?? null;
734
+ } else if (event.type === "node-start" && phase2 !== "completed" && phase2 !== "failed") {
735
+ phase2 = "executing";
736
+ } else if (event.type === "bot-completed") {
737
+ phase2 = d.success ? "completed" : "failed";
738
+ } else if (event.type === "bot-failed") {
739
+ phase2 = "failed";
740
+ } else if (event.type === "cost-update") {
741
+ cost2 = d.totalCost ?? cost2;
742
+ } else if (event.type === "audit:plan-created" || event.type === "plan-created") {
743
+ const planData = d.plan ?? d;
744
+ const summary = planData.summary ?? "";
745
+ const steps = planData.steps ?? [];
746
+ if (summary || steps.length > 0) {
747
+ plan2 = { summary, steps };
748
+ }
749
+ } else if (event.type === "approval-needed") {
750
+ awaitingApproval2 = true;
751
+ } else if (event.type === "audit:approval-decision") {
752
+ awaitingApproval2 = false;
753
+ }
754
+ }
755
+ if (isDone && phase2 !== "completed" && phase2 !== "failed") {
756
+ phase2 = "completed";
757
+ }
758
+ return { phase: phase2, instruction: instruction2, cost: cost2, plan: plan2, awaitingApproval: awaitingApproval2 };
759
+ }, [events, isDone]);
760
+ return { timeline, phase, instruction, elapsed, cost, plan, awaitingApproval };
761
+ }
762
+
763
+ // src/ui/trace-to-timeline.ts
764
+ function traceToTimeline(run) {
765
+ const entries = [];
766
+ let idCounter = 0;
767
+ const meta = run.nodeMeta ?? {};
768
+ if (run.trace && run.trace.length > 0) {
769
+ const nodeStarts = /* @__PURE__ */ new Map();
770
+ for (const event of run.trace) {
771
+ if (event.type === "node-start") {
772
+ nodeStarts.set(event.nodeId, event);
773
+ } else if (event.type === "node-complete" || event.type === "node-error") {
774
+ const start = nodeStarts.get(event.nodeId);
775
+ const duration = event.durationMs ?? (start ? event.timestamp - start.timestamp : void 0);
776
+ const nm = meta[event.nodeId] ?? (event.nodeType ? meta[event.nodeType] : void 0);
777
+ entries.push({
778
+ id: `trace-${idCounter++}`,
779
+ timestamp: new Date(event.timestamp),
780
+ type: event.type === "node-complete" ? "node-completed" : "node-failed",
781
+ nodeId: event.nodeId,
782
+ label: nm?.label ?? event.nodeType ?? event.nodeId,
783
+ detail: event.error,
784
+ duration,
785
+ color: nm?.color,
786
+ icon: nm?.icon,
787
+ outputs: event.outputs && event.outputs.length > 0 ? event.outputs : void 0
788
+ });
789
+ nodeStarts.delete(event.nodeId);
790
+ }
791
+ }
792
+ for (const [nodeId, event] of nodeStarts) {
793
+ const nm = meta[nodeId] ?? (event.nodeType ? meta[event.nodeType] : void 0);
794
+ entries.push({
795
+ id: `trace-${idCounter++}`,
796
+ timestamp: new Date(event.timestamp),
797
+ type: "node-started",
798
+ nodeId,
799
+ label: nm?.label ?? event.nodeType ?? nodeId,
800
+ color: nm?.color,
801
+ icon: nm?.icon
802
+ });
803
+ }
804
+ } else if (run.stepLog && run.stepLog.length > 0) {
805
+ for (const step of run.stepLog) {
806
+ const type = step.status === "ok" ? "node-completed" : step.status === "error" ? "node-failed" : "node-completed";
807
+ entries.push({
808
+ id: `step-${idCounter++}`,
809
+ timestamp: new Date(run.startedAt ?? Date.now()),
810
+ type,
811
+ label: step.step,
812
+ detail: step.detail,
813
+ icon: step.status === "ok" ? "success" : step.status === "error" ? "error" : "warning"
814
+ });
815
+ }
816
+ }
817
+ if (run.outcome === "failed" || run.outcome === "error") {
818
+ let errorDetail = "";
819
+ if (run.auditTrail) {
820
+ for (const a of run.auditTrail) {
821
+ if (a.type === "step-complete" && a.data) {
822
+ const errors = a.data.errors;
823
+ if (errors && errors.length > 0) {
824
+ errorDetail = errors.map((e) => e.length > 120 ? e.slice(0, 117) + "..." : e).join("\n");
825
+ break;
826
+ }
827
+ }
828
+ }
829
+ }
830
+ if (!errorDetail && run.summary) {
831
+ const parts = run.summary.split("|").map((p) => p.trim());
832
+ const outcomePart = parts.find((p) => p.startsWith("Outcome:"));
833
+ const summaryPart = parts.find((p) => p.startsWith("Summary:"));
834
+ errorDetail = summaryPart?.replace("Summary:", "").trim() ?? outcomePart ?? run.summary;
835
+ }
836
+ entries.push({
837
+ id: `result-${idCounter++}`,
838
+ timestamp: new Date(run.finishedAt ?? run.startedAt ?? Date.now()),
839
+ type: "task-failed",
840
+ label: "Task failed",
841
+ detail: errorDetail || "Unknown failure",
842
+ icon: "error"
843
+ });
844
+ } else if (run.outcome === "completed" && run.success) {
845
+ entries.push({
846
+ id: `result-${idCounter++}`,
847
+ timestamp: new Date(run.finishedAt ?? run.startedAt ?? Date.now()),
848
+ type: "task-completed",
849
+ label: "Task completed",
850
+ detail: run.summary?.includes("|") ? run.summary.split("|").find((p) => p.trim().startsWith("Summary:"))?.replace("Summary:", "").trim() : void 0
851
+ });
852
+ }
853
+ entries.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
854
+ return entries;
855
+ }
856
+
857
+ // src/ui/task-detail-view.tsx
858
+ var React7 = require("react");
859
+ var { useState: useState5, useEffect: useEffect2, useCallback: useCallback3, useRef: useRef2, useMemo: useMemo2 } = React7;
860
+ var {
861
+ Flex: Flex6,
862
+ Typography: Typography5,
863
+ ScrollArea: ScrollArea2,
864
+ StatusIcon: StatusIcon3,
865
+ Tag: Tag2,
866
+ Badge: Badge2,
867
+ Icon: Icon3,
868
+ IconButton: IconButton4,
869
+ TaskBlock,
870
+ Button: Button2,
871
+ Card,
872
+ Chip,
873
+ Checkbox,
874
+ Table,
875
+ Tabs,
876
+ EmptyState: EmptyState2,
877
+ toast: toast3,
878
+ usePackWorkspace: usePackWorkspace3,
879
+ useEventStream
880
+ } = require("@fw/plugin-ui-kit");
881
+ var statusToIcon2 = {
882
+ "pending": "pending",
883
+ "in-progress": "running",
884
+ "done": "completed",
885
+ "failed": "failed",
886
+ "blocked": "pending",
887
+ "cancelled": "failed"
888
+ };
889
+ var statusToLabel2 = {
890
+ "pending": "Pending",
891
+ "in-progress": "In Progress",
892
+ "done": "Done",
893
+ "failed": "Failed",
894
+ "blocked": "Blocked",
895
+ "cancelled": "Cancelled"
896
+ };
897
+ var headerStyle = {
898
+ padding: "12px 16px",
899
+ borderBottom: "1px solid var(--color-border-default)",
900
+ flexShrink: 0
901
+ };
902
+ var fileItemStyle = {
903
+ fontFamily: "monospace",
904
+ fontSize: "12px",
905
+ color: "var(--color-text-mid)",
906
+ padding: "2px 4px",
907
+ borderRadius: "4px"
908
+ };
909
+ var fileItemHoverStyle = {
910
+ ...fileItemStyle,
911
+ backgroundColor: "var(--color-surface-elevated)"
912
+ };
913
+ var subtaskRowStyle = {
914
+ padding: "6px 8px",
915
+ cursor: "pointer",
916
+ borderRadius: "6px"
917
+ };
918
+ var subtaskRowHoverStyle = {
919
+ ...subtaskRowStyle,
920
+ backgroundColor: "var(--color-surface-elevated)"
921
+ };
922
+ function SubtaskRowItem({ sub, onBack }) {
923
+ const [hovered, setHovered] = useState5(false);
924
+ return React7.createElement(
925
+ Flex6,
926
+ {
927
+ variant: "row-center-start-nowrap-8",
928
+ style: hovered ? subtaskRowHoverStyle : subtaskRowStyle,
929
+ onClick: () => onBack(),
930
+ onMouseEnter: () => setHovered(true),
931
+ onMouseLeave: () => setHovered(false)
932
+ },
933
+ React7.createElement(StatusIcon3, {
934
+ status: statusToIcon2[sub.status] || "pending",
935
+ size: "sm"
936
+ }),
937
+ React7.createElement(
938
+ Flex6,
939
+ {
940
+ variant: "row-center-start-nowrap-0",
941
+ style: { flex: 1, minWidth: 0 }
942
+ },
943
+ React7.createElement(Typography5, { variant: "caption-regular", truncate: true }, sub.title)
944
+ ),
945
+ sub.assignedProfile && React7.createElement(Tag2, {
946
+ size: "small",
947
+ color: "info"
948
+ }, sub.assignedProfile),
949
+ React7.createElement(Typography5, {
950
+ variant: "caption-regular",
951
+ color: "color-text-subtle"
952
+ }, `#${sub.attempt}`)
953
+ );
954
+ }
955
+ function TaskDetailView({ taskId, onBack }) {
956
+ const ctx = usePackWorkspace3();
957
+ const { callTool } = ctx;
958
+ const packId = ctx.packId;
959
+ const [task, setTask] = useState5(null);
960
+ const [subtasks, setSubtasks] = useState5([]);
961
+ const [history, setHistory] = useState5([]);
962
+ const [loading, setLoading] = useState5(true);
963
+ const fetchTask = useCallback3(async () => {
964
+ try {
965
+ const raw = await callTool("fw_weaver_task_get", { id: taskId });
966
+ const data = typeof raw === "string" ? JSON.parse(raw) : raw;
967
+ if (!data) return;
968
+ const t = data.task ?? data;
969
+ setTask(t);
970
+ if (data.subtasks && Array.isArray(data.subtasks)) {
971
+ setSubtasks(data.subtasks);
972
+ } else if (t.isParent) {
973
+ const listRaw = await callTool("fw_weaver_task_list", { parentId: taskId });
974
+ const listData = typeof listRaw === "string" ? JSON.parse(listRaw) : listRaw;
975
+ if (Array.isArray(listData)) {
976
+ setSubtasks(listData);
977
+ }
978
+ }
979
+ } catch (err) {
980
+ }
981
+ }, [callTool, taskId]);
982
+ const fetchHistory = useCallback3(async () => {
983
+ try {
984
+ const raw = await callTool("fw_weaver_history", { taskId });
985
+ const data = typeof raw === "string" ? JSON.parse(raw) : raw;
986
+ if (Array.isArray(data)) {
987
+ setHistory(
988
+ data.map((r) => {
989
+ const costObj = r.cost && typeof r.cost === "object" ? r.cost : void 0;
990
+ return { ...r, costDetail: costObj, cost: costObj?.totalCost };
991
+ })
992
+ );
993
+ }
994
+ } catch {
995
+ }
996
+ }, [callTool, taskId]);
997
+ useEffect2(() => {
998
+ setLoading(true);
999
+ Promise.all([fetchTask(), fetchHistory()]).finally(() => setLoading(false));
1000
+ }, [fetchTask, fetchHistory]);
1001
+ useEffect2(() => {
1002
+ if (!task || task.status !== "in-progress") return;
1003
+ const interval = setInterval(() => {
1004
+ fetchTask();
1005
+ fetchHistory();
1006
+ }, 5e3);
1007
+ return () => clearInterval(interval);
1008
+ }, [task?.status, fetchTask, fetchHistory]);
1009
+ const stream = useEventStream();
1010
+ const {
1011
+ timeline: liveTimeline,
1012
+ phase: livePhase,
1013
+ instruction: liveInstruction,
1014
+ elapsed,
1015
+ cost: liveCost,
1016
+ plan,
1017
+ awaitingApproval
1018
+ } = useStreamTimeline(stream.events, stream.isDone);
1019
+ const currentRunId = task?.currentRunId;
1020
+ const isLive = task?.status === "in-progress" && !!currentRunId;
1021
+ useEffect2(() => {
1022
+ if (!isLive || !currentRunId) return;
1023
+ stream.start(packId, "fw_weaver_events", currentRunId);
1024
+ return () => stream.stop();
1025
+ }, [isLive, currentRunId, packId]);
1026
+ const [detailTab, setDetailTab] = useState5("runs");
1027
+ const [actionLoading, setActionLoading] = useState5(null);
1028
+ const [availableProfiles, setAvailableProfiles] = useState5([]);
1029
+ useEffect2(() => {
1030
+ callTool("fw_weaver_profile_list", {}).then((raw) => {
1031
+ const data = typeof raw === "string" ? JSON.parse(raw) : raw;
1032
+ if (Array.isArray(data)) setAvailableProfiles(data);
1033
+ }).catch(() => {
1034
+ });
1035
+ }, [callTool]);
1036
+ const handleRetry = useCallback3(async () => {
1037
+ setActionLoading("retry");
1038
+ try {
1039
+ await callTool("fw_weaver_task_retry", { id: taskId });
1040
+ toast3("Task retried, back in pool", { type: "success" });
1041
+ fetchTask();
1042
+ } catch (err) {
1043
+ toast3(err instanceof Error ? err.message : "Failed to retry", { type: "error" });
1044
+ }
1045
+ setActionLoading(null);
1046
+ }, [callTool, taskId, fetchTask]);
1047
+ const handleCancel = useCallback3(async () => {
1048
+ const ok = await ctx.confirm("Cancel this task? It cannot be undone.", {
1049
+ title: "Cancel Task",
1050
+ confirmLabel: "Cancel Task",
1051
+ state: "warning"
1052
+ });
1053
+ if (!ok) return;
1054
+ setActionLoading("cancel");
1055
+ try {
1056
+ await callTool("fw_weaver_task_cancel", { id: taskId });
1057
+ toast3("Task cancelled", { type: "info" });
1058
+ fetchTask();
1059
+ } catch (err) {
1060
+ toast3(err instanceof Error ? err.message : "Failed to cancel", { type: "error" });
1061
+ }
1062
+ setActionLoading(null);
1063
+ }, [callTool, taskId, fetchTask, ctx]);
1064
+ const handleAssignProfile = useCallback3(async (profileId) => {
1065
+ setActionLoading("assign-profile");
1066
+ try {
1067
+ const newProfile = task?.assignedProfile === profileId ? void 0 : profileId;
1068
+ await callTool("fw_weaver_task_update", { id: taskId, assignedProfile: newProfile ?? null });
1069
+ toast3(newProfile ? `Assigned profile ${profileId}` : `Unassigned profile`, { type: "success" });
1070
+ fetchTask();
1071
+ } catch (err) {
1072
+ toast3(err instanceof Error ? err.message : "Failed to assign profile", { type: "error" });
1073
+ }
1074
+ setActionLoading(null);
1075
+ }, [callTool, taskId, task, fetchTask]);
1076
+ const handlePriorityChange = useCallback3(async (delta) => {
1077
+ const newPriority = Math.max(0, (task?.priority ?? 0) + delta);
1078
+ try {
1079
+ await callTool("fw_weaver_task_update", { id: taskId, priority: newPriority });
1080
+ fetchTask();
1081
+ } catch {
1082
+ }
1083
+ }, [callTool, taskId, task, fetchTask]);
1084
+ const [expandedRunId, setExpandedRunId] = useState5(null);
1085
+ const [liveExpanded, setLiveExpanded] = useState5(true);
1086
+ const toggleExpand = useCallback3((id) => {
1087
+ setExpandedRunId((prev) => prev === id ? null : id);
1088
+ }, []);
1089
+ const scrollRef = useRef2(null);
1090
+ useEffect2(() => {
1091
+ const el = scrollRef.current;
1092
+ if (el) el.scrollTop = el.scrollHeight;
1093
+ }, [liveTimeline.length, stream.events.length]);
1094
+ const [approvalStatus, setApprovalStatus] = useState5(null);
1095
+ const [approvalLoading, setApprovalLoading] = useState5(false);
1096
+ useEffect2(() => {
1097
+ if (awaitingApproval) setApprovalStatus("pending");
1098
+ }, [awaitingApproval]);
1099
+ useEffect2(() => {
1100
+ if (stream.events.length === 0) setApprovalStatus(null);
1101
+ }, [stream.events.length]);
1102
+ const handleApprove = useCallback3(async () => {
1103
+ setApprovalLoading(true);
1104
+ try {
1105
+ await callTool("fw_weaver_approve", { approved: true });
1106
+ setApprovalStatus("approved");
1107
+ toast3("Plan approved", { type: "success" });
1108
+ } catch (err) {
1109
+ toast3(err instanceof Error ? err.message : "Failed to approve", { type: "error" });
1110
+ }
1111
+ setApprovalLoading(false);
1112
+ }, [callTool]);
1113
+ const handleReject = useCallback3(async () => {
1114
+ setApprovalLoading(true);
1115
+ try {
1116
+ await callTool("fw_weaver_approve", { approved: false });
1117
+ setApprovalStatus("rejected");
1118
+ toast3("Plan rejected", { type: "info" });
1119
+ } catch (err) {
1120
+ toast3(err instanceof Error ? err.message : "Failed to reject", { type: "error" });
1121
+ }
1122
+ setApprovalLoading(false);
1123
+ }, [callTool]);
1124
+ const runItems = useMemo2(() => {
1125
+ return history.map((run) => ({
1126
+ run,
1127
+ runTimeline: traceToTimeline(run)
1128
+ }));
1129
+ }, [history]);
1130
+ function extractInstruction(run) {
1131
+ if (run.instruction) {
1132
+ const text = run.instruction;
1133
+ return text.length > 120 ? text.slice(0, 117) + "..." : text;
1134
+ }
1135
+ if (run.summary) {
1136
+ const summary = run.summary;
1137
+ if (summary.includes("|")) {
1138
+ const parts = summary.split("|").map((p) => p.trim());
1139
+ const taskPart = parts.find((p) => p.startsWith("Task:"));
1140
+ const summaryPart = parts.find((p) => p.startsWith("Summary:"));
1141
+ const text = taskPart?.replace("Task:", "").trim() ?? summaryPart?.replace("Summary:", "").trim() ?? parts[0] ?? "";
1142
+ return text.length > 120 ? text.slice(0, 117) + "..." : text;
1143
+ }
1144
+ return summary.length > 120 ? summary.slice(0, 117) + "..." : summary;
1145
+ }
1146
+ return "Bot run";
1147
+ }
1148
+ if (loading && !task) {
1149
+ return React7.createElement(
1150
+ Flex6,
1151
+ {
1152
+ variant: "column-stretch-start-nowrap-0",
1153
+ style: { width: "100%", height: "100%", overflow: "hidden" }
1154
+ },
1155
+ React7.createElement(
1156
+ Flex6,
1157
+ {
1158
+ variant: "column-stretch-start-nowrap-8",
1159
+ style: headerStyle
1160
+ },
1161
+ React7.createElement(
1162
+ Button2,
1163
+ { variant: "ghost", size: "sm", onClick: onBack },
1164
+ React7.createElement(Icon3, { name: "arrowBack", size: 16 }),
1165
+ " Back"
1166
+ )
1167
+ ),
1168
+ React7.createElement(
1169
+ Flex6,
1170
+ {
1171
+ variant: "column-center-center-nowrap-0",
1172
+ style: { flex: 1 }
1173
+ },
1174
+ React7.createElement(
1175
+ Typography5,
1176
+ { variant: "caption-regular", color: "color-text-subtle" },
1177
+ "Loading task..."
1178
+ )
1179
+ )
1180
+ );
1181
+ }
1182
+ if (!task) {
1183
+ return React7.createElement(
1184
+ Flex6,
1185
+ {
1186
+ variant: "column-stretch-start-nowrap-0",
1187
+ style: { width: "100%", height: "100%", overflow: "hidden" }
1188
+ },
1189
+ React7.createElement(
1190
+ Flex6,
1191
+ {
1192
+ variant: "column-stretch-start-nowrap-8",
1193
+ style: headerStyle
1194
+ },
1195
+ React7.createElement(
1196
+ Button2,
1197
+ { variant: "ghost", size: "sm", onClick: onBack },
1198
+ React7.createElement(Icon3, { name: "arrowBack", size: 16 }),
1199
+ " Back"
1200
+ )
1201
+ ),
1202
+ React7.createElement(
1203
+ Flex6,
1204
+ {
1205
+ variant: "column-center-center-nowrap-0",
1206
+ style: { flex: 1 }
1207
+ },
1208
+ React7.createElement(
1209
+ Typography5,
1210
+ { variant: "caption-regular", color: "color-text-subtle" },
1211
+ "Task not found."
1212
+ )
1213
+ )
1214
+ );
1215
+ }
1216
+ const hasContext = task.context?.files?.length > 0 || task.context?.notes;
1217
+ const hasSubtasks = task.isParent && subtasks.length > 0;
1218
+ const hasRuns = runItems.length > 0 || isLive;
1219
+ return React7.createElement(
1220
+ Flex6,
1221
+ {
1222
+ variant: "column-stretch-start-nowrap-0",
1223
+ style: { width: "100%", height: "100%", overflow: "hidden" }
1224
+ },
1225
+ // ── Header ──
1226
+ React7.createElement(
1227
+ Flex6,
1228
+ {
1229
+ variant: "column-stretch-start-nowrap-6",
1230
+ style: { ...headerStyle, padding: "12px 16px", borderBottom: "1px solid var(--color-border-default)" }
1231
+ },
1232
+ // Top row: back + title + status
1233
+ React7.createElement(
1234
+ Flex6,
1235
+ { variant: "row-center-start-nowrap-8" },
1236
+ React7.createElement(IconButton4, {
1237
+ icon: "back",
1238
+ size: "xs",
1239
+ variant: "clear",
1240
+ onClick: onBack
1241
+ }),
1242
+ React7.createElement(StatusIcon3, {
1243
+ status: statusToIcon2[task.status] || "pending",
1244
+ size: "sm"
1245
+ }),
1246
+ React7.createElement(Typography5, {
1247
+ variant: "caption-thick",
1248
+ color: "color-text-high",
1249
+ style: { flex: 1, minWidth: 0, wordBreak: "break-word" }
1250
+ }, task.title || "Untitled Task")
1251
+ ),
1252
+ // Meta row: status tag, assigned profile, priority, attempts
1253
+ React7.createElement(
1254
+ Flex6,
1255
+ { variant: "row-center-start-wrap-6" },
1256
+ React7.createElement(Tag2, {
1257
+ size: "small",
1258
+ color: task.status === "done" ? "positive" : task.status === "failed" ? "negative" : task.status === "in-progress" ? "info" : task.status === "blocked" ? "caution" : "secondary"
1259
+ }, statusToLabel2[task.status] || task.status || "pending"),
1260
+ task.assignedProfile && React7.createElement(Tag2, { key: `profile-${task.assignedProfile}`, size: "small", color: "info" }, task.assignedProfile),
1261
+ task.priority > 0 && React7.createElement(Tag2, {
1262
+ size: "small",
1263
+ color: task.priority >= 3 ? "caution" : "info"
1264
+ }, `P${task.priority}`),
1265
+ task.attempt != null && task.maxAttempts != null && React7.createElement(Typography5, {
1266
+ variant: "smallCaption-regular",
1267
+ color: "color-text-subtle"
1268
+ }, `Attempt ${task.attempt}/${task.maxAttempts}`)
1269
+ ),
1270
+ // Description
1271
+ task.description && React7.createElement(Typography5, {
1272
+ variant: "smallCaption-regular",
1273
+ color: "color-text-medium"
1274
+ }, task.description),
1275
+ // Profile routing info
1276
+ task.assignedProfile && React7.createElement(
1277
+ Flex6,
1278
+ { variant: "column-stretch-start-nowrap-2" },
1279
+ React7.createElement(Typography5, {
1280
+ variant: "smallCaption-regular",
1281
+ color: "color-text-medium"
1282
+ }, `Profile: ${task.assignedProfile}`),
1283
+ task.routingReason && React7.createElement(Typography5, {
1284
+ variant: "smallCaption-regular",
1285
+ color: "color-text-subtle"
1286
+ }, task.routingReason)
1287
+ )
1288
+ // (Actions moved to tab)
1289
+ ),
1290
+ // ── Tabs ──
1291
+ React7.createElement(Tabs, {
1292
+ tabs: [
1293
+ { id: "runs", title: `Runs (${runItems.length}${isLive ? "+1" : ""})` },
1294
+ ...hasSubtasks ? [{ id: "subtasks", title: `Subtasks (${subtasks.filter((s) => s.status === "done").length}/${subtasks.length})` }] : [],
1295
+ ...task.status !== "done" && task.status !== "cancelled" ? [{ id: "actions", title: "Actions" }] : [],
1296
+ ...hasContext ? [{ id: "context", title: "Context" }] : []
1297
+ ],
1298
+ activeTabId: detailTab,
1299
+ onSelectTab: (id) => setDetailTab(id),
1300
+ size: "sm"
1301
+ }),
1302
+ // ── Tab content ──
1303
+ React7.createElement(
1304
+ ScrollArea2,
1305
+ { ref: scrollRef, style: { flex: 1 } },
1306
+ React7.createElement(
1307
+ Flex6,
1308
+ { variant: "column-stretch-start-nowrap-8", style: { padding: "12px 16px" } },
1309
+ // ── Runs tab ──
1310
+ detailTab === "runs" && (hasRuns || (task.runs?.length ?? 0) > 0) ? React7.createElement(
1311
+ Flex6,
1312
+ { variant: "column-stretch-start-nowrap-8" },
1313
+ ...runItems.map(({ run, runTimeline }) => {
1314
+ const runId = run.id;
1315
+ const isExpanded = expandedRunId === runId;
1316
+ const isSuccess = run.outcome === "completed" || run.success === true;
1317
+ return React7.createElement(TaskBlock, {
1318
+ key: `run-${runId}`,
1319
+ state: isSuccess ? "completed" : "failed",
1320
+ instruction: extractInstruction(run),
1321
+ timeline: runTimeline,
1322
+ cost: typeof run.cost === "number" ? run.cost : run.costDetail?.totalCost ?? null,
1323
+ plan: run.plan,
1324
+ startedAt: run.startedAt,
1325
+ durationMs: run.durationMs ?? run.duration,
1326
+ expanded: isExpanded,
1327
+ onToggleExpand: () => toggleExpand(runId)
1328
+ });
1329
+ }),
1330
+ // Live run
1331
+ isLive && React7.createElement(TaskBlock, {
1332
+ key: "live-run",
1333
+ state: "running",
1334
+ instruction: liveInstruction ?? task.title,
1335
+ timeline: liveTimeline,
1336
+ phase: livePhase,
1337
+ elapsed,
1338
+ cost: liveCost,
1339
+ plan,
1340
+ error: stream.error,
1341
+ approval: approvalStatus ?? void 0,
1342
+ approvalLoading,
1343
+ onApprove: handleApprove,
1344
+ onReject: handleReject,
1345
+ expanded: liveExpanded,
1346
+ onToggleExpand: () => setLiveExpanded((v) => !v)
1347
+ })
1348
+ ) : detailTab === "runs" && React7.createElement(EmptyState2, {
1349
+ icon: "smartToy",
1350
+ message: "No runs yet",
1351
+ description: "This task has not been executed yet."
1352
+ }),
1353
+ // ── Subtasks tab ──
1354
+ detailTab === "subtasks" && hasSubtasks && React7.createElement(
1355
+ Flex6,
1356
+ {
1357
+ variant: "column-stretch-start-nowrap-0"
1358
+ },
1359
+ ...(subtasks ?? []).map(
1360
+ (sub) => React7.createElement(SubtaskRowItem, {
1361
+ key: sub.id,
1362
+ sub,
1363
+ onBack
1364
+ })
1365
+ )
1366
+ ),
1367
+ // ── Actions tab ──
1368
+ detailTab === "actions" && React7.createElement(
1369
+ Flex6,
1370
+ {
1371
+ variant: "column-stretch-start-nowrap-16"
1372
+ },
1373
+ // Status actions
1374
+ React7.createElement(
1375
+ Flex6,
1376
+ { variant: "column-stretch-start-nowrap-8" },
1377
+ React7.createElement(Typography5, { variant: "caption-thick", color: "color-text-medium" }, "Status"),
1378
+ React7.createElement(
1379
+ Flex6,
1380
+ { variant: "row-center-start-nowrap-6" },
1381
+ task.status === "failed" && React7.createElement(Button2, {
1382
+ size: "xs",
1383
+ variant: "fill",
1384
+ color: "primary",
1385
+ onClick: handleRetry,
1386
+ loading: actionLoading === "retry",
1387
+ disabled: !!actionLoading
1388
+ }, "Retry Task"),
1389
+ (task.status === "pending" || task.status === "in-progress" || task.status === "blocked") && React7.createElement(Button2, {
1390
+ size: "xs",
1391
+ variant: "outlined",
1392
+ color: "danger",
1393
+ onClick: handleCancel,
1394
+ loading: actionLoading === "cancel",
1395
+ disabled: !!actionLoading
1396
+ }, "Cancel Task")
1397
+ )
1398
+ ),
1399
+ // Priority
1400
+ React7.createElement(
1401
+ Flex6,
1402
+ { variant: "column-stretch-start-nowrap-8" },
1403
+ React7.createElement(Typography5, { variant: "caption-thick", color: "color-text-medium" }, "Priority"),
1404
+ React7.createElement(
1405
+ Flex6,
1406
+ { variant: "row-center-start-nowrap-8" },
1407
+ React7.createElement(IconButton4, {
1408
+ icon: "expandLess",
1409
+ size: "xs",
1410
+ variant: "outlined",
1411
+ onClick: () => handlePriorityChange(1),
1412
+ title: "Increase priority"
1413
+ }),
1414
+ React7.createElement(
1415
+ Typography5,
1416
+ { variant: "smallCaption-regular", color: "color-text-high" },
1417
+ `P${task.priority ?? 0}`
1418
+ ),
1419
+ React7.createElement(IconButton4, {
1420
+ icon: "expandMore",
1421
+ size: "xs",
1422
+ variant: "outlined",
1423
+ onClick: () => handlePriorityChange(-1),
1424
+ title: "Decrease priority"
1425
+ })
1426
+ )
1427
+ ),
1428
+ // Assign Profile
1429
+ availableProfiles.length > 0 && React7.createElement(
1430
+ Flex6,
1431
+ { variant: "column-stretch-start-nowrap-8" },
1432
+ React7.createElement(Typography5, { variant: "caption-thick", color: "color-text-medium" }, "Assign Profile"),
1433
+ React7.createElement(Table, {
1434
+ size: "compact",
1435
+ getRowKey: (row) => row.id,
1436
+ columns: [
1437
+ {
1438
+ key: "icon",
1439
+ header: "",
1440
+ width: "30px",
1441
+ render: (_, row) => React7.createElement(Icon3, {
1442
+ name: row.icon || "smartToy",
1443
+ size: 14,
1444
+ color: row.color || "color-text-medium"
1445
+ })
1446
+ },
1447
+ {
1448
+ key: "name",
1449
+ header: "Profile"
1450
+ },
1451
+ {
1452
+ key: "capabilities",
1453
+ header: "Capabilities",
1454
+ render: (_, row) => {
1455
+ const caps = row.capabilities || [];
1456
+ if (caps.length === 0) return null;
1457
+ return React7.createElement(
1458
+ Flex6,
1459
+ { variant: "row-center-start-wrap-4" },
1460
+ ...caps.slice(0, 4).map(
1461
+ (cap) => React7.createElement(
1462
+ "span",
1463
+ { key: cap.name, title: cap.description },
1464
+ React7.createElement(Chip, { label: cap.name, size: "small", color: "color-brand-main" })
1465
+ )
1466
+ ),
1467
+ caps.length > 4 && React7.createElement(Typography5, {
1468
+ variant: "smallCaption-regular",
1469
+ color: "color-text-subtle"
1470
+ }, `+${caps.length - 4}`)
1471
+ );
1472
+ }
1473
+ },
1474
+ {
1475
+ key: "assigned",
1476
+ header: "Assign",
1477
+ width: "50px",
1478
+ align: "right",
1479
+ render: (_, row) => {
1480
+ const isAssigned = task.assignedProfile === row.id;
1481
+ return React7.createElement(Checkbox, {
1482
+ checked: isAssigned,
1483
+ onChange: () => handleAssignProfile(row.id),
1484
+ size: "sm"
1485
+ });
1486
+ }
1487
+ }
1488
+ ],
1489
+ data: availableProfiles
1490
+ })
1491
+ )
1492
+ ),
1493
+ // ── Context tab ──
1494
+ detailTab === "context" && hasContext && React7.createElement(
1495
+ Flex6,
1496
+ {
1497
+ variant: "column-stretch-start-nowrap-12"
1498
+ },
1499
+ // Files
1500
+ (task.context?.files?.length ?? 0) > 0 && React7.createElement(
1501
+ Flex6,
1502
+ {
1503
+ variant: "column-stretch-start-nowrap-4"
1504
+ },
1505
+ React7.createElement(Typography5, { variant: "caption-thick", color: "color-text-medium" }, "Files"),
1506
+ ...(task.context?.files ?? []).map(
1507
+ (file) => React7.createElement(Typography5, {
1508
+ key: file,
1509
+ variant: "smallCaption-regular",
1510
+ color: "color-text-high",
1511
+ style: { fontFamily: "var(--font-mono, monospace)" }
1512
+ }, file)
1513
+ )
1514
+ ),
1515
+ // Notes
1516
+ task.context?.notes && React7.createElement(
1517
+ Flex6,
1518
+ {
1519
+ variant: "column-stretch-start-nowrap-4"
1520
+ },
1521
+ React7.createElement(Typography5, { variant: "caption-thick", color: "color-text-medium" }, "Notes"),
1522
+ React7.createElement(Typography5, {
1523
+ variant: "smallCaption-regular",
1524
+ color: "color-text-high",
1525
+ style: { whiteSpace: "pre-wrap" }
1526
+ }, task.context.notes)
1527
+ ),
1528
+ // Last error
1529
+ task.context?.lastError && React7.createElement(
1530
+ Flex6,
1531
+ {
1532
+ variant: "column-stretch-start-nowrap-4"
1533
+ },
1534
+ React7.createElement(Typography5, { variant: "caption-thick", color: "color-status-negative" }, "Last Error"),
1535
+ React7.createElement(Typography5, {
1536
+ variant: "smallCaption-regular",
1537
+ color: "color-text-high",
1538
+ style: { fontFamily: "var(--font-mono, monospace)", whiteSpace: "pre-wrap" }
1539
+ }, task.context.lastError)
1540
+ ),
1541
+ // Run summaries (accumulated context)
1542
+ (task.context?.runSummaries?.length ?? 0) > 0 && React7.createElement(
1543
+ Flex6,
1544
+ {
1545
+ variant: "column-stretch-start-nowrap-6"
1546
+ },
1547
+ React7.createElement(Typography5, { variant: "caption-thick", color: "color-text-medium" }, "Run Summaries"),
1548
+ ...(task.context?.runSummaries ?? []).map(
1549
+ (rs, i) => React7.createElement(
1550
+ Card,
1551
+ {
1552
+ key: `rs-${i}`,
1553
+ variant: "bordered",
1554
+ padding: "compact",
1555
+ style: { gap: "4px" }
1556
+ },
1557
+ React7.createElement(
1558
+ Flex6,
1559
+ { variant: "row-center-space-between-nowrap-8" },
1560
+ React7.createElement(Typography5, {
1561
+ variant: "smallCaption-thick",
1562
+ color: rs.outcome === "success" ? "color-status-positive" : "color-status-negative"
1563
+ }, `${rs.outcome === "success" ? "Success" : "Failed"} (${rs.botId ?? "unknown bot"})`),
1564
+ React7.createElement(Typography5, {
1565
+ variant: "smallCaption-regular",
1566
+ color: "color-text-subtle"
1567
+ }, `${Math.round((rs.durationMs ?? 0) / 1e3)}s \xB7 ${rs.tokensUsed ?? 0} tok \xB7 $${(rs.cost ?? 0).toFixed(3)}`)
1568
+ ),
1569
+ React7.createElement(Typography5, {
1570
+ variant: "smallCaption-regular",
1571
+ color: "color-text-medium"
1572
+ }, rs.summary ?? ""),
1573
+ (rs.filesModified ?? []).length > 0 && React7.createElement(Typography5, {
1574
+ variant: "smallCaption-regular",
1575
+ color: "color-text-subtle",
1576
+ style: { fontFamily: "var(--font-mono, monospace)" }
1577
+ }, `Files: ${rs.filesModified.join(", ")}`),
1578
+ rs.error && React7.createElement(Typography5, {
1579
+ variant: "smallCaption-regular",
1580
+ color: "color-status-negative"
1581
+ }, `Error: ${rs.error}`)
1582
+ )
1583
+ )
1584
+ ),
1585
+ // Budget
1586
+ (task.tokensUsed > 0 || task.costUsed > 0) && React7.createElement(
1587
+ Flex6,
1588
+ {
1589
+ variant: "column-stretch-start-nowrap-4"
1590
+ },
1591
+ React7.createElement(Typography5, { variant: "caption-thick", color: "color-text-medium" }, "Budget"),
1592
+ React7.createElement(
1593
+ Flex6,
1594
+ { variant: "row-center-start-nowrap-16" },
1595
+ React7.createElement(
1596
+ Typography5,
1597
+ { variant: "smallCaption-regular", color: "color-text-high" },
1598
+ `Tokens: ${task.tokensUsed?.toLocaleString() ?? 0}${task.budgetTokens ? ` / ${task.budgetTokens.toLocaleString()}` : ""}`
1599
+ ),
1600
+ React7.createElement(
1601
+ Typography5,
1602
+ { variant: "smallCaption-regular", color: "color-text-high" },
1603
+ `Cost: $${(task.costUsed ?? 0).toFixed(3)}${task.budgetCost ? ` / $${task.budgetCost.toFixed(2)}` : ""}`
1604
+ )
1605
+ )
1606
+ )
1607
+ )
1608
+ )
1609
+ )
1610
+ );
1611
+ }
1612
+ var task_detail_view_default = TaskDetailView;
1613
+ module.exports = TaskDetailView;
1614
+
1615
+ // src/ui/profile-card.tsx
1616
+ var React8 = require("react");
1617
+ var {
1618
+ Flex: Flex7,
1619
+ Typography: Typography6,
1620
+ Icon: Icon4,
1621
+ Chip: Chip2,
1622
+ IconButton: IconButton5,
1623
+ Tag: Tag3
1624
+ } = require("@fw/plugin-ui-kit");
1625
+ function ProfileCard({ profile, activeInstances, onEdit, onDelete }) {
1626
+ const {
1627
+ id,
1628
+ name,
1629
+ description,
1630
+ icon,
1631
+ color,
1632
+ capabilities,
1633
+ preferences,
1634
+ maxInstances
1635
+ } = profile;
1636
+ const budgetParts = [];
1637
+ if (preferences.maxCostPerRun) budgetParts.push(`$${preferences.maxCostPerRun.toFixed(2)}/run`);
1638
+ if (preferences.maxCostPerTask) budgetParts.push(`$${preferences.maxCostPerTask.toFixed(2)}/task`);
1639
+ const budgetText = budgetParts.length > 0 ? budgetParts.join(" / ") : null;
1640
+ return React8.createElement(
1641
+ Flex7,
1642
+ {
1643
+ variant: "column-stretch-start-nowrap-6",
1644
+ style: {
1645
+ padding: "10px 12px",
1646
+ borderRadius: "8px",
1647
+ border: "1px solid var(--color-border-default)",
1648
+ backgroundColor: "var(--color-surface-elevated)"
1649
+ }
1650
+ },
1651
+ // Row 1: Icon + Name + Edit/Delete buttons
1652
+ React8.createElement(
1653
+ Flex7,
1654
+ { variant: "row-center-start-nowrap-8" },
1655
+ React8.createElement(
1656
+ Flex7,
1657
+ {
1658
+ variant: "row-center-center-nowrap-0",
1659
+ style: { color: `var(--${color})`, flexShrink: 0 }
1660
+ },
1661
+ React8.createElement(Icon4, { name: icon || "smartToy", size: 18 })
1662
+ ),
1663
+ React8.createElement(Typography6, {
1664
+ variant: "caption-thick",
1665
+ color: "color-text-high",
1666
+ style: { flex: 1, minWidth: 0 }
1667
+ }, name),
1668
+ onEdit && React8.createElement(IconButton5, {
1669
+ icon: "edit",
1670
+ size: "xs",
1671
+ variant: "clear",
1672
+ onClick: () => onEdit(id),
1673
+ title: "Edit profile"
1674
+ }),
1675
+ onDelete && React8.createElement(IconButton5, {
1676
+ icon: "delete",
1677
+ size: "xs",
1678
+ variant: "clear",
1679
+ color: "danger",
1680
+ onClick: () => onDelete(id),
1681
+ title: "Delete profile"
1682
+ })
1683
+ ),
1684
+ // Description
1685
+ description && React8.createElement(Typography6, {
1686
+ variant: "smallCaption-regular",
1687
+ color: "color-text-medium"
1688
+ }, description),
1689
+ // Divider
1690
+ React8.createElement(Flex7, {
1691
+ variant: "row-center-start-nowrap-0",
1692
+ style: { borderTop: "1px solid var(--color-border-default)", margin: "2px 0" }
1693
+ }),
1694
+ // Capabilities
1695
+ capabilities.length > 0 && React8.createElement(
1696
+ Flex7,
1697
+ { variant: "row-center-start-wrap-4" },
1698
+ React8.createElement(Typography6, {
1699
+ variant: "smallCaption-regular",
1700
+ color: "color-text-subtle",
1701
+ style: { flexShrink: 0 }
1702
+ }, "Capabilities:"),
1703
+ ...capabilities.map(
1704
+ (cap) => React8.createElement(
1705
+ "span",
1706
+ { key: cap.name, title: cap.description },
1707
+ React8.createElement(Chip2, { label: cap.name, size: "small", color: "color-brand-main" })
1708
+ )
1709
+ )
1710
+ ),
1711
+ // Cost strategy
1712
+ React8.createElement(
1713
+ Flex7,
1714
+ { variant: "row-center-start-wrap-12" },
1715
+ React8.createElement(Typography6, {
1716
+ variant: "smallCaption-regular",
1717
+ color: "color-text-subtle"
1718
+ }, `Cost strategy: ${preferences.costStrategy}`)
1719
+ ),
1720
+ // Instances + Approval + Budget
1721
+ React8.createElement(
1722
+ Flex7,
1723
+ { variant: "row-center-start-wrap-12" },
1724
+ React8.createElement(Typography6, {
1725
+ variant: "smallCaption-regular",
1726
+ color: "color-text-subtle"
1727
+ }, `Instances: ${activeInstances}/${maxInstances} active`),
1728
+ React8.createElement(Tag3, {
1729
+ size: "small",
1730
+ color: preferences.requireApproval ? "caution" : "positive"
1731
+ }, preferences.requireApproval ? "Approval required" : "Auto-approve")
1732
+ ),
1733
+ // Budget line
1734
+ budgetText && React8.createElement(Typography6, {
1735
+ variant: "smallCaption-regular",
1736
+ color: "color-text-subtle"
1737
+ }, `Budget: ${budgetText}`),
1738
+ // Instructions
1739
+ preferences.instructions && React8.createElement(Typography6, {
1740
+ variant: "smallCaption-regular",
1741
+ color: "color-text-subtle",
1742
+ style: { fontStyle: "italic" }
1743
+ }, preferences.instructions)
1744
+ );
1745
+ }
1746
+ var profile_card_default = ProfileCard;
1747
+ module.exports = ProfileCard;
1748
+
1749
+ // src/ui/bot-constants.ts
1750
+ var ICON_CATALOG = [
1751
+ {
1752
+ category: "Actions",
1753
+ icons: [
1754
+ "playArrow",
1755
+ "stop",
1756
+ "pause",
1757
+ "restart",
1758
+ "send",
1759
+ "check",
1760
+ "edit",
1761
+ "copy",
1762
+ "add",
1763
+ "reset",
1764
+ "resume"
1765
+ ]
1766
+ },
1767
+ {
1768
+ category: "AI & Science",
1769
+ icons: ["smartToy", "psychology", "autoAwesome", "modelTraining", "biotech", "science", "ai"]
1770
+ },
1771
+ {
1772
+ category: "Data & Storage",
1773
+ icons: ["database", "dataObject", "dataArray", "tableChart", "token", "dns", "cloudStorage"]
1774
+ },
1775
+ {
1776
+ category: "Logic & Flow",
1777
+ icons: [
1778
+ "altRoute",
1779
+ "callSplit",
1780
+ "callMerge",
1781
+ "rule",
1782
+ "filterAlt",
1783
+ "sort",
1784
+ "loop",
1785
+ "repeat",
1786
+ "compareArrows",
1787
+ "swapHoriz",
1788
+ "syncAlt",
1789
+ "changeCircle",
1790
+ "allInclusive"
1791
+ ]
1792
+ },
1793
+ {
1794
+ category: "Cloud & Network",
1795
+ icons: [
1796
+ "cloudSync",
1797
+ "cloudUpload",
1798
+ "cloudDownload",
1799
+ "cloudDone",
1800
+ "api",
1801
+ "webhook",
1802
+ "public",
1803
+ "router"
1804
+ ]
1805
+ },
1806
+ {
1807
+ category: "Communication",
1808
+ icons: ["email", "chat", "forum", "notifications", "campaign", "rssFeed", "sms"]
1809
+ },
1810
+ {
1811
+ category: "Security",
1812
+ icons: [
1813
+ "key",
1814
+ "shield",
1815
+ "security",
1816
+ "vpnKey",
1817
+ "adminPanel",
1818
+ "policy",
1819
+ "verified",
1820
+ "lockClosed",
1821
+ "lockOpened"
1822
+ ]
1823
+ },
1824
+ {
1825
+ category: "Time",
1826
+ icons: [
1827
+ "timer",
1828
+ "alarm",
1829
+ "hourglassEmpty",
1830
+ "pendingActions",
1831
+ "update",
1832
+ "watchLater",
1833
+ "scheduled"
1834
+ ]
1835
+ },
1836
+ {
1837
+ category: "Tasks & Workflow",
1838
+ icons: [
1839
+ "task",
1840
+ "taskAlt",
1841
+ "checklist",
1842
+ "assignment",
1843
+ "publish",
1844
+ "build",
1845
+ "construction",
1846
+ "engineering"
1847
+ ]
1848
+ },
1849
+ {
1850
+ category: "Analytics",
1851
+ icons: [
1852
+ "analytics",
1853
+ "insights",
1854
+ "barChart",
1855
+ "pieChart",
1856
+ "trendingUp",
1857
+ "showChart",
1858
+ "leaderboard",
1859
+ "monitoring"
1860
+ ]
1861
+ },
1862
+ {
1863
+ category: "Flow Nodes",
1864
+ icons: [
1865
+ "schema",
1866
+ "hub",
1867
+ "deviceHub",
1868
+ "source",
1869
+ "trigger",
1870
+ "pulse",
1871
+ "step",
1872
+ "event",
1873
+ "outlinedFlow"
1874
+ ]
1875
+ },
1876
+ {
1877
+ category: "Files & Documents",
1878
+ icons: [
1879
+ "file",
1880
+ "filePresent",
1881
+ "fileCopy",
1882
+ "textSnippet",
1883
+ "attachFile",
1884
+ "uploadFile",
1885
+ "save",
1886
+ "upload",
1887
+ "download"
1888
+ ]
1889
+ },
1890
+ {
1891
+ category: "General",
1892
+ icons: [
1893
+ "rocketLaunch",
1894
+ "code",
1895
+ "terminal",
1896
+ "logs",
1897
+ "info",
1898
+ "help",
1899
+ "list",
1900
+ "category",
1901
+ "hash",
1902
+ "calendar",
1903
+ "history",
1904
+ "integration",
1905
+ "layers",
1906
+ "link",
1907
+ "search",
1908
+ "outlinedSettings",
1909
+ "outlinedBug",
1910
+ "collaboration",
1911
+ "person",
1912
+ "people",
1913
+ "backup",
1914
+ "healthAndSafety"
1915
+ ]
1916
+ }
1917
+ ];
1918
+ var BOT_COLORS = [
1919
+ { label: "Blue", token: "color-node-blue-icon" },
1920
+ { label: "Purple", token: "color-node-purple-icon" },
1921
+ { label: "Cyan", token: "color-node-cyan-icon" },
1922
+ { label: "Orange", token: "color-node-orange-icon" },
1923
+ { label: "Pink", token: "color-node-pink-icon" },
1924
+ { label: "Green", token: "color-node-green-icon" },
1925
+ { label: "Brand", token: "color-brand-main" },
1926
+ { label: "Accent", token: "color-brand-alt" },
1927
+ { label: "Positive", token: "color-status-positive" },
1928
+ { label: "Info", token: "color-status-info" }
1929
+ ];
1930
+
1931
+ // src/ui/profile-editor.tsx
1932
+ var React9 = require("react");
1933
+ var { useState: useState6, useEffect: useEffect3, useCallback: useCallback4 } = React9;
1934
+ var {
1935
+ Flex: Flex8,
1936
+ Typography: Typography7,
1937
+ Input: Input2,
1938
+ Button: Button3,
1939
+ IconButton: IconButton6,
1940
+ Icon: Icon5,
1941
+ SectionTitle,
1942
+ Checkbox: Checkbox2,
1943
+ Field,
1944
+ IconPicker,
1945
+ ColorPicker,
1946
+ toast: toast4,
1947
+ usePackWorkspace: usePackWorkspace4
1948
+ } = require("@fw/plugin-ui-kit");
1949
+ function ProfileEditor({ mode, profileId, bots, onSave, onCancel, onDelete }) {
1950
+ const ctx = usePackWorkspace4();
1951
+ const { callTool } = ctx;
1952
+ const [name, setName] = useState6("");
1953
+ const [description, setDescription] = useState6("");
1954
+ const [botId, setBotId] = useState6(mode === "create" && bots.length > 0 ? bots[0].id : "");
1955
+ const [icon, setIcon] = useState6("smartToy");
1956
+ const [color, setColor] = useState6("color-node-blue-icon");
1957
+ const [costStrategy, setCostStrategy] = useState6("balanced");
1958
+ const [capabilities, setCapabilities] = useState6([]);
1959
+ const [capName, setCapName] = useState6("");
1960
+ const [capDescription, setCapDescription] = useState6("");
1961
+ const [instructions, setInstructions] = useState6("");
1962
+ const [requireApproval, setRequireApproval] = useState6(false);
1963
+ const [loading, setLoading] = useState6(mode === "edit");
1964
+ useEffect3(() => {
1965
+ if (mode !== "edit" || !profileId) return;
1966
+ let cancelled = false;
1967
+ (async () => {
1968
+ try {
1969
+ const raw = await callTool("fw_weaver_profile_list", {});
1970
+ if (cancelled) return;
1971
+ const data = typeof raw === "string" ? JSON.parse(raw) : raw;
1972
+ const list = Array.isArray(data) ? data : [];
1973
+ const profile = list.find((p) => p.id === profileId);
1974
+ if (!profile) {
1975
+ toast4("Profile not found", { type: "error" });
1976
+ onCancel();
1977
+ return;
1978
+ }
1979
+ setName(profile.name || "");
1980
+ setDescription(profile.description || "");
1981
+ setBotId(profile.botId || "");
1982
+ setIcon(profile.icon || "smartToy");
1983
+ setColor(profile.color || "color-node-blue-icon");
1984
+ const prefs = profile.preferences || {};
1985
+ setCostStrategy(prefs.costStrategy || "balanced");
1986
+ setCapabilities(
1987
+ Array.isArray(profile.capabilities) ? profile.capabilities : []
1988
+ );
1989
+ setInstructions(prefs.instructions || "");
1990
+ setRequireApproval(!!prefs.requireApproval);
1991
+ } catch {
1992
+ toast4("Failed to load profile", { type: "error" });
1993
+ onCancel();
1994
+ } finally {
1995
+ if (!cancelled) setLoading(false);
1996
+ }
1997
+ })();
1998
+ return () => {
1999
+ cancelled = true;
2000
+ };
2001
+ }, [mode, profileId, callTool, onCancel]);
2002
+ const handleAddCapability = useCallback4(() => {
2003
+ const trimName = capName.trim();
2004
+ const trimDesc = capDescription.trim();
2005
+ if (!trimName || !trimDesc) return;
2006
+ setCapabilities((prev) => [...prev, { name: trimName, description: trimDesc }]);
2007
+ setCapName("");
2008
+ setCapDescription("");
2009
+ }, [capName, capDescription]);
2010
+ const handleRemoveCapability = useCallback4((index) => {
2011
+ setCapabilities((prev) => prev.filter((_, i) => i !== index));
2012
+ }, []);
2013
+ const handleSave = useCallback4(async () => {
2014
+ if (!name.trim()) {
2015
+ toast4("Profile name is required", { type: "error" });
2016
+ return;
2017
+ }
2018
+ if (!botId) {
2019
+ toast4("Select a bot workflow", { type: "error" });
2020
+ return;
2021
+ }
2022
+ if (capabilities.length === 0) {
2023
+ toast4("Add at least one capability", { type: "error" });
2024
+ return;
2025
+ }
2026
+ try {
2027
+ if (mode === "create") {
2028
+ await callTool("fw_weaver_profile_create", {
2029
+ name: name.trim(),
2030
+ description: description.trim(),
2031
+ botId,
2032
+ icon,
2033
+ color,
2034
+ capabilities,
2035
+ costStrategy,
2036
+ instructions: instructions.trim() || void 0,
2037
+ requireApproval
2038
+ });
2039
+ toast4("Profile created", { type: "success" });
2040
+ } else {
2041
+ await callTool("fw_weaver_profile_update", {
2042
+ id: profileId,
2043
+ name: name.trim(),
2044
+ description: description.trim(),
2045
+ icon,
2046
+ color,
2047
+ capabilities,
2048
+ costStrategy,
2049
+ instructions: instructions.trim() || void 0,
2050
+ requireApproval
2051
+ });
2052
+ toast4("Profile updated", { type: "success" });
2053
+ }
2054
+ onSave();
2055
+ } catch (err) {
2056
+ toast4(err instanceof Error ? err.message : `Failed to ${mode} profile`, { type: "error" });
2057
+ }
2058
+ }, [mode, profileId, name, description, botId, icon, color, costStrategy, capabilities, instructions, requireApproval, callTool, onSave]);
2059
+ const handleDelete = useCallback4(async () => {
2060
+ if (!profileId) return;
2061
+ const ok = await ctx.confirm("Are you sure you want to delete this profile?", {
2062
+ title: "Delete Profile",
2063
+ confirmLabel: "Delete",
2064
+ state: "danger"
2065
+ });
2066
+ if (!ok) return;
2067
+ try {
2068
+ await callTool("fw_weaver_profile_delete", { id: profileId });
2069
+ toast4("Profile deleted", { type: "success" });
2070
+ if (onDelete) onDelete();
2071
+ } catch (err) {
2072
+ toast4(err instanceof Error ? err.message : "Failed to delete profile", { type: "error" });
2073
+ }
2074
+ }, [profileId, callTool, onDelete, ctx]);
2075
+ if (loading) {
2076
+ return React9.createElement(
2077
+ Flex8,
2078
+ {
2079
+ variant: "column-center-center-nowrap-12",
2080
+ style: { padding: "24px 16px" }
2081
+ },
2082
+ React9.createElement(Typography7, { variant: "caption-regular", color: "color-text-subtle" }, "Loading profile...")
2083
+ );
2084
+ }
2085
+ return React9.createElement(
2086
+ Flex8,
2087
+ {
2088
+ variant: "column-stretch-start-nowrap-0",
2089
+ style: { width: "100%", height: "100%", overflow: "hidden" }
2090
+ },
2091
+ // ── Header bar ──
2092
+ React9.createElement(
2093
+ Flex8,
2094
+ {
2095
+ variant: "row-center-space-between-nowrap-8",
2096
+ style: { padding: "8px 16px", flexShrink: 0, borderBottom: "1px solid var(--color-border-default)" }
2097
+ },
2098
+ React9.createElement(
2099
+ Flex8,
2100
+ { variant: "row-center-start-nowrap-8" },
2101
+ React9.createElement(IconButton6, {
2102
+ icon: "back",
2103
+ size: "xs",
2104
+ variant: "clear",
2105
+ onClick: onCancel,
2106
+ title: "Back"
2107
+ }),
2108
+ React9.createElement(Typography7, { variant: "caption-thick", color: "color-text-high" }, mode === "create" ? "Create Profile" : "Edit Profile")
2109
+ ),
2110
+ mode === "edit" && onDelete && React9.createElement(IconButton6, {
2111
+ icon: "outlinedDelete",
2112
+ size: "sm",
2113
+ variant: "clear",
2114
+ color: "danger",
2115
+ onClick: handleDelete,
2116
+ title: "Delete profile"
2117
+ })
2118
+ ),
2119
+ // ── Scrollable form body ──
2120
+ React9.createElement(
2121
+ Flex8,
2122
+ {
2123
+ variant: "column-stretch-start-nowrap-10",
2124
+ style: { flex: 1, minHeight: 0, overflow: "auto", padding: "12px 16px" }
2125
+ },
2126
+ // ── Name ──
2127
+ React9.createElement(
2128
+ Field,
2129
+ { label: "Name" },
2130
+ React9.createElement(Input2, {
2131
+ type: "text",
2132
+ size: "small",
2133
+ placeholder: "Profile name",
2134
+ value: name,
2135
+ onChange: (v) => setName(v),
2136
+ defaultBoxStyle: { flex: 1, minWidth: 0 },
2137
+ inputBoxStyle: { maxWidth: "none" }
2138
+ })
2139
+ ),
2140
+ // ── Description ──
2141
+ React9.createElement(
2142
+ Field,
2143
+ { label: "Description" },
2144
+ React9.createElement(Input2, {
2145
+ type: "text",
2146
+ size: "small",
2147
+ placeholder: "What does this profile do?",
2148
+ value: description,
2149
+ onChange: (v) => setDescription(v),
2150
+ defaultBoxStyle: { flex: 1, minWidth: 0 },
2151
+ inputBoxStyle: { maxWidth: "none" }
2152
+ })
2153
+ ),
2154
+ // ── Bot ──
2155
+ React9.createElement(
2156
+ Field,
2157
+ { label: "Bot" },
2158
+ React9.createElement(Input2, {
2159
+ type: "select",
2160
+ size: "small",
2161
+ options: bots.map((b) => ({ id: b.id, label: b.name, icon: b.icon || "smartToy" })),
2162
+ optionId: botId,
2163
+ onChange: (id) => setBotId(id),
2164
+ disabled: mode === "edit",
2165
+ placeholder: "Select bot",
2166
+ defaultBoxStyle: { flex: 1, minWidth: 0 }
2167
+ })
2168
+ ),
2169
+ // ── Icon ──
2170
+ React9.createElement(
2171
+ Field,
2172
+ { label: "Icon", align: "start" },
2173
+ React9.createElement(IconPicker, {
2174
+ catalog: ICON_CATALOG,
2175
+ value: icon,
2176
+ onChange: (v) => setIcon(v),
2177
+ accentColor: color,
2178
+ variant: "inline"
2179
+ })
2180
+ ),
2181
+ // ── Color ──
2182
+ React9.createElement(
2183
+ Field,
2184
+ { label: "Color", align: "start" },
2185
+ React9.createElement(ColorPicker, {
2186
+ colors: BOT_COLORS,
2187
+ value: color,
2188
+ onChange: (v) => setColor(v),
2189
+ variant: "inline"
2190
+ })
2191
+ ),
2192
+ // ── Cost Strategy ──
2193
+ React9.createElement(
2194
+ Field,
2195
+ { label: "Cost Strategy" },
2196
+ React9.createElement(Input2, {
2197
+ type: "select",
2198
+ size: "small",
2199
+ options: [
2200
+ { id: "frugal", label: "Frugal", icon: "timer" },
2201
+ { id: "balanced", label: "Balanced", icon: "syncAlt" },
2202
+ { id: "performance", label: "Performance", icon: "rocketLaunch" }
2203
+ ],
2204
+ optionId: costStrategy,
2205
+ onChange: (id) => setCostStrategy(id),
2206
+ defaultBoxStyle: { flex: 1, minWidth: 0 }
2207
+ })
2208
+ ),
2209
+ // ── Capabilities ──
2210
+ React9.createElement(
2211
+ Field,
2212
+ { label: "Capabilities", align: "start" },
2213
+ React9.createElement(
2214
+ Flex8,
2215
+ { variant: "column-stretch-start-nowrap-6" },
2216
+ // Add capability row
2217
+ React9.createElement(
2218
+ Flex8,
2219
+ { variant: "row-center-start-nowrap-4", style: { overflow: "hidden" } },
2220
+ React9.createElement(Input2, {
2221
+ type: "text",
2222
+ size: "small",
2223
+ placeholder: "Name",
2224
+ value: capName,
2225
+ onChange: (v) => setCapName(v),
2226
+ defaultBoxStyle: { flex: 1, minWidth: 0 },
2227
+ inputBoxStyle: { maxWidth: "none" }
2228
+ }),
2229
+ React9.createElement(Input2, {
2230
+ type: "text",
2231
+ size: "small",
2232
+ placeholder: "Description",
2233
+ value: capDescription,
2234
+ onChange: (v) => setCapDescription(v),
2235
+ onEnter: handleAddCapability,
2236
+ defaultBoxStyle: { flex: 2, minWidth: 0 },
2237
+ inputBoxStyle: { maxWidth: "none" }
2238
+ }),
2239
+ React9.createElement(IconButton6, {
2240
+ icon: "add",
2241
+ size: "sm",
2242
+ variant: "outlined",
2243
+ color: "primary",
2244
+ onClick: handleAddCapability,
2245
+ disabled: !capName.trim() || !capDescription.trim()
2246
+ })
2247
+ ),
2248
+ ...capabilities.map(
2249
+ (cap, idx) => React9.createElement(
2250
+ Flex8,
2251
+ {
2252
+ key: `${cap.name}-${idx}`,
2253
+ variant: "row-center-start-nowrap-6",
2254
+ style: { paddingLeft: "4px" }
2255
+ },
2256
+ React9.createElement(Typography7, {
2257
+ variant: "smallCaption-regular",
2258
+ color: "color-text-high",
2259
+ style: { flexShrink: 0 }
2260
+ }, `\u2022 ${cap.name}:`),
2261
+ React9.createElement(Typography7, {
2262
+ variant: "smallCaption-regular",
2263
+ color: "color-text-medium",
2264
+ style: { flex: 1, minWidth: 0 }
2265
+ }, cap.description),
2266
+ React9.createElement(IconButton6, {
2267
+ icon: "close",
2268
+ size: "xs",
2269
+ variant: "clear",
2270
+ color: "danger",
2271
+ onClick: () => handleRemoveCapability(idx)
2272
+ })
2273
+ )
2274
+ )
2275
+ )
2276
+ ),
2277
+ // ── Instructions ──
2278
+ React9.createElement(
2279
+ Field,
2280
+ { label: "Instructions" },
2281
+ React9.createElement(Input2, {
2282
+ type: "text",
2283
+ size: "small",
2284
+ placeholder: "Optional instructions for the bot",
2285
+ value: instructions,
2286
+ onChange: (v) => setInstructions(v),
2287
+ defaultBoxStyle: { flex: 1, minWidth: 0 },
2288
+ inputBoxStyle: { maxWidth: "none" }
2289
+ })
2290
+ ),
2291
+ // ── Require Approval ──
2292
+ React9.createElement(
2293
+ Field,
2294
+ { label: "" },
2295
+ React9.createElement(Checkbox2, {
2296
+ checked: requireApproval,
2297
+ onChange: (v) => setRequireApproval(v),
2298
+ label: "Require approval",
2299
+ size: "sm"
2300
+ })
2301
+ ),
2302
+ // ── Save button ──
2303
+ React9.createElement(
2304
+ Flex8,
2305
+ { variant: "row-center-end-nowrap-8" },
2306
+ React9.createElement(Button3, {
2307
+ size: "xs",
2308
+ variant: "fill",
2309
+ color: "primary",
2310
+ onClick: handleSave,
2311
+ disabled: !name.trim() || !botId || capabilities.length === 0
2312
+ }, mode === "create" ? "Create" : "Save")
2313
+ )
2314
+ )
2315
+ );
2316
+ }
2317
+ var profile_editor_default = ProfileEditor;
2318
+ module.exports = ProfileEditor;
2319
+
2320
+ // src/ui/decision-log.tsx
2321
+ var React10 = require("react");
2322
+ var {
2323
+ Flex: Flex9,
2324
+ Typography: Typography8,
2325
+ ScrollArea: ScrollArea3,
2326
+ Tag: Tag4,
2327
+ SectionTitle: SectionTitle2
2328
+ } = require("@fw/plugin-ui-kit");
2329
+ var methodColors = {
2330
+ "exact-match": "info",
2331
+ "single-eligible": "positive",
2332
+ "ai-routed": "secondary",
2333
+ "round-robin": "secondary",
2334
+ "manual": "caution"
2335
+ };
2336
+ var methodLabels = {
2337
+ "exact-match": "exact",
2338
+ "single-eligible": "single",
2339
+ "ai-routed": "ai",
2340
+ "round-robin": "robin",
2341
+ "manual": "manual"
2342
+ };
2343
+ function formatTime(ts) {
2344
+ const d = new Date(ts);
2345
+ return d.toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit", second: "2-digit" });
2346
+ }
2347
+ function truncate(text, max) {
2348
+ return text.length > max ? text.slice(0, max - 1) + "\u2026" : text;
2349
+ }
2350
+ function DecisionLog({ decisions }) {
2351
+ if (!decisions || decisions.length === 0) {
2352
+ return React10.createElement(
2353
+ Flex9,
2354
+ {
2355
+ variant: "column-stretch-start-nowrap-4"
2356
+ },
2357
+ React10.createElement(SectionTitle2, null, "Decision Log"),
2358
+ React10.createElement(Typography8, {
2359
+ variant: "smallCaption-regular",
2360
+ color: "color-text-subtle"
2361
+ }, "No routing decisions yet.")
2362
+ );
2363
+ }
2364
+ const sorted = [...decisions].sort((a, b) => b.timestamp - a.timestamp);
2365
+ return React10.createElement(
2366
+ Flex9,
2367
+ {
2368
+ variant: "column-stretch-start-nowrap-4"
2369
+ },
2370
+ React10.createElement(SectionTitle2, null, "Decision Log"),
2371
+ React10.createElement(
2372
+ ScrollArea3,
2373
+ {
2374
+ style: { maxHeight: "260px" }
2375
+ },
2376
+ React10.createElement(
2377
+ Flex9,
2378
+ {
2379
+ variant: "column-stretch-start-nowrap-0"
2380
+ },
2381
+ ...sorted.map((d) => {
2382
+ const methodLabel = d.method === "ai-routed" && d.confidence != null ? `ai ${d.confidence}%` : methodLabels[d.method] || d.method;
2383
+ return React10.createElement(
2384
+ Flex9,
2385
+ {
2386
+ key: d.id,
2387
+ variant: "column-stretch-start-nowrap-2",
2388
+ style: {
2389
+ padding: "6px 8px",
2390
+ borderBottom: "1px solid var(--color-border-default)"
2391
+ }
2392
+ },
2393
+ // Main line: time, title, instance, method
2394
+ React10.createElement(
2395
+ Flex9,
2396
+ { variant: "row-center-start-nowrap-8" },
2397
+ React10.createElement(Typography8, {
2398
+ variant: "smallCaption-regular",
2399
+ color: "color-text-subtle",
2400
+ style: { flexShrink: 0, fontFamily: "var(--font-mono, monospace)", fontSize: "11px" }
2401
+ }, formatTime(d.timestamp)),
2402
+ React10.createElement(Typography8, {
2403
+ variant: "smallCaption-regular",
2404
+ color: "color-text-high",
2405
+ style: { flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }
2406
+ }, `"${truncate(d.taskTitle, 40)}"`),
2407
+ React10.createElement(Typography8, {
2408
+ variant: "smallCaption-regular",
2409
+ color: "color-text-medium",
2410
+ style: { flexShrink: 0 }
2411
+ }, `\u2192 ${d.assignedInstanceId}`),
2412
+ React10.createElement(Tag4, {
2413
+ size: "small",
2414
+ color: methodColors[d.method] || "secondary"
2415
+ }, methodLabel)
2416
+ ),
2417
+ // Reason line
2418
+ React10.createElement(Typography8, {
2419
+ variant: "smallCaption-regular",
2420
+ color: "color-text-subtle",
2421
+ style: { paddingLeft: "70px" }
2422
+ }, d.reason)
2423
+ );
2424
+ })
2425
+ )
2426
+ )
2427
+ );
2428
+ }
2429
+ var decision_log_default = DecisionLog;
2430
+ module.exports = DecisionLog;
2431
+
2432
+ // src/ui/swarm-dashboard.tsx
2433
+ var React11 = require("react");
2434
+ var { useState: useState7, useEffect: useEffect4, useCallback: useCallback5, useRef: useRef3 } = React11;
2435
+ var {
2436
+ Flex: Flex10,
2437
+ ScrollArea: ScrollArea4,
2438
+ EmptyState: EmptyState3,
2439
+ Typography: Typography9,
2440
+ StatusIcon: StatusIcon4,
2441
+ Icon: Icon6,
2442
+ Tag: Tag5,
2443
+ Card: Card2,
2444
+ Tabs: Tabs2,
2445
+ SectionTitle: SectionTitle3,
2446
+ Button: Button4,
2447
+ Input: Input3,
2448
+ IconButton: IconButton7,
2449
+ IconPicker: IconPicker2,
2450
+ ColorPicker: ColorPicker2,
2451
+ toast: toast5,
2452
+ usePackWorkspace: usePackWorkspace5
2453
+ } = require("@fw/plugin-ui-kit");
2454
+ function parseToolResult2(raw) {
2455
+ if (typeof raw === "string") {
2456
+ try {
2457
+ return JSON.parse(raw);
2458
+ } catch {
2459
+ return raw;
2460
+ }
2461
+ }
2462
+ return raw;
2463
+ }
2464
+ function resolveTaskTitle(taskId, tasks) {
2465
+ if (!taskId) return void 0;
2466
+ const task = tasks.find((t) => t.id === taskId);
2467
+ return task?.title;
2468
+ }
2469
+ function SwarmDashboard() {
2470
+ const ctx = usePackWorkspace5();
2471
+ const { callTool, onRefresh } = ctx;
2472
+ const [selectedTaskId, setSelectedTaskId] = useState7(null);
2473
+ const [activeTab, setActiveTab] = useState7("tasks");
2474
+ const [editingBotId, setEditingBotId] = useState7(null);
2475
+ const [profileEditorMode, setProfileEditorMode] = useState7(null);
2476
+ const [editingProfileId, setEditingProfileId] = useState7(null);
2477
+ const [swarmStatus, setSwarmStatus] = useState7(null);
2478
+ const [tasks, setTasks] = useState7([]);
2479
+ const [registeredBots, setRegisteredBots] = useState7([]);
2480
+ const [providers, setProviders] = useState7([]);
2481
+ const [insights, setInsights] = useState7(null);
2482
+ const [profiles, setProfiles] = useState7([]);
2483
+ const [orchestratorDecisions, setOrchestratorDecisions] = useState7([]);
2484
+ const [loading, setLoading] = useState7(true);
2485
+ const mountedRef = useRef3(true);
2486
+ useEffect4(() => {
2487
+ mountedRef.current = true;
2488
+ return () => {
2489
+ mountedRef.current = false;
2490
+ };
2491
+ }, []);
2492
+ const fetchSwarmStatus = useCallback5(async () => {
2493
+ try {
2494
+ const raw = await callTool("fw_weaver_swarm_status", {});
2495
+ if (!mountedRef.current) return;
2496
+ const data = parseToolResult2(raw);
2497
+ if (data) setSwarmStatus(data);
2498
+ } catch {
2499
+ }
2500
+ }, [callTool]);
2501
+ const fetchTaskList = useCallback5(async () => {
2502
+ try {
2503
+ const raw = await callTool("fw_weaver_task_list", {});
2504
+ if (!mountedRef.current) return;
2505
+ const data = parseToolResult2(raw);
2506
+ if (Array.isArray(data)) {
2507
+ setTasks(data);
2508
+ } else if (data && typeof data === "object" && Array.isArray(data.tasks)) {
2509
+ setTasks(data.tasks);
2510
+ }
2511
+ } catch {
2512
+ }
2513
+ }, [callTool]);
2514
+ const fetchBots = useCallback5(async () => {
2515
+ try {
2516
+ const raw = await callTool("fw_weaver_list_bots", {});
2517
+ if (!mountedRef.current) return;
2518
+ const data = typeof raw === "string" ? JSON.parse(raw) : raw;
2519
+ if (Array.isArray(data)) setRegisteredBots(data);
2520
+ } catch {
2521
+ }
2522
+ }, [callTool]);
2523
+ const fetchConfig = useCallback5(async () => {
2524
+ try {
2525
+ const [provRaw, insRaw] = await Promise.all([
2526
+ callTool("fw_weaver_providers", {}),
2527
+ callTool("fw_weaver_insights", {})
2528
+ ]);
2529
+ if (!mountedRef.current) return;
2530
+ const provData = typeof provRaw === "string" ? JSON.parse(provRaw) : provRaw;
2531
+ const insData = typeof insRaw === "string" ? JSON.parse(insRaw) : insRaw;
2532
+ if (Array.isArray(provData)) setProviders(provData);
2533
+ if (insData && typeof insData === "object") setInsights(insData);
2534
+ } catch {
2535
+ }
2536
+ }, [callTool]);
2537
+ const fetchProfiles = useCallback5(async () => {
2538
+ try {
2539
+ const raw = await callTool("fw_weaver_profile_list", {});
2540
+ if (!mountedRef.current) return;
2541
+ const data = typeof raw === "string" ? JSON.parse(raw) : raw;
2542
+ if (Array.isArray(data)) setProfiles(data);
2543
+ } catch {
2544
+ }
2545
+ }, [callTool]);
2546
+ const fetchOrchestratorStatus = useCallback5(async () => {
2547
+ try {
2548
+ const raw = await callTool("fw_weaver_orchestrator_status", {});
2549
+ if (!mountedRef.current) return;
2550
+ const data = typeof raw === "string" ? JSON.parse(raw) : raw;
2551
+ if (data && typeof data === "object") {
2552
+ const decisions = data.recentDecisions;
2553
+ if (Array.isArray(decisions)) setOrchestratorDecisions(decisions);
2554
+ }
2555
+ } catch {
2556
+ }
2557
+ }, [callTool]);
2558
+ const refreshAll = useCallback5(async () => {
2559
+ await Promise.all([fetchSwarmStatus(), fetchTaskList(), fetchBots(), fetchConfig(), fetchProfiles(), fetchOrchestratorStatus()]);
2560
+ if (mountedRef.current) setLoading(false);
2561
+ }, [fetchSwarmStatus, fetchTaskList, fetchBots, fetchConfig, fetchProfiles, fetchOrchestratorStatus]);
2562
+ useEffect4(() => {
2563
+ refreshAll();
2564
+ }, [refreshAll]);
2565
+ useEffect4(() => {
2566
+ const interval = setInterval(fetchSwarmStatus, 3e3);
2567
+ return () => clearInterval(interval);
2568
+ }, [fetchSwarmStatus]);
2569
+ useEffect4(() => {
2570
+ const interval = setInterval(fetchTaskList, 5e3);
2571
+ return () => clearInterval(interval);
2572
+ }, [fetchTaskList]);
2573
+ useEffect4(() => {
2574
+ const interval = setInterval(() => {
2575
+ fetchProfiles();
2576
+ fetchOrchestratorStatus();
2577
+ }, 1e4);
2578
+ return () => clearInterval(interval);
2579
+ }, [fetchProfiles, fetchOrchestratorStatus]);
2580
+ useEffect4(() => {
2581
+ return onRefresh(() => refreshAll());
2582
+ }, [refreshAll, onRefresh]);
2583
+ const handleUpdateBot = useCallback5(async (botId, patch) => {
2584
+ try {
2585
+ await callTool("fw_weaver_register_bot", { id: botId, ...patch });
2586
+ await fetchBots();
2587
+ toast5("Bot updated", { type: "success" });
2588
+ } catch (err) {
2589
+ toast5(err instanceof Error ? err.message : "Failed to update bot", { type: "error" });
2590
+ }
2591
+ }, [callTool, fetchBots]);
2592
+ const handleSteerBot = useCallback5(async (botId, command) => {
2593
+ try {
2594
+ await callTool("fw_weaver_steer", { botId, command });
2595
+ toast5(`${command} signal sent to ${botId}`, { type: "info" });
2596
+ fetchSwarmStatus();
2597
+ } catch (err) {
2598
+ toast5(err instanceof Error ? err.message : `Failed to ${command}`, { type: "error" });
2599
+ }
2600
+ }, [callTool, fetchSwarmStatus]);
2601
+ const handleTaskClick = useCallback5((taskId) => {
2602
+ setSelectedTaskId(taskId);
2603
+ }, []);
2604
+ const handleBack = useCallback5(() => {
2605
+ setSelectedTaskId(null);
2606
+ refreshAll();
2607
+ }, [refreshAll]);
2608
+ const handleTaskCreated = useCallback5(() => {
2609
+ fetchTaskList();
2610
+ }, [fetchTaskList]);
2611
+ if (selectedTaskId) {
2612
+ return React11.createElement(task_detail_view_default, {
2613
+ taskId: selectedTaskId,
2614
+ onBack: handleBack
2615
+ });
2616
+ }
2617
+ const swarmInstances = swarmStatus?.instances ?? {};
2618
+ const swarmInstanceEntries = Object.values(swarmInstances);
2619
+ const hasSwarmInstances = swarmInstanceEntries.length > 0;
2620
+ const hasRegisteredBots = registeredBots.length > 0;
2621
+ const sessionBudget = swarmStatus?.budgets?.session;
2622
+ const hasBudget = !!(sessionBudget && (sessionBudget.limitTokens > 0 || sessionBudget.limitCost > 0));
2623
+ const hasContent = hasSwarmInstances || hasRegisteredBots || tasks.length > 0 || swarmStatus !== null;
2624
+ return React11.createElement(
2625
+ Flex10,
2626
+ {
2627
+ variant: "column-stretch-start-nowrap-0",
2628
+ style: { width: "100%", height: "100%", overflow: "hidden" }
2629
+ },
2630
+ // ── SwarmControls (top bar) ──────────────────────────────────
2631
+ React11.createElement(swarm_controls_default, {
2632
+ swarmStatus,
2633
+ onRefresh: refreshAll
2634
+ }),
2635
+ // ── BudgetBar (below controls) ──────────────────────────────
2636
+ hasBudget && React11.createElement(
2637
+ Flex10,
2638
+ {
2639
+ variant: "column-stretch-start-nowrap-4",
2640
+ style: { padding: "8px 16px", flexShrink: 0, borderBottom: "1px solid var(--color-border-default)" }
2641
+ },
2642
+ React11.createElement(budget_bar_default, {
2643
+ label: "Tokens",
2644
+ used: sessionBudget.usedTokens,
2645
+ limit: sessionBudget.limitTokens,
2646
+ unit: "tokens"
2647
+ }),
2648
+ React11.createElement(budget_bar_default, {
2649
+ label: "Cost",
2650
+ used: sessionBudget.usedCost,
2651
+ limit: sessionBudget.limitCost,
2652
+ unit: "USD"
2653
+ })
2654
+ ),
2655
+ // ── Bot slot cards (horizontal scrollable row) ──────────────
2656
+ // ── Tabs ──────────────────────────────────────────────────────
2657
+ React11.createElement(Tabs2, {
2658
+ tabs: [
2659
+ { id: "tasks", title: `Tasks (${tasks.length})` },
2660
+ { id: "bots", title: hasSwarmInstances ? `Instances (${swarmInstanceEntries.length})` : `Bots (${registeredBots.length})` },
2661
+ { id: "profiles", title: `Profiles (${profiles.length})` },
2662
+ { id: "config", title: "Config" }
2663
+ ],
2664
+ activeTabId: activeTab,
2665
+ onSelectTab: (id) => setActiveTab(id),
2666
+ size: "sm"
2667
+ }),
2668
+ // ── Tab content ──────────────────────────────────────────────
2669
+ React11.createElement(
2670
+ Flex10,
2671
+ {
2672
+ variant: "column-stretch-start-nowrap-0",
2673
+ style: { flex: 1, minHeight: 0, overflow: "auto" }
2674
+ },
2675
+ // Tasks tab
2676
+ activeTab === "tasks" && React11.createElement(
2677
+ Flex10,
2678
+ {
2679
+ variant: "column-stretch-start-nowrap-0",
2680
+ style: { flex: 1, minHeight: 0 }
2681
+ },
2682
+ !hasContent && !loading && React11.createElement(EmptyState3, {
2683
+ icon: "smartToy",
2684
+ message: "Swarm not started",
2685
+ description: "Start the swarm to begin processing tasks, or create tasks below."
2686
+ }),
2687
+ hasContent && React11.createElement(task_pool_list_default, {
2688
+ tasks,
2689
+ onTaskClick: handleTaskClick
2690
+ }),
2691
+ // Task create form (inside Tasks tab)
2692
+ React11.createElement(
2693
+ Flex10,
2694
+ {
2695
+ variant: "column-stretch-start-nowrap-0",
2696
+ style: { padding: "8px 16px", flexShrink: 0, borderTop: "1px solid var(--color-border-default)" }
2697
+ },
2698
+ React11.createElement(task_create_form_default, {
2699
+ onTaskCreated: handleTaskCreated
2700
+ })
2701
+ )
2702
+ ),
2703
+ // Bots tab
2704
+ activeTab === "bots" && React11.createElement(
2705
+ Flex10,
2706
+ {
2707
+ variant: "column-stretch-start-nowrap-0",
2708
+ style: { flex: 1, minHeight: 0 }
2709
+ },
2710
+ // When swarm is running: show swarm instances
2711
+ hasSwarmInstances && React11.createElement(
2712
+ Flex10,
2713
+ {
2714
+ variant: "column-stretch-start-nowrap-0"
2715
+ },
2716
+ React11.createElement(
2717
+ Flex10,
2718
+ {
2719
+ variant: "row-center-start-nowrap-8",
2720
+ style: { padding: "8px 16px", borderBottom: "1px solid var(--color-border-default)" }
2721
+ },
2722
+ React11.createElement(Typography9, {
2723
+ variant: "smallCaption-regular",
2724
+ color: "color-text-subtle",
2725
+ style: { width: "120px", flexShrink: 0 }
2726
+ }, "Instance"),
2727
+ React11.createElement(Typography9, {
2728
+ variant: "smallCaption-regular",
2729
+ color: "color-text-subtle",
2730
+ style: { width: "110px", flexShrink: 0 }
2731
+ }, "Bot"),
2732
+ React11.createElement(Typography9, {
2733
+ variant: "smallCaption-regular",
2734
+ color: "color-text-subtle",
2735
+ style: { width: "70px", flexShrink: 0 }
2736
+ }, "Status"),
2737
+ React11.createElement(Typography9, {
2738
+ variant: "smallCaption-regular",
2739
+ color: "color-text-subtle",
2740
+ style: { flex: 1, minWidth: 0 }
2741
+ }, "Task"),
2742
+ React11.createElement(Typography9, {
2743
+ variant: "smallCaption-regular",
2744
+ color: "color-text-subtle",
2745
+ style: { width: "50px", flexShrink: 0, textAlign: "right" }
2746
+ }, "Tokens"),
2747
+ React11.createElement(Typography9, {
2748
+ variant: "smallCaption-regular",
2749
+ color: "color-text-subtle",
2750
+ style: { width: "50px", flexShrink: 0, textAlign: "right" }
2751
+ }, "Cost"),
2752
+ React11.createElement(Typography9, {
2753
+ variant: "smallCaption-regular",
2754
+ color: "color-text-subtle",
2755
+ style: { width: "50px", flexShrink: 0 }
2756
+ }, "")
2757
+ ),
2758
+ ...swarmInstanceEntries.map((inst) => {
2759
+ const profile = profiles.find((p) => p.id === inst.profileId);
2760
+ const botId = profile?.botId;
2761
+ const bot = registeredBots.find((b) => b.id === botId);
2762
+ return React11.createElement(bot_slot_card_default, {
2763
+ key: inst.instanceId,
2764
+ bot: {
2765
+ botId: inst.instanceId,
2766
+ botName: `${inst.profileId} #${inst.index}`,
2767
+ status: inst.status,
2768
+ currentTaskId: inst.currentTaskId,
2769
+ currentRunId: inst.currentRunId,
2770
+ startedAt: inst.startedAt,
2771
+ tokensUsed: inst.tokensUsed,
2772
+ cost: inst.cost
2773
+ },
2774
+ profileName: profile?.name || inst.profileId,
2775
+ botDisplayName: bot?.name,
2776
+ botIcon: bot?.icon,
2777
+ botColor: bot?.color,
2778
+ currentTaskTitle: resolveTaskTitle(inst.currentTaskId, tasks),
2779
+ onPause: (id) => handleSteerBot(id, "pause"),
2780
+ onResume: (id) => handleSteerBot(id, "resume"),
2781
+ onStop: (id) => handleSteerBot(id, "cancel")
2782
+ });
2783
+ })
2784
+ ),
2785
+ // When swarm is NOT running: show registered bots
2786
+ !hasSwarmInstances && hasRegisteredBots && React11.createElement(
2787
+ Flex10,
2788
+ {
2789
+ variant: "column-stretch-start-nowrap-0",
2790
+ style: { padding: "8px 16px" }
2791
+ },
2792
+ ...registeredBots.map((bot) => {
2793
+ const isEditing = editingBotId === bot.id;
2794
+ return React11.createElement(
2795
+ Flex10,
2796
+ {
2797
+ key: bot.id,
2798
+ variant: "column-stretch-start-nowrap-0",
2799
+ style: { borderBottom: "1px solid var(--color-border-default)" }
2800
+ },
2801
+ // Bot row (clickable to toggle edit)
2802
+ React11.createElement(
2803
+ Flex10,
2804
+ {
2805
+ variant: "row-center-start-nowrap-8",
2806
+ style: { padding: "6px 0", cursor: "pointer" },
2807
+ onClick: () => setEditingBotId(isEditing ? null : bot.id)
2808
+ },
2809
+ React11.createElement(Icon6, {
2810
+ name: bot.icon || "smartToy",
2811
+ size: 16,
2812
+ color: bot.color || "color-text-medium"
2813
+ }),
2814
+ React11.createElement(
2815
+ Flex10,
2816
+ { variant: "column-start-start-nowrap-1", style: { flex: 1, minWidth: 0 } },
2817
+ React11.createElement(Typography9, { variant: "smallCaption-regular", color: "color-text-high" }, bot.name),
2818
+ bot.description && React11.createElement(Typography9, { variant: "smallCaption-regular", color: "color-text-subtle" }, bot.description)
2819
+ ),
2820
+ React11.createElement(Icon6, { name: isEditing ? "expandLess" : "expandMore", size: 14, color: "color-text-subtle" })
2821
+ ),
2822
+ // Edit section (icon + color pickers)
2823
+ isEditing && React11.createElement(
2824
+ Flex10,
2825
+ {
2826
+ variant: "column-stretch-start-nowrap-12",
2827
+ style: { padding: "8px 0 12px 24px" }
2828
+ },
2829
+ React11.createElement(IconPicker2, {
2830
+ catalog: ICON_CATALOG,
2831
+ value: bot.icon || "smartToy",
2832
+ onChange: (icon) => handleUpdateBot(bot.id, { icon }),
2833
+ accentColor: bot.color || void 0,
2834
+ defaultExpanded: true
2835
+ }),
2836
+ React11.createElement(ColorPicker2, {
2837
+ colors: BOT_COLORS,
2838
+ value: bot.color || "",
2839
+ onChange: (color) => handleUpdateBot(bot.id, { color }),
2840
+ defaultExpanded: true
2841
+ })
2842
+ )
2843
+ );
2844
+ })
2845
+ ),
2846
+ // No bots at all
2847
+ !hasSwarmInstances && !hasRegisteredBots && React11.createElement(EmptyState3, {
2848
+ icon: "smartToy",
2849
+ message: "No bots registered",
2850
+ description: "Register bots to start the swarm."
2851
+ })
2852
+ ),
2853
+ // Profiles tab — when ProfileEditor is open, render it INSTEAD of the list
2854
+ activeTab === "profiles" && profileEditorMode != null && React11.createElement(profile_editor_default, {
2855
+ mode: profileEditorMode,
2856
+ profileId: editingProfileId ?? void 0,
2857
+ bots: registeredBots.map((b) => ({ id: b.id, name: b.name, icon: b.icon, color: b.color })),
2858
+ onSave: () => {
2859
+ setProfileEditorMode(null);
2860
+ setEditingProfileId(null);
2861
+ fetchProfiles();
2862
+ },
2863
+ onCancel: () => {
2864
+ setProfileEditorMode(null);
2865
+ setEditingProfileId(null);
2866
+ },
2867
+ onDelete: profileEditorMode === "edit" ? () => {
2868
+ setProfileEditorMode(null);
2869
+ setEditingProfileId(null);
2870
+ fetchProfiles();
2871
+ } : void 0
2872
+ }),
2873
+ // Profiles tab — default list view
2874
+ activeTab === "profiles" && profileEditorMode == null && React11.createElement(
2875
+ Flex10,
2876
+ {
2877
+ variant: "column-stretch-start-nowrap-0",
2878
+ style: { flex: 1, minHeight: 0 }
2879
+ },
2880
+ // ── Profile cards (scrollable) ──
2881
+ React11.createElement(
2882
+ Flex10,
2883
+ {
2884
+ variant: "column-stretch-start-nowrap-8",
2885
+ style: { flex: 1, minHeight: 0, overflow: "auto", padding: "12px 16px" }
2886
+ },
2887
+ profiles.length > 0 ? React11.createElement(
2888
+ Flex10,
2889
+ { variant: "column-stretch-start-nowrap-8" },
2890
+ ...profiles.map((p) => {
2891
+ const activeCount = swarmInstanceEntries.filter(
2892
+ (inst) => inst.profileId === p.id && inst.status === "executing"
2893
+ ).length;
2894
+ return React11.createElement(profile_card_default, {
2895
+ key: p.id,
2896
+ profile: p,
2897
+ activeInstances: activeCount,
2898
+ onEdit: (id) => {
2899
+ setEditingProfileId(id);
2900
+ setProfileEditorMode("edit");
2901
+ },
2902
+ onDelete: async (id) => {
2903
+ const ok = await ctx.confirm("Are you sure you want to delete this profile?", {
2904
+ title: "Delete Profile",
2905
+ confirmLabel: "Delete",
2906
+ state: "danger"
2907
+ });
2908
+ if (!ok) return;
2909
+ try {
2910
+ await callTool("fw_weaver_profile_delete", { id });
2911
+ await fetchProfiles();
2912
+ toast5("Profile deleted", { type: "success" });
2913
+ } catch (err) {
2914
+ toast5(err instanceof Error ? err.message : "Failed to delete profile", { type: "error" });
2915
+ }
2916
+ }
2917
+ });
2918
+ })
2919
+ ) : React11.createElement(EmptyState3, {
2920
+ icon: "person",
2921
+ message: "No profiles",
2922
+ description: "Profiles define how bots behave. Start the swarm to create defaults, or create one below."
2923
+ }),
2924
+ // ── Routing / Decision Log (inside scrollable area) ──
2925
+ orchestratorDecisions.length > 0 && React11.createElement(decision_log_default, {
2926
+ decisions: orchestratorDecisions
2927
+ })
2928
+ ),
2929
+ // ── New Profile button (bottom bar) ──
2930
+ React11.createElement(
2931
+ Flex10,
2932
+ {
2933
+ variant: "column-stretch-start-nowrap-0",
2934
+ style: { flexShrink: 0, borderTop: "1px solid var(--color-border-default)", padding: "8px 16px" }
2935
+ },
2936
+ React11.createElement(Button4, {
2937
+ size: "xs",
2938
+ variant: "clear",
2939
+ color: "primary",
2940
+ leftIcon: "add",
2941
+ onClick: () => setProfileEditorMode("create")
2942
+ }, "New Profile")
2943
+ )
2944
+ ),
2945
+ // Config tab
2946
+ activeTab === "config" && React11.createElement(
2947
+ Flex10,
2948
+ {
2949
+ variant: "column-stretch-start-nowrap-12",
2950
+ style: { padding: "12px 16px" }
2951
+ },
2952
+ // ── Provider ──
2953
+ React11.createElement(
2954
+ Flex10,
2955
+ { variant: "row-center-space-between-nowrap-8" },
2956
+ React11.createElement(Typography9, { variant: "smallCaption-regular", color: "color-text-subtle" }, "Provider"),
2957
+ React11.createElement(
2958
+ Flex10,
2959
+ { variant: "row-center-start-nowrap-4" },
2960
+ React11.createElement(
2961
+ Typography9,
2962
+ { variant: "smallCaption-regular", color: "color-text-high" },
2963
+ providers.find((p) => p.envVarsSet)?.name ?? "None detected"
2964
+ ),
2965
+ providers.find((p) => p.envVarsSet) && React11.createElement(Tag5, { size: "small", color: "info" }, "active")
2966
+ )
2967
+ ),
2968
+ // Trust
2969
+ insights?.trust && React11.createElement(
2970
+ Flex10,
2971
+ { variant: "row-center-space-between-nowrap-8" },
2972
+ React11.createElement(Typography9, { variant: "smallCaption-regular", color: "color-text-subtle" }, "Trust"),
2973
+ React11.createElement(
2974
+ Typography9,
2975
+ { variant: "smallCaption-regular", color: "color-text-high" },
2976
+ `Phase ${insights.trust.phase} \xB7 ${insights.trust.score}/100`
2977
+ )
2978
+ ),
2979
+ // Health
2980
+ insights?.health && React11.createElement(
2981
+ Flex10,
2982
+ { variant: "row-center-space-between-nowrap-8" },
2983
+ React11.createElement(Typography9, { variant: "smallCaption-regular", color: "color-text-subtle" }, "Health"),
2984
+ React11.createElement(
2985
+ Typography9,
2986
+ { variant: "smallCaption-regular", color: "color-text-high" },
2987
+ `${insights.health.overall}/100`
2988
+ )
2989
+ ),
2990
+ // Available providers
2991
+ providers.length > 0 && React11.createElement(
2992
+ Flex10,
2993
+ { variant: "column-stretch-start-nowrap-4" },
2994
+ React11.createElement(Typography9, { variant: "smallCaption-regular", color: "color-text-subtle" }, "Available Providers"),
2995
+ React11.createElement(
2996
+ Flex10,
2997
+ { variant: "row-center-start-wrap-4" },
2998
+ ...providers.map(
2999
+ (p) => React11.createElement(Tag5, {
3000
+ key: p.name,
3001
+ size: "small",
3002
+ color: p.envVarsSet ? "positive" : "secondary"
3003
+ }, p.name)
3004
+ )
3005
+ )
3006
+ )
3007
+ )
3008
+ )
3009
+ );
3010
+ }
3011
+ var swarm_dashboard_default = SwarmDashboard;
3012
+ module.exports = SwarmDashboard;