@synergenius/flow-weaver-pack-weaver 0.9.62 → 0.9.78

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 (162) 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 +173 -19
  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/bot-registry.js +2 -2
  14. package/dist/bot/bot-registry.js.map +1 -1
  15. package/dist/bot/conversation-store.d.ts +1 -0
  16. package/dist/bot/conversation-store.d.ts.map +1 -1
  17. package/dist/bot/conversation-store.js.map +1 -1
  18. package/dist/bot/improve-loop.js.map +1 -1
  19. package/dist/bot/instance-manager.d.ts +31 -0
  20. package/dist/bot/instance-manager.d.ts.map +1 -0
  21. package/dist/bot/instance-manager.js +115 -0
  22. package/dist/bot/instance-manager.js.map +1 -0
  23. package/dist/bot/orchestrator.d.ts +36 -0
  24. package/dist/bot/orchestrator.d.ts.map +1 -0
  25. package/dist/bot/orchestrator.js +176 -0
  26. package/dist/bot/orchestrator.js.map +1 -0
  27. package/dist/bot/profile-store.d.ts +36 -0
  28. package/dist/bot/profile-store.d.ts.map +1 -0
  29. package/dist/bot/profile-store.js +208 -0
  30. package/dist/bot/profile-store.js.map +1 -0
  31. package/dist/bot/profile-types.d.ts +126 -0
  32. package/dist/bot/profile-types.d.ts.map +1 -0
  33. package/dist/bot/profile-types.js +7 -0
  34. package/dist/bot/profile-types.js.map +1 -0
  35. package/dist/bot/session-state.d.ts +25 -0
  36. package/dist/bot/session-state.d.ts.map +1 -0
  37. package/dist/bot/session-state.js +110 -0
  38. package/dist/bot/session-state.js.map +1 -0
  39. package/dist/bot/swarm-controller.d.ts +37 -21
  40. package/dist/bot/swarm-controller.d.ts.map +1 -1
  41. package/dist/bot/swarm-controller.js +344 -163
  42. package/dist/bot/swarm-controller.js.map +1 -1
  43. package/dist/bot/task-prompt-builder.d.ts +2 -1
  44. package/dist/bot/task-prompt-builder.d.ts.map +1 -1
  45. package/dist/bot/task-prompt-builder.js +33 -10
  46. package/dist/bot/task-prompt-builder.js.map +1 -1
  47. package/dist/bot/task-queue.d.ts +46 -0
  48. package/dist/bot/task-queue.d.ts.map +1 -0
  49. package/dist/bot/task-queue.js +237 -0
  50. package/dist/bot/task-queue.js.map +1 -0
  51. package/dist/bot/task-store.d.ts +1 -6
  52. package/dist/bot/task-store.d.ts.map +1 -1
  53. package/dist/bot/task-store.js +27 -78
  54. package/dist/bot/task-store.js.map +1 -1
  55. package/dist/bot/task-types.d.ts +8 -4
  56. package/dist/bot/task-types.d.ts.map +1 -1
  57. package/dist/cli-handlers.d.ts.map +1 -1
  58. package/dist/cli-handlers.js +2 -3
  59. package/dist/cli-handlers.js.map +1 -1
  60. package/dist/cli.d.ts +3 -0
  61. package/dist/cli.d.ts.map +1 -0
  62. package/dist/cli.js +749 -0
  63. package/dist/cli.js.map +1 -0
  64. package/dist/docs/docs/weaver-bot-usage.md +35 -18
  65. package/dist/docs/docs/weaver-config.md +20 -0
  66. package/dist/docs/docs/weaver-task-queue.md +31 -19
  67. package/dist/docs/weaver-config.md +15 -9
  68. package/dist/mcp-tools.d.ts +17 -0
  69. package/dist/mcp-tools.d.ts.map +1 -1
  70. package/dist/mcp-tools.js +98 -232
  71. package/dist/mcp-tools.js.map +1 -1
  72. package/dist/node-types/orchestrator-dispatch.d.ts +17 -0
  73. package/dist/node-types/orchestrator-dispatch.d.ts.map +1 -0
  74. package/dist/node-types/orchestrator-dispatch.js +63 -0
  75. package/dist/node-types/orchestrator-dispatch.js.map +1 -0
  76. package/dist/node-types/orchestrator-load-state.d.ts +16 -0
  77. package/dist/node-types/orchestrator-load-state.d.ts.map +1 -0
  78. package/dist/node-types/orchestrator-load-state.js +60 -0
  79. package/dist/node-types/orchestrator-load-state.js.map +1 -0
  80. package/dist/node-types/orchestrator-route.d.ts +16 -0
  81. package/dist/node-types/orchestrator-route.d.ts.map +1 -0
  82. package/dist/node-types/orchestrator-route.js +28 -0
  83. package/dist/node-types/orchestrator-route.js.map +1 -0
  84. package/dist/node-types/receive-task.d.ts +2 -3
  85. package/dist/node-types/receive-task.d.ts.map +1 -1
  86. package/dist/node-types/receive-task.js +3 -28
  87. package/dist/node-types/receive-task.js.map +1 -1
  88. package/dist/templates/weaver-template.d.ts +11 -0
  89. package/dist/templates/weaver-template.d.ts.map +1 -0
  90. package/dist/templates/weaver-template.js +53 -0
  91. package/dist/templates/weaver-template.js.map +1 -0
  92. package/dist/ui/bot-constants.d.ts +14 -0
  93. package/dist/ui/bot-constants.d.ts.map +1 -0
  94. package/dist/ui/bot-constants.js +189 -0
  95. package/dist/ui/bot-constants.js.map +1 -0
  96. package/dist/ui/bot-panel.js +51 -90
  97. package/dist/ui/bot-slot-card.js +87 -122
  98. package/dist/ui/budget-bar.js +5 -3
  99. package/dist/ui/chat-task-result.js +4 -7
  100. package/dist/ui/decision-log.js +136 -0
  101. package/dist/ui/profile-card.js +158 -0
  102. package/dist/ui/profile-editor.js +597 -0
  103. package/dist/ui/swarm-controls.js +36 -27
  104. package/dist/ui/swarm-dashboard.js +2034 -736
  105. package/dist/ui/task-create-form.js +39 -116
  106. package/dist/ui/task-detail-view.js +490 -239
  107. package/dist/ui/task-pool-list.js +69 -94
  108. package/dist/workflows/orchestrator.d.ts +21 -0
  109. package/dist/workflows/orchestrator.d.ts.map +1 -0
  110. package/dist/workflows/orchestrator.js +281 -0
  111. package/dist/workflows/orchestrator.js.map +1 -0
  112. package/dist/workflows/weaver-bot-session.d.ts +65 -0
  113. package/dist/workflows/weaver-bot-session.d.ts.map +1 -0
  114. package/dist/workflows/weaver-bot-session.js +68 -0
  115. package/dist/workflows/weaver-bot-session.js.map +1 -0
  116. package/dist/workflows/weaver.d.ts +24 -0
  117. package/dist/workflows/weaver.d.ts.map +1 -0
  118. package/dist/workflows/weaver.js +28 -0
  119. package/dist/workflows/weaver.js.map +1 -0
  120. package/flowweaver.manifest.json +253 -66
  121. package/package.json +1 -1
  122. package/src/ai-chat-provider.ts +184 -18
  123. package/src/bot/ai-router.ts +132 -0
  124. package/src/bot/bot-registry.ts +2 -2
  125. package/src/bot/conversation-store.ts +2 -1
  126. package/src/bot/improve-loop.ts +6 -6
  127. package/src/bot/instance-manager.ts +128 -0
  128. package/src/bot/orchestrator.ts +244 -0
  129. package/src/bot/profile-store.ts +225 -0
  130. package/src/bot/profile-types.ts +141 -0
  131. package/src/bot/swarm-controller.ts +385 -186
  132. package/src/bot/task-prompt-builder.ts +37 -6
  133. package/src/bot/task-store.ts +28 -89
  134. package/src/bot/task-types.ts +10 -4
  135. package/src/cli-handlers.ts +2 -3
  136. package/src/docs/weaver-bot-usage.md +35 -18
  137. package/src/docs/weaver-config.md +20 -0
  138. package/src/docs/weaver-task-queue.md +31 -19
  139. package/src/mcp-tools.ts +129 -320
  140. package/src/node-types/orchestrator-dispatch.ts +71 -0
  141. package/src/node-types/orchestrator-load-state.ts +66 -0
  142. package/src/node-types/orchestrator-route.ts +33 -0
  143. package/src/node-types/receive-task.ts +3 -26
  144. package/src/ui/bot-constants.ts +192 -0
  145. package/src/ui/bot-panel.tsx +55 -79
  146. package/src/ui/bot-slot-card.tsx +69 -117
  147. package/src/ui/budget-bar.tsx +5 -3
  148. package/src/ui/chat-task-result.tsx +6 -9
  149. package/src/ui/decision-log.tsx +148 -0
  150. package/src/ui/profile-card.tsx +157 -0
  151. package/src/ui/profile-editor.tsx +384 -0
  152. package/src/ui/swarm-controls.tsx +35 -31
  153. package/src/ui/swarm-dashboard.tsx +409 -80
  154. package/src/ui/task-create-form.tsx +29 -119
  155. package/src/ui/task-detail-view.tsx +461 -215
  156. package/src/ui/task-pool-list.tsx +74 -95
  157. package/src/workflows/orchestrator.ts +302 -0
  158. package/dist/docs/weaver-bot-usage.md +0 -34
  159. package/dist/docs/weaver-genesis.md +0 -32
  160. package/dist/docs/weaver-task-queue.md +0 -34
  161. package/src/bot/error-guide.ts +0 -4
  162. package/src/bot/retry-utils.ts +0 -4
@@ -0,0 +1,384 @@
1
+ /**
2
+ * ProfileEditor — unified component for creating and editing bot profiles.
3
+ *
4
+ * Handles both modes:
5
+ * - Create: all fields start empty/default, saves via fw_weaver_profile_create.
6
+ * - Edit: loads profile by ID on mount, saves via fw_weaver_profile_update.
7
+ *
8
+ * Calls tool functions internally via usePackWorkspace.
9
+ *
10
+ * Pattern: CommonJS require for platform deps, ESM import for local files,
11
+ * React.createElement throughout, module.exports at end.
12
+ */
13
+ const React = require('react');
14
+ const { useState, useEffect, useCallback } = React;
15
+ const {
16
+ Flex, Typography, Input, Button, IconButton, Icon, SectionTitle, Checkbox, Field,
17
+ IconPicker, ColorPicker, toast, usePackWorkspace,
18
+ } = require('@fw/plugin-ui-kit');
19
+
20
+ import { ICON_CATALOG, BOT_COLORS } from './bot-constants';
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // Types
24
+ // ---------------------------------------------------------------------------
25
+
26
+ interface ProfileEditorProps {
27
+ mode: 'create' | 'edit';
28
+ profileId?: string;
29
+ bots: Array<{ id: string; name: string; icon?: string; color?: string }>;
30
+ onSave: () => void;
31
+ onCancel: () => void;
32
+ onDelete?: () => void;
33
+ }
34
+
35
+ // ---------------------------------------------------------------------------
36
+ // Component
37
+ // ---------------------------------------------------------------------------
38
+
39
+ function ProfileEditor({ mode, profileId, bots, onSave, onCancel, onDelete }: ProfileEditorProps) {
40
+ const ctx = usePackWorkspace();
41
+ const { callTool } = ctx;
42
+
43
+ // --- Field state ---
44
+ const [name, setName] = useState('');
45
+ const [description, setDescription] = useState('');
46
+ const [botId, setBotId] = useState(mode === 'create' && bots.length > 0 ? bots[0].id : '');
47
+ const [icon, setIcon] = useState('smartToy');
48
+ const [color, setColor] = useState('color-node-blue-icon');
49
+ const [costStrategy, setCostStrategy] = useState<'frugal' | 'balanced' | 'performance'>('balanced');
50
+ const [capabilities, setCapabilities] = useState<Array<{ name: string; description: string }>>([]);
51
+ const [capName, setCapName] = useState('');
52
+ const [capDescription, setCapDescription] = useState('');
53
+ const [instructions, setInstructions] = useState('');
54
+ const [requireApproval, setRequireApproval] = useState(false);
55
+ const [loading, setLoading] = useState(mode === 'edit');
56
+
57
+ // --- Load profile for edit mode ---
58
+ useEffect(() => {
59
+ if (mode !== 'edit' || !profileId) return;
60
+ let cancelled = false;
61
+
62
+ (async () => {
63
+ try {
64
+ const raw = await callTool('fw_weaver_profile_list', {});
65
+ if (cancelled) return;
66
+ const data = typeof raw === 'string' ? JSON.parse(raw) : raw;
67
+ const list = Array.isArray(data) ? data : [];
68
+ const profile = list.find((p: Record<string, unknown>) => p.id === profileId);
69
+ if (!profile) {
70
+ toast('Profile not found', { type: 'error' });
71
+ onCancel();
72
+ return;
73
+ }
74
+ setName((profile.name as string) || '');
75
+ setDescription((profile.description as string) || '');
76
+ setBotId((profile.botId as string) || '');
77
+ setIcon((profile.icon as string) || 'smartToy');
78
+ setColor((profile.color as string) || 'color-node-blue-icon');
79
+ const prefs = (profile.preferences || {}) as Record<string, unknown>;
80
+ setCostStrategy((prefs.costStrategy as 'frugal' | 'balanced' | 'performance') || 'balanced');
81
+ setCapabilities(
82
+ Array.isArray(profile.capabilities)
83
+ ? (profile.capabilities as Array<{ name: string; description: string }>)
84
+ : [],
85
+ );
86
+ setInstructions((prefs.instructions as string) || '');
87
+ setRequireApproval(!!(prefs.requireApproval));
88
+ } catch {
89
+ toast('Failed to load profile', { type: 'error' });
90
+ onCancel();
91
+ } finally {
92
+ if (!cancelled) setLoading(false);
93
+ }
94
+ })();
95
+
96
+ return () => { cancelled = true; };
97
+ }, [mode, profileId, callTool, onCancel]);
98
+
99
+ // --- Capabilities ---
100
+ const handleAddCapability = useCallback(() => {
101
+ const trimName = capName.trim();
102
+ const trimDesc = capDescription.trim();
103
+ if (!trimName || !trimDesc) return;
104
+ setCapabilities((prev: Array<{ name: string; description: string }>) => [...prev, { name: trimName, description: trimDesc }]);
105
+ setCapName('');
106
+ setCapDescription('');
107
+ }, [capName, capDescription]);
108
+
109
+ const handleRemoveCapability = useCallback((index: number) => {
110
+ setCapabilities((prev: Array<{ name: string; description: string }>) => prev.filter((_: unknown, i: number) => i !== index));
111
+ }, []);
112
+
113
+ // --- Save ---
114
+ const handleSave = useCallback(async () => {
115
+ if (!name.trim()) {
116
+ toast('Profile name is required', { type: 'error' });
117
+ return;
118
+ }
119
+ if (!botId) {
120
+ toast('Select a bot workflow', { type: 'error' });
121
+ return;
122
+ }
123
+ if (capabilities.length === 0) {
124
+ toast('Add at least one capability', { type: 'error' });
125
+ return;
126
+ }
127
+
128
+ try {
129
+ if (mode === 'create') {
130
+ await callTool('fw_weaver_profile_create', {
131
+ name: name.trim(),
132
+ description: description.trim(),
133
+ botId,
134
+ icon,
135
+ color,
136
+ capabilities,
137
+ costStrategy,
138
+ instructions: instructions.trim() || undefined,
139
+ requireApproval,
140
+ });
141
+ toast('Profile created', { type: 'success' });
142
+ } else {
143
+ await callTool('fw_weaver_profile_update', {
144
+ id: profileId,
145
+ name: name.trim(),
146
+ description: description.trim(),
147
+ icon,
148
+ color,
149
+ capabilities,
150
+ costStrategy,
151
+ instructions: instructions.trim() || undefined,
152
+ requireApproval,
153
+ });
154
+ toast('Profile updated', { type: 'success' });
155
+ }
156
+ onSave();
157
+ } catch (err: unknown) {
158
+ toast(err instanceof Error ? err.message : `Failed to ${mode} profile`, { type: 'error' });
159
+ }
160
+ }, [mode, profileId, name, description, botId, icon, color, costStrategy, capabilities, instructions, requireApproval, callTool, onSave]);
161
+
162
+ // --- Delete ---
163
+ const handleDelete = useCallback(async () => {
164
+ if (!profileId) return;
165
+ const ok = await ctx.confirm('Are you sure you want to delete this profile?', {
166
+ title: 'Delete Profile',
167
+ confirmLabel: 'Delete',
168
+ state: 'danger',
169
+ });
170
+ if (!ok) return;
171
+ try {
172
+ await callTool('fw_weaver_profile_delete', { id: profileId });
173
+ toast('Profile deleted', { type: 'success' });
174
+ if (onDelete) onDelete();
175
+ } catch (err: unknown) {
176
+ toast(err instanceof Error ? err.message : 'Failed to delete profile', { type: 'error' });
177
+ }
178
+ }, [profileId, callTool, onDelete, ctx]);
179
+
180
+
181
+
182
+ // --- Loading state ---
183
+ if (loading) {
184
+ return React.createElement(Flex, {
185
+ variant: 'column-center-center-nowrap-12',
186
+ style: { padding: '24px 16px' },
187
+ },
188
+ React.createElement(Typography, { variant: 'caption-regular', color: 'color-text-subtle' }, 'Loading profile...'),
189
+ );
190
+ }
191
+
192
+ // --- Render ---
193
+ return React.createElement(Flex, {
194
+ variant: 'column-stretch-start-nowrap-0',
195
+ style: { width: '100%', height: '100%', overflow: 'hidden' },
196
+ },
197
+ // ── Header bar ──
198
+ React.createElement(Flex, {
199
+ variant: 'row-center-space-between-nowrap-8',
200
+ style: { padding: '8px 16px', flexShrink: 0, borderBottom: '1px solid var(--color-border-default)' },
201
+ },
202
+ React.createElement(Flex, { variant: 'row-center-start-nowrap-8' },
203
+ React.createElement(IconButton, {
204
+ icon: 'back', size: 'xs', variant: 'clear',
205
+ onClick: onCancel,
206
+ title: 'Back',
207
+ }),
208
+ React.createElement(Typography, { variant: 'caption-thick', color: 'color-text-high' }, mode === 'create' ? 'Create Profile' : 'Edit Profile'),
209
+ ),
210
+ mode === 'edit' && onDelete && React.createElement(IconButton, {
211
+ icon: 'outlinedDelete', size: 'sm', variant: 'clear', color: 'danger',
212
+ onClick: handleDelete,
213
+ title: 'Delete profile',
214
+ }),
215
+ ),
216
+
217
+ // ── Scrollable form body ──
218
+ React.createElement(Flex, {
219
+ variant: 'column-stretch-start-nowrap-10',
220
+ style: { flex: 1, minHeight: 0, overflow: 'auto', padding: '12px 16px' },
221
+ },
222
+
223
+ // ── Name ──
224
+ React.createElement(Field, { label: 'Name' },
225
+ React.createElement(Input, {
226
+ type: 'text', size: 'small',
227
+ placeholder: 'Profile name',
228
+ value: name,
229
+ onChange: (v: string) => setName(v),
230
+ defaultBoxStyle: { flex: 1, minWidth: 0 },
231
+ inputBoxStyle: { maxWidth: 'none' },
232
+ }),
233
+ ),
234
+
235
+ // ── Description ──
236
+ React.createElement(Field, { label: 'Description' },
237
+ React.createElement(Input, {
238
+ type: 'text', size: 'small',
239
+ placeholder: 'What does this profile do?',
240
+ value: description,
241
+ onChange: (v: string) => setDescription(v),
242
+ defaultBoxStyle: { flex: 1, minWidth: 0 },
243
+ inputBoxStyle: { maxWidth: 'none' },
244
+ }),
245
+ ),
246
+
247
+ // ── Bot ──
248
+ React.createElement(Field, { label: 'Bot' },
249
+ React.createElement(Input, {
250
+ type: 'select', size: 'small',
251
+ options: bots.map((b: { id: string; name: string; icon?: string }) => ({ id: b.id, label: b.name, icon: b.icon || 'smartToy' })),
252
+ optionId: botId,
253
+ onChange: (id: string) => setBotId(id),
254
+ disabled: mode === 'edit',
255
+ placeholder: 'Select bot',
256
+ defaultBoxStyle: { flex: 1, minWidth: 0 },
257
+ }),
258
+ ),
259
+
260
+ // ── Icon ──
261
+ React.createElement(Field, { label: 'Icon', align: 'start' },
262
+ React.createElement(IconPicker, {
263
+ catalog: ICON_CATALOG,
264
+ value: icon,
265
+ onChange: (v: string) => setIcon(v),
266
+ accentColor: color,
267
+ variant: 'inline',
268
+ }),
269
+ ),
270
+
271
+ // ── Color ──
272
+ React.createElement(Field, { label: 'Color', align: 'start' },
273
+ React.createElement(ColorPicker, {
274
+ colors: BOT_COLORS,
275
+ value: color,
276
+ onChange: (v: string) => setColor(v),
277
+ variant: 'inline',
278
+ }),
279
+ ),
280
+
281
+ // ── Cost Strategy ──
282
+ React.createElement(Field, { label: 'Cost Strategy' },
283
+ React.createElement(Input, {
284
+ type: 'select', size: 'small',
285
+ options: [
286
+ { id: 'frugal', label: 'Frugal', icon: 'timer' },
287
+ { id: 'balanced', label: 'Balanced', icon: 'syncAlt' },
288
+ { id: 'performance', label: 'Performance', icon: 'rocketLaunch' },
289
+ ],
290
+ optionId: costStrategy,
291
+ onChange: (id: string) => setCostStrategy(id as 'frugal' | 'balanced' | 'performance'),
292
+ defaultBoxStyle: { flex: 1, minWidth: 0 },
293
+ }),
294
+ ),
295
+
296
+ // ── Capabilities ──
297
+ React.createElement(Field, { label: 'Capabilities', align: 'start' },
298
+ React.createElement(Flex, { variant: 'column-stretch-start-nowrap-6' },
299
+ // Add capability row
300
+ React.createElement(Flex, { variant: 'row-center-start-nowrap-4', style: { overflow: 'hidden' } },
301
+ React.createElement(Input, {
302
+ type: 'text', size: 'small',
303
+ placeholder: 'Name',
304
+ value: capName,
305
+ onChange: (v: string) => setCapName(v),
306
+ defaultBoxStyle: { flex: 1, minWidth: 0 },
307
+ inputBoxStyle: { maxWidth: 'none' },
308
+ }),
309
+ React.createElement(Input, {
310
+ type: 'text', size: 'small',
311
+ placeholder: 'Description',
312
+ value: capDescription,
313
+ onChange: (v: string) => setCapDescription(v),
314
+ onEnter: handleAddCapability,
315
+ defaultBoxStyle: { flex: 2, minWidth: 0 },
316
+ inputBoxStyle: { maxWidth: 'none' },
317
+ }),
318
+ React.createElement(IconButton, {
319
+ icon: 'add', size: 'sm', variant: 'outlined', color: 'primary',
320
+ onClick: handleAddCapability,
321
+ disabled: !capName.trim() || !capDescription.trim(),
322
+ }),
323
+ ),
324
+ // Capability list
325
+ ...capabilities.map((cap: { name: string; description: string }, idx: number) =>
326
+ React.createElement(Flex, {
327
+ key: `${cap.name}-${idx}`,
328
+ variant: 'row-center-start-nowrap-6',
329
+ style: { paddingLeft: '4px' },
330
+ },
331
+ React.createElement(Typography, {
332
+ variant: 'smallCaption-regular', color: 'color-text-high',
333
+ style: { flexShrink: 0 },
334
+ }, `\u2022 ${cap.name}:`),
335
+ React.createElement(Typography, {
336
+ variant: 'smallCaption-regular', color: 'color-text-medium',
337
+ style: { flex: 1, minWidth: 0 },
338
+ }, cap.description),
339
+ React.createElement(IconButton, {
340
+ icon: 'close', size: 'xs', variant: 'clear', color: 'danger',
341
+ onClick: () => handleRemoveCapability(idx),
342
+ }),
343
+ ),
344
+ ),
345
+ ),
346
+ ),
347
+
348
+ // ── Instructions ──
349
+ React.createElement(Field, { label: 'Instructions' },
350
+ React.createElement(Input, {
351
+ type: 'text', size: 'small',
352
+ placeholder: 'Optional instructions for the bot',
353
+ value: instructions,
354
+ onChange: (v: string) => setInstructions(v),
355
+ defaultBoxStyle: { flex: 1, minWidth: 0 },
356
+ inputBoxStyle: { maxWidth: 'none' },
357
+ }),
358
+ ),
359
+
360
+ // ── Require Approval ──
361
+ React.createElement(Field, { label: '' },
362
+ React.createElement(Checkbox, {
363
+ checked: requireApproval,
364
+ onChange: (v: boolean) => setRequireApproval(v),
365
+ label: 'Require approval',
366
+ size: 'sm',
367
+ }),
368
+ ),
369
+
370
+ // ── Save button ──
371
+ React.createElement(Flex, { variant: 'row-center-end-nowrap-8' },
372
+ React.createElement(Button, {
373
+ size: 'xs', variant: 'fill', color: 'primary',
374
+ onClick: handleSave,
375
+ disabled: !name.trim() || !botId || capabilities.length === 0,
376
+ }, mode === 'create' ? 'Create' : 'Save'),
377
+ ),
378
+ ),
379
+ );
380
+ }
381
+
382
+ export { ProfileEditor };
383
+ export default ProfileEditor;
384
+ module.exports = ProfileEditor;
@@ -12,16 +12,16 @@ const {
12
12
  // Types
13
13
  // ---------------------------------------------------------------------------
14
14
 
15
- interface BotSlotInfo {
16
- botId: string;
17
- botName: string;
15
+ interface InstanceInfo {
16
+ instanceId: string;
17
+ profileId: string;
18
18
  status: 'idle' | 'executing' | 'paused' | 'stopped';
19
19
  currentTaskId?: string;
20
20
  }
21
21
 
22
22
  interface SwarmStatus {
23
23
  status: 'idle' | 'running' | 'paused' | 'stopping';
24
- bots: Record<string, BotSlotInfo>;
24
+ instances: Record<string, InstanceInfo>;
25
25
  maxConcurrent: number;
26
26
  tasksCompleted: number;
27
27
  tasksFailed: number;
@@ -39,15 +39,13 @@ interface SwarmControlsProps {
39
39
  // Helpers
40
40
  // ---------------------------------------------------------------------------
41
41
 
42
- type StatusVariant = 'positive' | 'negative' | 'caution' | 'neutral' | 'info';
43
-
44
- function statusToVariant(status: string): StatusVariant {
42
+ function swarmStatusToIconStatus(status: string): 'pending' | 'running' | 'completed' | 'failed' {
45
43
  switch (status) {
46
- case 'running': return 'positive';
47
- case 'paused': return 'caution';
48
- case 'stopping': return 'negative';
44
+ case 'running': return 'running';
45
+ case 'paused': return 'pending';
46
+ case 'stopping': return 'failed';
49
47
  case 'idle':
50
- default: return 'neutral';
48
+ default: return 'completed';
51
49
  }
52
50
  }
53
51
 
@@ -61,9 +59,9 @@ function statusLabel(status: string): string {
61
59
  }
62
60
  }
63
61
 
64
- function countActiveBots(bots: Record<string, BotSlotInfo>): { active: number; total: number } {
65
- const entries = Object.values(bots);
66
- const active = entries.filter(b => b.status === 'executing').length;
62
+ function countActiveInstances(instances: Record<string, InstanceInfo>): { active: number; total: number } {
63
+ const entries = Object.values(instances);
64
+ const active = entries.filter(i => i.status === 'executing').length;
67
65
  return { active, total: entries.length };
68
66
  }
69
67
 
@@ -78,8 +76,8 @@ function SwarmControls({ swarmStatus, onRefresh }: SwarmControlsProps) {
78
76
  const [stopping, setStopping] = useState(false);
79
77
 
80
78
  const status = swarmStatus?.status ?? 'idle';
81
- const bots = swarmStatus?.bots ?? {};
82
- const { active, total } = countActiveBots(bots);
79
+ const instances = swarmStatus?.instances ?? {};
80
+ const { active, total } = countActiveInstances(instances);
83
81
 
84
82
  const isIdle = status === 'idle';
85
83
  const isRunning = status === 'running';
@@ -144,6 +142,12 @@ function SwarmControls({ swarmStatus, onRefresh }: SwarmControlsProps) {
144
142
 
145
143
  const handleStop = useCallback(async () => {
146
144
  setStopping(true);
145
+ const ok = await ctx.confirm('This will stop all running instances and abort active tasks.', {
146
+ title: 'Stop Swarm',
147
+ confirmLabel: 'Stop',
148
+ state: 'danger',
149
+ });
150
+ if (!ok) { setStopping(false); return; }
147
151
  try {
148
152
  const result = await ctx.callTool('fw_weaver_swarm_stop', {});
149
153
  const data = result as Record<string, unknown> | null;
@@ -167,9 +171,9 @@ function SwarmControls({ swarmStatus, onRefresh }: SwarmControlsProps) {
167
171
  return React.createElement(Flex, {
168
172
  variant: 'row-center-space-between-nowrap-10',
169
173
  style: {
170
- flexShrink: 0,
171
174
  padding: '8px 16px',
172
175
  borderBottom: '1px solid var(--color-border-default)',
176
+ flexShrink: 0,
173
177
  backgroundColor: isRunning
174
178
  ? 'var(--color-brand-main-alpha-10)'
175
179
  : isPaused
@@ -180,19 +184,18 @@ function SwarmControls({ swarmStatus, onRefresh }: SwarmControlsProps) {
180
184
  // Left: status icon + label
181
185
  React.createElement(Flex, { variant: 'row-center-start-nowrap-8', style: { flexShrink: 0 } },
182
186
  React.createElement(StatusIcon, {
183
- variant: statusToVariant(status),
184
- size: 10,
187
+ status: swarmStatusToIconStatus(status),
188
+ size: 'sm',
185
189
  }),
186
190
  React.createElement(Typography, {
187
191
  variant: 'caption-bold',
188
- style: { textTransform: 'capitalize' },
189
192
  }, statusLabel(status)),
190
193
  ),
191
194
 
192
195
  // Center: bot count + stats
193
196
  React.createElement(Flex, {
194
197
  variant: 'row-center-start-nowrap-12',
195
- style: { flex: 1, fontSize: '12px' },
198
+ style: { flex: 1 },
196
199
  },
197
200
  total > 0 && React.createElement(Typography, {
198
201
  variant: 'caption-regular',
@@ -209,11 +212,12 @@ function SwarmControls({ swarmStatus, onRefresh }: SwarmControlsProps) {
209
212
  ),
210
213
 
211
214
  // Right: action buttons
212
- React.createElement(Flex, { variant: 'row-center-start-nowrap-6', style: { flexShrink: 0 } },
215
+ React.createElement(Flex, { variant: 'row-center-start-nowrap-4', style: { flexShrink: 0 } },
213
216
  // Start / Resume button
214
217
  (isIdle || isPaused) && React.createElement(Button, {
215
- size: 'sm',
216
- variant: 'clear',
218
+ size: 'xs',
219
+ variant: 'outlined',
220
+ color: 'primary',
217
221
  onClick: isPaused ? handleResume : handleStart,
218
222
  loading: starting,
219
223
  disabled: anyLoading,
@@ -221,8 +225,9 @@ function SwarmControls({ swarmStatus, onRefresh }: SwarmControlsProps) {
221
225
 
222
226
  // Pause button (only when running)
223
227
  isRunning && React.createElement(Button, {
224
- size: 'sm',
225
- variant: 'clear',
228
+ size: 'xs',
229
+ variant: 'outlined',
230
+ color: 'warning',
226
231
  onClick: handlePause,
227
232
  loading: pausing,
228
233
  disabled: anyLoading,
@@ -230,8 +235,8 @@ function SwarmControls({ swarmStatus, onRefresh }: SwarmControlsProps) {
230
235
 
231
236
  // Stop button (when running or paused)
232
237
  (isRunning || isPaused) && React.createElement(Button, {
233
- size: 'sm',
234
- variant: 'clear',
238
+ size: 'xs',
239
+ variant: 'outlined',
235
240
  color: 'danger',
236
241
  onClick: handleStop,
237
242
  loading: stopping,
@@ -240,12 +245,11 @@ function SwarmControls({ swarmStatus, onRefresh }: SwarmControlsProps) {
240
245
 
241
246
  // Refresh button (always available)
242
247
  React.createElement(IconButton, {
243
- icon: 'refresh',
248
+ icon: 'reset',
244
249
  size: 'xs',
245
- variant: 'clear',
250
+ variant: 'outlined',
246
251
  onClick: onRefresh,
247
252
  disabled: anyLoading,
248
- title: 'Refresh swarm status',
249
253
  }),
250
254
  ),
251
255
  );