@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.
- package/dist/ai-chat-provider.d.ts +12 -0
- package/dist/ai-chat-provider.d.ts.map +1 -1
- package/dist/ai-chat-provider.js +173 -19
- package/dist/ai-chat-provider.js.map +1 -1
- package/dist/bot/agent-loop.d.ts +20 -0
- package/dist/bot/agent-loop.d.ts.map +1 -0
- package/dist/bot/agent-loop.js +331 -0
- package/dist/bot/agent-loop.js.map +1 -0
- package/dist/bot/ai-router.d.ts +19 -0
- package/dist/bot/ai-router.d.ts.map +1 -0
- package/dist/bot/ai-router.js +104 -0
- package/dist/bot/ai-router.js.map +1 -0
- package/dist/bot/bot-registry.js +2 -2
- package/dist/bot/bot-registry.js.map +1 -1
- package/dist/bot/conversation-store.d.ts +1 -0
- package/dist/bot/conversation-store.d.ts.map +1 -1
- package/dist/bot/conversation-store.js.map +1 -1
- package/dist/bot/improve-loop.js.map +1 -1
- package/dist/bot/instance-manager.d.ts +31 -0
- package/dist/bot/instance-manager.d.ts.map +1 -0
- package/dist/bot/instance-manager.js +115 -0
- package/dist/bot/instance-manager.js.map +1 -0
- package/dist/bot/orchestrator.d.ts +36 -0
- package/dist/bot/orchestrator.d.ts.map +1 -0
- package/dist/bot/orchestrator.js +176 -0
- package/dist/bot/orchestrator.js.map +1 -0
- package/dist/bot/profile-store.d.ts +36 -0
- package/dist/bot/profile-store.d.ts.map +1 -0
- package/dist/bot/profile-store.js +208 -0
- package/dist/bot/profile-store.js.map +1 -0
- package/dist/bot/profile-types.d.ts +126 -0
- package/dist/bot/profile-types.d.ts.map +1 -0
- package/dist/bot/profile-types.js +7 -0
- package/dist/bot/profile-types.js.map +1 -0
- package/dist/bot/session-state.d.ts +25 -0
- package/dist/bot/session-state.d.ts.map +1 -0
- package/dist/bot/session-state.js +110 -0
- package/dist/bot/session-state.js.map +1 -0
- package/dist/bot/swarm-controller.d.ts +37 -21
- package/dist/bot/swarm-controller.d.ts.map +1 -1
- package/dist/bot/swarm-controller.js +344 -163
- package/dist/bot/swarm-controller.js.map +1 -1
- package/dist/bot/task-prompt-builder.d.ts +2 -1
- package/dist/bot/task-prompt-builder.d.ts.map +1 -1
- package/dist/bot/task-prompt-builder.js +33 -10
- package/dist/bot/task-prompt-builder.js.map +1 -1
- package/dist/bot/task-queue.d.ts +46 -0
- package/dist/bot/task-queue.d.ts.map +1 -0
- package/dist/bot/task-queue.js +237 -0
- package/dist/bot/task-queue.js.map +1 -0
- package/dist/bot/task-store.d.ts +1 -6
- package/dist/bot/task-store.d.ts.map +1 -1
- package/dist/bot/task-store.js +27 -78
- package/dist/bot/task-store.js.map +1 -1
- package/dist/bot/task-types.d.ts +8 -4
- package/dist/bot/task-types.d.ts.map +1 -1
- package/dist/cli-handlers.d.ts.map +1 -1
- package/dist/cli-handlers.js +2 -3
- package/dist/cli-handlers.js.map +1 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +749 -0
- package/dist/cli.js.map +1 -0
- package/dist/docs/docs/weaver-bot-usage.md +35 -18
- package/dist/docs/docs/weaver-config.md +20 -0
- package/dist/docs/docs/weaver-task-queue.md +31 -19
- package/dist/docs/weaver-config.md +15 -9
- package/dist/mcp-tools.d.ts +17 -0
- package/dist/mcp-tools.d.ts.map +1 -1
- package/dist/mcp-tools.js +98 -232
- package/dist/mcp-tools.js.map +1 -1
- package/dist/node-types/orchestrator-dispatch.d.ts +17 -0
- package/dist/node-types/orchestrator-dispatch.d.ts.map +1 -0
- package/dist/node-types/orchestrator-dispatch.js +63 -0
- package/dist/node-types/orchestrator-dispatch.js.map +1 -0
- package/dist/node-types/orchestrator-load-state.d.ts +16 -0
- package/dist/node-types/orchestrator-load-state.d.ts.map +1 -0
- package/dist/node-types/orchestrator-load-state.js +60 -0
- package/dist/node-types/orchestrator-load-state.js.map +1 -0
- package/dist/node-types/orchestrator-route.d.ts +16 -0
- package/dist/node-types/orchestrator-route.d.ts.map +1 -0
- package/dist/node-types/orchestrator-route.js +28 -0
- package/dist/node-types/orchestrator-route.js.map +1 -0
- package/dist/node-types/receive-task.d.ts +2 -3
- package/dist/node-types/receive-task.d.ts.map +1 -1
- package/dist/node-types/receive-task.js +3 -28
- package/dist/node-types/receive-task.js.map +1 -1
- package/dist/templates/weaver-template.d.ts +11 -0
- package/dist/templates/weaver-template.d.ts.map +1 -0
- package/dist/templates/weaver-template.js +53 -0
- package/dist/templates/weaver-template.js.map +1 -0
- package/dist/ui/bot-constants.d.ts +14 -0
- package/dist/ui/bot-constants.d.ts.map +1 -0
- package/dist/ui/bot-constants.js +189 -0
- package/dist/ui/bot-constants.js.map +1 -0
- package/dist/ui/bot-panel.js +51 -90
- package/dist/ui/bot-slot-card.js +87 -122
- package/dist/ui/budget-bar.js +5 -3
- package/dist/ui/chat-task-result.js +4 -7
- package/dist/ui/decision-log.js +136 -0
- package/dist/ui/profile-card.js +158 -0
- package/dist/ui/profile-editor.js +597 -0
- package/dist/ui/swarm-controls.js +36 -27
- package/dist/ui/swarm-dashboard.js +2034 -736
- package/dist/ui/task-create-form.js +39 -116
- package/dist/ui/task-detail-view.js +490 -239
- package/dist/ui/task-pool-list.js +69 -94
- package/dist/workflows/orchestrator.d.ts +21 -0
- package/dist/workflows/orchestrator.d.ts.map +1 -0
- package/dist/workflows/orchestrator.js +281 -0
- package/dist/workflows/orchestrator.js.map +1 -0
- package/dist/workflows/weaver-bot-session.d.ts +65 -0
- package/dist/workflows/weaver-bot-session.d.ts.map +1 -0
- package/dist/workflows/weaver-bot-session.js +68 -0
- package/dist/workflows/weaver-bot-session.js.map +1 -0
- package/dist/workflows/weaver.d.ts +24 -0
- package/dist/workflows/weaver.d.ts.map +1 -0
- package/dist/workflows/weaver.js +28 -0
- package/dist/workflows/weaver.js.map +1 -0
- package/flowweaver.manifest.json +253 -66
- package/package.json +1 -1
- package/src/ai-chat-provider.ts +184 -18
- package/src/bot/ai-router.ts +132 -0
- package/src/bot/bot-registry.ts +2 -2
- package/src/bot/conversation-store.ts +2 -1
- package/src/bot/improve-loop.ts +6 -6
- package/src/bot/instance-manager.ts +128 -0
- package/src/bot/orchestrator.ts +244 -0
- package/src/bot/profile-store.ts +225 -0
- package/src/bot/profile-types.ts +141 -0
- package/src/bot/swarm-controller.ts +385 -186
- package/src/bot/task-prompt-builder.ts +37 -6
- package/src/bot/task-store.ts +28 -89
- package/src/bot/task-types.ts +10 -4
- package/src/cli-handlers.ts +2 -3
- package/src/docs/weaver-bot-usage.md +35 -18
- package/src/docs/weaver-config.md +20 -0
- package/src/docs/weaver-task-queue.md +31 -19
- package/src/mcp-tools.ts +129 -320
- package/src/node-types/orchestrator-dispatch.ts +71 -0
- package/src/node-types/orchestrator-load-state.ts +66 -0
- package/src/node-types/orchestrator-route.ts +33 -0
- package/src/node-types/receive-task.ts +3 -26
- package/src/ui/bot-constants.ts +192 -0
- package/src/ui/bot-panel.tsx +55 -79
- package/src/ui/bot-slot-card.tsx +69 -117
- package/src/ui/budget-bar.tsx +5 -3
- package/src/ui/chat-task-result.tsx +6 -9
- package/src/ui/decision-log.tsx +148 -0
- package/src/ui/profile-card.tsx +157 -0
- package/src/ui/profile-editor.tsx +384 -0
- package/src/ui/swarm-controls.tsx +35 -31
- package/src/ui/swarm-dashboard.tsx +409 -80
- package/src/ui/task-create-form.tsx +29 -119
- package/src/ui/task-detail-view.tsx +461 -215
- package/src/ui/task-pool-list.tsx +74 -95
- package/src/workflows/orchestrator.ts +302 -0
- package/dist/docs/weaver-bot-usage.md +0 -34
- package/dist/docs/weaver-genesis.md +0 -32
- package/dist/docs/weaver-task-queue.md +0 -34
- package/src/bot/error-guide.ts +0 -4
- package/src/bot/retry-utils.ts +0 -4
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
const React = require('react');
|
|
21
21
|
const { useState, useEffect, useCallback, useRef } = React;
|
|
22
22
|
const {
|
|
23
|
-
Flex, ScrollArea, EmptyState, Typography,
|
|
23
|
+
Flex, ScrollArea, EmptyState, Typography, StatusIcon, Icon, Tag, Card, Tabs, SectionTitle, Button,
|
|
24
|
+
Input, IconButton, IconPicker, ColorPicker, toast, usePackWorkspace,
|
|
24
25
|
} = require('@fw/plugin-ui-kit');
|
|
25
26
|
|
|
26
27
|
// Local pack-specific components (bundled by esbuild)
|
|
@@ -30,38 +31,48 @@ import BotSlotCard from './bot-slot-card';
|
|
|
30
31
|
import TaskPoolList from './task-pool-list';
|
|
31
32
|
import TaskCreateForm from './task-create-form';
|
|
32
33
|
import TaskDetailView from './task-detail-view';
|
|
34
|
+
import ProfileCard from './profile-card';
|
|
35
|
+
import ProfileEditor from './profile-editor';
|
|
36
|
+
import DecisionLog from './decision-log';
|
|
37
|
+
import { ICON_CATALOG, BOT_COLORS } from './bot-constants';
|
|
33
38
|
|
|
34
39
|
// ---------------------------------------------------------------------------
|
|
35
40
|
// Types
|
|
36
41
|
// ---------------------------------------------------------------------------
|
|
37
42
|
|
|
38
|
-
interface
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
interface InstanceInfo {
|
|
44
|
+
instanceId: string;
|
|
45
|
+
profileId: string;
|
|
46
|
+
index: number;
|
|
41
47
|
status: 'idle' | 'executing' | 'paused' | 'stopped';
|
|
42
48
|
currentTaskId?: string;
|
|
43
49
|
currentRunId?: string;
|
|
44
50
|
startedAt?: string;
|
|
45
51
|
tokensUsed: number;
|
|
46
52
|
cost: number;
|
|
53
|
+
tasksCompleted: number;
|
|
54
|
+
tasksFailed: number;
|
|
47
55
|
}
|
|
48
56
|
|
|
49
|
-
interface
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
interface SwarmBudgetLayer {
|
|
58
|
+
limitTokens: number;
|
|
59
|
+
usedTokens: number;
|
|
60
|
+
limitCost: number;
|
|
61
|
+
usedCost: number;
|
|
54
62
|
}
|
|
55
63
|
|
|
56
64
|
interface SwarmStatus {
|
|
57
65
|
status: 'idle' | 'running' | 'paused' | 'stopping';
|
|
58
|
-
|
|
66
|
+
instances: Record<string, InstanceInfo>;
|
|
59
67
|
maxConcurrent: number;
|
|
60
68
|
tasksCompleted: number;
|
|
61
69
|
tasksFailed: number;
|
|
62
70
|
totalTokensUsed: number;
|
|
63
71
|
totalCost: number;
|
|
64
|
-
|
|
72
|
+
budgets?: {
|
|
73
|
+
workspace: SwarmBudgetLayer;
|
|
74
|
+
session: SwarmBudgetLayer;
|
|
75
|
+
};
|
|
65
76
|
startedAt?: string;
|
|
66
77
|
}
|
|
67
78
|
|
|
@@ -74,7 +85,7 @@ interface PoolTask {
|
|
|
74
85
|
priority: number;
|
|
75
86
|
isParent: boolean;
|
|
76
87
|
parentId?: string;
|
|
77
|
-
|
|
88
|
+
assignedProfile?: string;
|
|
78
89
|
createdAt: string;
|
|
79
90
|
}
|
|
80
91
|
|
|
@@ -106,10 +117,19 @@ function SwarmDashboard() {
|
|
|
106
117
|
|
|
107
118
|
// --- Navigation state ---
|
|
108
119
|
const [selectedTaskId, setSelectedTaskId] = useState<string | null>(null);
|
|
120
|
+
const [activeTab, setActiveTab] = useState('tasks');
|
|
121
|
+
const [editingBotId, setEditingBotId] = useState<string | null>(null);
|
|
122
|
+
const [profileEditorMode, setProfileEditorMode] = useState<'create' | 'edit' | null>(null);
|
|
123
|
+
const [editingProfileId, setEditingProfileId] = useState<string | null>(null);
|
|
109
124
|
|
|
110
125
|
// --- Data state ---
|
|
111
126
|
const [swarmStatus, setSwarmStatus] = useState<SwarmStatus | null>(null);
|
|
112
127
|
const [tasks, setTasks] = useState<PoolTask[]>([]);
|
|
128
|
+
const [registeredBots, setRegisteredBots] = useState<Array<{ id: string; name: string; description: string; icon?: string; color?: string }>>([]);
|
|
129
|
+
const [providers, setProviders] = useState<Array<{ name: string; source: string; envVarsSet: boolean }>>([]);
|
|
130
|
+
const [insights, setInsights] = useState<Record<string, unknown> | null>(null);
|
|
131
|
+
const [profiles, setProfiles] = useState<Array<Record<string, unknown>>>([]);
|
|
132
|
+
const [orchestratorDecisions, setOrchestratorDecisions] = useState<Array<Record<string, unknown>>>([]);
|
|
113
133
|
const [loading, setLoading] = useState(true);
|
|
114
134
|
|
|
115
135
|
// Refs to track mounted state for async cleanup
|
|
@@ -147,10 +167,54 @@ function SwarmDashboard() {
|
|
|
147
167
|
}
|
|
148
168
|
}, [callTool]);
|
|
149
169
|
|
|
170
|
+
const fetchBots = useCallback(async () => {
|
|
171
|
+
try {
|
|
172
|
+
const raw = await callTool('fw_weaver_list_bots', {});
|
|
173
|
+
if (!mountedRef.current) return;
|
|
174
|
+
const data = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
175
|
+
if (Array.isArray(data)) setRegisteredBots(data);
|
|
176
|
+
} catch { /* non-fatal */ }
|
|
177
|
+
}, [callTool]);
|
|
178
|
+
|
|
179
|
+
const fetchConfig = useCallback(async () => {
|
|
180
|
+
try {
|
|
181
|
+
const [provRaw, insRaw] = await Promise.all([
|
|
182
|
+
callTool('fw_weaver_providers', {}),
|
|
183
|
+
callTool('fw_weaver_insights', {}),
|
|
184
|
+
]);
|
|
185
|
+
if (!mountedRef.current) return;
|
|
186
|
+
const provData = typeof provRaw === 'string' ? JSON.parse(provRaw) : provRaw;
|
|
187
|
+
const insData = typeof insRaw === 'string' ? JSON.parse(insRaw) : insRaw;
|
|
188
|
+
if (Array.isArray(provData)) setProviders(provData);
|
|
189
|
+
if (insData && typeof insData === 'object') setInsights(insData as Record<string, unknown>);
|
|
190
|
+
} catch { /* non-fatal */ }
|
|
191
|
+
}, [callTool]);
|
|
192
|
+
|
|
193
|
+
const fetchProfiles = useCallback(async () => {
|
|
194
|
+
try {
|
|
195
|
+
const raw = await callTool('fw_weaver_profile_list', {});
|
|
196
|
+
if (!mountedRef.current) return;
|
|
197
|
+
const data = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
198
|
+
if (Array.isArray(data)) setProfiles(data);
|
|
199
|
+
} catch { /* non-fatal */ }
|
|
200
|
+
}, [callTool]);
|
|
201
|
+
|
|
202
|
+
const fetchOrchestratorStatus = useCallback(async () => {
|
|
203
|
+
try {
|
|
204
|
+
const raw = await callTool('fw_weaver_orchestrator_status', {});
|
|
205
|
+
if (!mountedRef.current) return;
|
|
206
|
+
const data = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
207
|
+
if (data && typeof data === 'object') {
|
|
208
|
+
const decisions = (data as Record<string, unknown>).recentDecisions;
|
|
209
|
+
if (Array.isArray(decisions)) setOrchestratorDecisions(decisions);
|
|
210
|
+
}
|
|
211
|
+
} catch { /* non-fatal */ }
|
|
212
|
+
}, [callTool]);
|
|
213
|
+
|
|
150
214
|
const refreshAll = useCallback(async () => {
|
|
151
|
-
await Promise.all([fetchSwarmStatus(), fetchTaskList()]);
|
|
215
|
+
await Promise.all([fetchSwarmStatus(), fetchTaskList(), fetchBots(), fetchConfig(), fetchProfiles(), fetchOrchestratorStatus()]);
|
|
152
216
|
if (mountedRef.current) setLoading(false);
|
|
153
|
-
}, [fetchSwarmStatus, fetchTaskList]);
|
|
217
|
+
}, [fetchSwarmStatus, fetchTaskList, fetchBots, fetchConfig, fetchProfiles, fetchOrchestratorStatus]);
|
|
154
218
|
|
|
155
219
|
// Initial fetch on mount
|
|
156
220
|
useEffect(() => {
|
|
@@ -169,11 +233,44 @@ function SwarmDashboard() {
|
|
|
169
233
|
return () => clearInterval(interval);
|
|
170
234
|
}, [fetchTaskList]);
|
|
171
235
|
|
|
236
|
+
// Poll profiles & orchestrator status every 10s
|
|
237
|
+
useEffect(() => {
|
|
238
|
+
const interval = setInterval(() => {
|
|
239
|
+
fetchProfiles();
|
|
240
|
+
fetchOrchestratorStatus();
|
|
241
|
+
}, 10_000);
|
|
242
|
+
return () => clearInterval(interval);
|
|
243
|
+
}, [fetchProfiles, fetchOrchestratorStatus]);
|
|
244
|
+
|
|
172
245
|
// Listen for platform refresh events
|
|
173
246
|
useEffect(() => {
|
|
174
247
|
return onRefresh(() => refreshAll());
|
|
175
248
|
}, [refreshAll, onRefresh]);
|
|
176
249
|
|
|
250
|
+
// --- Bot edit handlers ---
|
|
251
|
+
|
|
252
|
+
const handleUpdateBot = useCallback(async (botId: string, patch: Record<string, unknown>) => {
|
|
253
|
+
try {
|
|
254
|
+
await callTool('fw_weaver_register_bot', { id: botId, ...patch });
|
|
255
|
+
await fetchBots();
|
|
256
|
+
toast('Bot updated', { type: 'success' });
|
|
257
|
+
} catch (err: unknown) {
|
|
258
|
+
toast(err instanceof Error ? err.message : 'Failed to update bot', { type: 'error' });
|
|
259
|
+
}
|
|
260
|
+
}, [callTool, fetchBots]);
|
|
261
|
+
|
|
262
|
+
// --- Bot steering handlers ---
|
|
263
|
+
|
|
264
|
+
const handleSteerBot = useCallback(async (botId: string, command: string) => {
|
|
265
|
+
try {
|
|
266
|
+
await callTool('fw_weaver_steer', { botId, command });
|
|
267
|
+
toast(`${command} signal sent to ${botId}`, { type: 'info' });
|
|
268
|
+
fetchSwarmStatus();
|
|
269
|
+
} catch (err: unknown) {
|
|
270
|
+
toast(err instanceof Error ? err.message : `Failed to ${command}`, { type: 'error' });
|
|
271
|
+
}
|
|
272
|
+
}, [callTool, fetchSwarmStatus]);
|
|
273
|
+
|
|
177
274
|
// --- Navigation handlers ---
|
|
178
275
|
|
|
179
276
|
const handleTaskClick = useCallback((taskId: string) => {
|
|
@@ -201,11 +298,13 @@ function SwarmDashboard() {
|
|
|
201
298
|
|
|
202
299
|
// --- Render: Swarm Dashboard (default view) ---
|
|
203
300
|
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
const
|
|
207
|
-
const
|
|
208
|
-
const
|
|
301
|
+
const swarmInstances = swarmStatus?.instances ?? {};
|
|
302
|
+
const swarmInstanceEntries = Object.values(swarmInstances) as InstanceInfo[];
|
|
303
|
+
const hasSwarmInstances = swarmInstanceEntries.length > 0;
|
|
304
|
+
const hasRegisteredBots = registeredBots.length > 0;
|
|
305
|
+
const sessionBudget = swarmStatus?.budgets?.session;
|
|
306
|
+
const hasBudget = !!(sessionBudget && (sessionBudget.limitTokens > 0 || sessionBudget.limitCost > 0));
|
|
307
|
+
const hasContent = hasSwarmInstances || hasRegisteredBots || tasks.length > 0 || swarmStatus !== null;
|
|
209
308
|
|
|
210
309
|
return React.createElement(Flex, {
|
|
211
310
|
variant: 'column-stretch-start-nowrap-0',
|
|
@@ -220,96 +319,326 @@ function SwarmDashboard() {
|
|
|
220
319
|
// ── BudgetBar (below controls) ──────────────────────────────
|
|
221
320
|
hasBudget && React.createElement(Flex, {
|
|
222
321
|
variant: 'column-stretch-start-nowrap-4',
|
|
223
|
-
style: {
|
|
224
|
-
padding: '8px 16px',
|
|
225
|
-
borderBottom: '1px solid var(--color-border-default)',
|
|
226
|
-
flexShrink: 0,
|
|
227
|
-
},
|
|
322
|
+
style: { padding: '8px 16px', flexShrink: 0, borderBottom: '1px solid var(--color-border-default)' },
|
|
228
323
|
},
|
|
229
324
|
React.createElement(BudgetBar, {
|
|
230
325
|
label: 'Tokens',
|
|
231
|
-
used:
|
|
232
|
-
limit:
|
|
326
|
+
used: sessionBudget!.usedTokens,
|
|
327
|
+
limit: sessionBudget!.limitTokens,
|
|
233
328
|
unit: 'tokens',
|
|
234
329
|
}),
|
|
235
330
|
React.createElement(BudgetBar, {
|
|
236
331
|
label: 'Cost',
|
|
237
|
-
used:
|
|
238
|
-
limit:
|
|
332
|
+
used: sessionBudget!.usedCost,
|
|
333
|
+
limit: sessionBudget!.limitCost,
|
|
239
334
|
unit: 'USD',
|
|
240
335
|
}),
|
|
241
336
|
),
|
|
242
337
|
|
|
243
338
|
// ── Bot slot cards (horizontal scrollable row) ──────────────
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
key: bot.botId,
|
|
257
|
-
bot,
|
|
258
|
-
currentTaskTitle: resolveTaskTitle(bot.currentTaskId, tasks),
|
|
259
|
-
}),
|
|
260
|
-
),
|
|
261
|
-
),
|
|
339
|
+
// ── Tabs ──────────────────────────────────────────────────────
|
|
340
|
+
React.createElement(Tabs, {
|
|
341
|
+
tabs: [
|
|
342
|
+
{ id: 'tasks', title: `Tasks (${tasks.length})` },
|
|
343
|
+
{ id: 'bots', title: hasSwarmInstances ? `Instances (${swarmInstanceEntries.length})` : `Bots (${registeredBots.length})` },
|
|
344
|
+
{ id: 'profiles', title: `Profiles (${profiles.length})` },
|
|
345
|
+
{ id: 'config', title: 'Config' },
|
|
346
|
+
],
|
|
347
|
+
activeTabId: activeTab,
|
|
348
|
+
onSelectTab: (id: string) => setActiveTab(id),
|
|
349
|
+
size: 'sm',
|
|
350
|
+
}),
|
|
262
351
|
|
|
263
|
-
// ──
|
|
352
|
+
// ── Tab content ──────────────────────────────────────────────
|
|
264
353
|
React.createElement(Flex, {
|
|
265
354
|
variant: 'column-stretch-start-nowrap-0',
|
|
266
|
-
style: { flex: 1, minHeight: 0 },
|
|
355
|
+
style: { flex: 1, minHeight: 0, overflow: 'auto' },
|
|
267
356
|
},
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
357
|
+
|
|
358
|
+
// Tasks tab
|
|
359
|
+
activeTab === 'tasks' && React.createElement(Flex, {
|
|
360
|
+
variant: 'column-stretch-start-nowrap-0',
|
|
361
|
+
style: { flex: 1, minHeight: 0 },
|
|
362
|
+
},
|
|
363
|
+
!hasContent && !loading && React.createElement(EmptyState, {
|
|
364
|
+
icon: 'smartToy',
|
|
365
|
+
message: 'Swarm not started',
|
|
366
|
+
description: 'Start the swarm to begin processing tasks, or create tasks below.',
|
|
367
|
+
}),
|
|
368
|
+
|
|
369
|
+
hasContent && React.createElement(TaskPoolList, {
|
|
370
|
+
tasks,
|
|
371
|
+
onTaskClick: handleTaskClick,
|
|
372
|
+
}),
|
|
373
|
+
|
|
374
|
+
// Task create form (inside Tasks tab)
|
|
375
|
+
React.createElement(Flex, {
|
|
376
|
+
variant: 'column-stretch-start-nowrap-0',
|
|
377
|
+
style: { padding: '8px 16px', flexShrink: 0, borderTop: '1px solid var(--color-border-default)' },
|
|
274
378
|
},
|
|
379
|
+
React.createElement(TaskCreateForm, {
|
|
380
|
+
onTaskCreated: handleTaskCreated,
|
|
381
|
+
}),
|
|
382
|
+
),
|
|
383
|
+
),
|
|
384
|
+
|
|
385
|
+
// Bots tab
|
|
386
|
+
activeTab === 'bots' && React.createElement(Flex, {
|
|
387
|
+
variant: 'column-stretch-start-nowrap-0',
|
|
388
|
+
style: { flex: 1, minHeight: 0 },
|
|
275
389
|
},
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
},
|
|
390
|
+
// When swarm is running: show swarm instances
|
|
391
|
+
hasSwarmInstances && React.createElement(Flex, {
|
|
392
|
+
variant: 'column-stretch-start-nowrap-0',
|
|
393
|
+
},
|
|
394
|
+
React.createElement(Flex, {
|
|
395
|
+
variant: 'row-center-start-nowrap-8',
|
|
396
|
+
style: { padding: '8px 16px', borderBottom: '1px solid var(--color-border-default)' },
|
|
397
|
+
},
|
|
398
|
+
React.createElement(Typography, {
|
|
399
|
+
variant: 'smallCaption-regular', color: 'color-text-subtle',
|
|
400
|
+
style: { width: '120px', flexShrink: 0 },
|
|
401
|
+
}, 'Instance'),
|
|
402
|
+
React.createElement(Typography, {
|
|
403
|
+
variant: 'smallCaption-regular', color: 'color-text-subtle',
|
|
404
|
+
style: { width: '110px', flexShrink: 0 },
|
|
405
|
+
}, 'Bot'),
|
|
406
|
+
React.createElement(Typography, {
|
|
407
|
+
variant: 'smallCaption-regular', color: 'color-text-subtle',
|
|
408
|
+
style: { width: '70px', flexShrink: 0 },
|
|
409
|
+
}, 'Status'),
|
|
410
|
+
React.createElement(Typography, {
|
|
411
|
+
variant: 'smallCaption-regular', color: 'color-text-subtle',
|
|
412
|
+
style: { flex: 1, minWidth: 0 },
|
|
413
|
+
}, 'Task'),
|
|
414
|
+
React.createElement(Typography, {
|
|
415
|
+
variant: 'smallCaption-regular', color: 'color-text-subtle',
|
|
416
|
+
style: { width: '50px', flexShrink: 0, textAlign: 'right' },
|
|
417
|
+
}, 'Tokens'),
|
|
418
|
+
React.createElement(Typography, {
|
|
419
|
+
variant: 'smallCaption-regular', color: 'color-text-subtle',
|
|
420
|
+
style: { width: '50px', flexShrink: 0, textAlign: 'right' },
|
|
421
|
+
}, 'Cost'),
|
|
422
|
+
React.createElement(Typography, {
|
|
423
|
+
variant: 'smallCaption-regular', color: 'color-text-subtle',
|
|
424
|
+
style: { width: '50px', flexShrink: 0 },
|
|
425
|
+
}, ''),
|
|
426
|
+
),
|
|
427
|
+
// Instance rows
|
|
428
|
+
...swarmInstanceEntries.map((inst: InstanceInfo) => {
|
|
429
|
+
// Look up profile → bot for this instance
|
|
430
|
+
const profile = profiles.find((p: Record<string, unknown>) => p.id === inst.profileId);
|
|
431
|
+
const botId = profile?.botId as string | undefined;
|
|
432
|
+
const bot = registeredBots.find((b) => b.id === botId);
|
|
433
|
+
return React.createElement(BotSlotCard, {
|
|
434
|
+
key: inst.instanceId,
|
|
435
|
+
bot: {
|
|
436
|
+
botId: inst.instanceId,
|
|
437
|
+
botName: `${inst.profileId} #${inst.index}`,
|
|
438
|
+
status: inst.status,
|
|
439
|
+
currentTaskId: inst.currentTaskId,
|
|
440
|
+
currentRunId: inst.currentRunId,
|
|
441
|
+
startedAt: inst.startedAt,
|
|
442
|
+
tokensUsed: inst.tokensUsed,
|
|
443
|
+
cost: inst.cost,
|
|
444
|
+
},
|
|
445
|
+
profileName: (profile?.name as string) || inst.profileId,
|
|
446
|
+
botDisplayName: bot?.name,
|
|
447
|
+
botIcon: bot?.icon,
|
|
448
|
+
botColor: bot?.color,
|
|
449
|
+
currentTaskTitle: resolveTaskTitle(inst.currentTaskId, tasks),
|
|
450
|
+
onPause: (id: string) => handleSteerBot(id, 'pause'),
|
|
451
|
+
onResume: (id: string) => handleSteerBot(id, 'resume'),
|
|
452
|
+
onStop: (id: string) => handleSteerBot(id, 'cancel'),
|
|
453
|
+
});
|
|
454
|
+
}),
|
|
455
|
+
),
|
|
456
|
+
|
|
457
|
+
// When swarm is NOT running: show registered bots
|
|
458
|
+
!hasSwarmInstances && hasRegisteredBots && React.createElement(Flex, {
|
|
459
|
+
variant: 'column-stretch-start-nowrap-0',
|
|
460
|
+
style: { padding: '8px 16px' },
|
|
461
|
+
},
|
|
462
|
+
...registeredBots.map((bot) => {
|
|
463
|
+
const isEditing = editingBotId === bot.id;
|
|
464
|
+
return React.createElement(Flex, {
|
|
465
|
+
key: bot.id,
|
|
466
|
+
variant: 'column-stretch-start-nowrap-0',
|
|
467
|
+
style: { borderBottom: '1px solid var(--color-border-default)' },
|
|
468
|
+
},
|
|
469
|
+
// Bot row (clickable to toggle edit)
|
|
470
|
+
React.createElement(Flex, {
|
|
471
|
+
variant: 'row-center-start-nowrap-8',
|
|
472
|
+
style: { padding: '6px 0', cursor: 'pointer' },
|
|
473
|
+
onClick: () => setEditingBotId(isEditing ? null : bot.id),
|
|
474
|
+
},
|
|
475
|
+
React.createElement(Icon, {
|
|
476
|
+
name: bot.icon || 'smartToy', size: 16,
|
|
477
|
+
color: bot.color || 'color-text-medium',
|
|
478
|
+
}),
|
|
479
|
+
React.createElement(Flex, { variant: 'column-start-start-nowrap-1', style: { flex: 1, minWidth: 0 } },
|
|
480
|
+
React.createElement(Typography, { variant: 'smallCaption-regular', color: 'color-text-high' }, bot.name),
|
|
481
|
+
bot.description && React.createElement(Typography, { variant: 'smallCaption-regular', color: 'color-text-subtle' }, bot.description),
|
|
482
|
+
),
|
|
483
|
+
React.createElement(Icon, { name: isEditing ? 'expandLess' : 'expandMore', size: 14, color: 'color-text-subtle' }),
|
|
484
|
+
),
|
|
485
|
+
|
|
486
|
+
// Edit section (icon + color pickers)
|
|
487
|
+
isEditing && React.createElement(Flex, {
|
|
488
|
+
variant: 'column-stretch-start-nowrap-12',
|
|
489
|
+
style: { padding: '8px 0 12px 24px' },
|
|
490
|
+
},
|
|
491
|
+
React.createElement(IconPicker, {
|
|
492
|
+
catalog: ICON_CATALOG,
|
|
493
|
+
value: bot.icon || 'smartToy',
|
|
494
|
+
onChange: (icon: string) => handleUpdateBot(bot.id, { icon }),
|
|
495
|
+
accentColor: bot.color || undefined,
|
|
496
|
+
defaultExpanded: true,
|
|
497
|
+
}),
|
|
498
|
+
React.createElement(ColorPicker, {
|
|
499
|
+
colors: BOT_COLORS,
|
|
500
|
+
value: bot.color || '',
|
|
501
|
+
onChange: (color: string) => handleUpdateBot(bot.id, { color }),
|
|
502
|
+
defaultExpanded: true,
|
|
503
|
+
}),
|
|
504
|
+
),
|
|
505
|
+
);
|
|
506
|
+
}),
|
|
507
|
+
),
|
|
508
|
+
|
|
509
|
+
// No bots at all
|
|
510
|
+
!hasSwarmInstances && !hasRegisteredBots && React.createElement(EmptyState, {
|
|
511
|
+
icon: 'smartToy',
|
|
512
|
+
message: 'No bots registered',
|
|
513
|
+
description: 'Register bots to start the swarm.',
|
|
514
|
+
}),
|
|
280
515
|
),
|
|
281
516
|
|
|
282
|
-
//
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
517
|
+
// Profiles tab — when ProfileEditor is open, render it INSTEAD of the list
|
|
518
|
+
activeTab === 'profiles' && profileEditorMode != null && React.createElement(ProfileEditor, {
|
|
519
|
+
mode: profileEditorMode,
|
|
520
|
+
profileId: editingProfileId ?? undefined,
|
|
521
|
+
bots: registeredBots.map((b: { id: string; name: string; icon?: string; color?: string }) => ({ id: b.id, name: b.name, icon: b.icon, color: b.color })),
|
|
522
|
+
onSave: () => { setProfileEditorMode(null); setEditingProfileId(null); fetchProfiles(); },
|
|
523
|
+
onCancel: () => { setProfileEditorMode(null); setEditingProfileId(null); },
|
|
524
|
+
onDelete: profileEditorMode === 'edit'
|
|
525
|
+
? () => { setProfileEditorMode(null); setEditingProfileId(null); fetchProfiles(); }
|
|
526
|
+
: undefined,
|
|
287
527
|
}),
|
|
288
528
|
|
|
289
|
-
|
|
529
|
+
// Profiles tab — default list view
|
|
530
|
+
activeTab === 'profiles' && profileEditorMode == null && React.createElement(Flex, {
|
|
290
531
|
variant: 'column-stretch-start-nowrap-0',
|
|
291
532
|
style: { flex: 1, minHeight: 0 },
|
|
292
533
|
},
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
534
|
+
// ── Profile cards (scrollable) ──
|
|
535
|
+
React.createElement(Flex, {
|
|
536
|
+
variant: 'column-stretch-start-nowrap-8',
|
|
537
|
+
style: { flex: 1, minHeight: 0, overflow: 'auto', padding: '12px 16px' },
|
|
538
|
+
},
|
|
539
|
+
profiles.length > 0
|
|
540
|
+
? React.createElement(Flex, { variant: 'column-stretch-start-nowrap-8' },
|
|
541
|
+
...profiles.map((p: Record<string, unknown>) => {
|
|
542
|
+
const activeCount = swarmInstanceEntries.filter(
|
|
543
|
+
(inst: InstanceInfo) => inst.profileId === p.id && inst.status === 'executing',
|
|
544
|
+
).length;
|
|
545
|
+
return React.createElement(ProfileCard, {
|
|
546
|
+
key: p.id as string,
|
|
547
|
+
profile: p as unknown,
|
|
548
|
+
activeInstances: activeCount,
|
|
549
|
+
onEdit: (id: string) => { setEditingProfileId(id); setProfileEditorMode('edit'); },
|
|
550
|
+
onDelete: async (id: string) => {
|
|
551
|
+
const ok = await ctx.confirm('Are you sure you want to delete this profile?', {
|
|
552
|
+
title: 'Delete Profile',
|
|
553
|
+
confirmLabel: 'Delete',
|
|
554
|
+
state: 'danger',
|
|
555
|
+
});
|
|
556
|
+
if (!ok) return;
|
|
557
|
+
try {
|
|
558
|
+
await callTool('fw_weaver_profile_delete', { id });
|
|
559
|
+
await fetchProfiles();
|
|
560
|
+
toast('Profile deleted', { type: 'success' });
|
|
561
|
+
} catch (err: unknown) {
|
|
562
|
+
toast(err instanceof Error ? err.message : 'Failed to delete profile', { type: 'error' });
|
|
563
|
+
}
|
|
564
|
+
},
|
|
565
|
+
});
|
|
566
|
+
}),
|
|
567
|
+
)
|
|
568
|
+
: React.createElement(EmptyState, {
|
|
569
|
+
icon: 'person',
|
|
570
|
+
message: 'No profiles',
|
|
571
|
+
description: 'Profiles define how bots behave. Start the swarm to create defaults, or create one below.',
|
|
572
|
+
}),
|
|
573
|
+
|
|
574
|
+
// ── Routing / Decision Log (inside scrollable area) ──
|
|
575
|
+
orchestratorDecisions.length > 0 && React.createElement(DecisionLog, {
|
|
576
|
+
decisions: orchestratorDecisions as unknown[],
|
|
577
|
+
}),
|
|
578
|
+
),
|
|
579
|
+
|
|
580
|
+
// ── New Profile button (bottom bar) ──
|
|
581
|
+
React.createElement(Flex, {
|
|
582
|
+
variant: 'column-stretch-start-nowrap-0',
|
|
583
|
+
style: { flexShrink: 0, borderTop: '1px solid var(--color-border-default)', padding: '8px 16px' },
|
|
584
|
+
},
|
|
585
|
+
React.createElement(Button, {
|
|
586
|
+
size: 'xs', variant: 'clear', color: 'primary',
|
|
587
|
+
leftIcon: 'add',
|
|
588
|
+
onClick: () => setProfileEditorMode('create'),
|
|
589
|
+
}, 'New Profile'),
|
|
590
|
+
),
|
|
591
|
+
|
|
297
592
|
),
|
|
298
|
-
),
|
|
299
593
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
padding: '8px 16px',
|
|
305
|
-
borderTop: '1px solid var(--color-border-default)',
|
|
306
|
-
flexShrink: 0,
|
|
594
|
+
// Config tab
|
|
595
|
+
activeTab === 'config' && React.createElement(Flex, {
|
|
596
|
+
variant: 'column-stretch-start-nowrap-12',
|
|
597
|
+
style: { padding: '12px 16px' },
|
|
307
598
|
},
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
599
|
+
// ── Provider ──
|
|
600
|
+
React.createElement(Flex, { variant: 'row-center-space-between-nowrap-8' },
|
|
601
|
+
React.createElement(Typography, { variant: 'smallCaption-regular', color: 'color-text-subtle' }, 'Provider'),
|
|
602
|
+
React.createElement(Flex, { variant: 'row-center-start-nowrap-4' },
|
|
603
|
+
React.createElement(Typography, { variant: 'smallCaption-regular', color: 'color-text-high' },
|
|
604
|
+
providers.find((p) => p.envVarsSet)?.name ?? 'None detected',
|
|
605
|
+
),
|
|
606
|
+
providers.find((p) => p.envVarsSet) && React.createElement(Tag, { size: 'small', color: 'info' }, 'active'),
|
|
607
|
+
),
|
|
608
|
+
),
|
|
609
|
+
|
|
610
|
+
// Trust
|
|
611
|
+
insights?.trust && React.createElement(Flex, { variant: 'row-center-space-between-nowrap-8' },
|
|
612
|
+
React.createElement(Typography, { variant: 'smallCaption-regular', color: 'color-text-subtle' }, 'Trust'),
|
|
613
|
+
React.createElement(Typography, { variant: 'smallCaption-regular', color: 'color-text-high' },
|
|
614
|
+
`Phase ${(insights.trust as Record<string, unknown>).phase} · ${(insights.trust as Record<string, unknown>).score}/100`,
|
|
615
|
+
),
|
|
616
|
+
),
|
|
617
|
+
|
|
618
|
+
// Health
|
|
619
|
+
insights?.health && React.createElement(Flex, { variant: 'row-center-space-between-nowrap-8' },
|
|
620
|
+
React.createElement(Typography, { variant: 'smallCaption-regular', color: 'color-text-subtle' }, 'Health'),
|
|
621
|
+
React.createElement(Typography, { variant: 'smallCaption-regular', color: 'color-text-high' },
|
|
622
|
+
`${(insights.health as Record<string, unknown>).overall}/100`,
|
|
623
|
+
),
|
|
624
|
+
),
|
|
625
|
+
|
|
626
|
+
// Available providers
|
|
627
|
+
providers.length > 0 && React.createElement(Flex, { variant: 'column-stretch-start-nowrap-4' },
|
|
628
|
+
React.createElement(Typography, { variant: 'smallCaption-regular', color: 'color-text-subtle' }, 'Available Providers'),
|
|
629
|
+
React.createElement(Flex, { variant: 'row-center-start-wrap-4' },
|
|
630
|
+
...providers.map((p) =>
|
|
631
|
+
React.createElement(Tag, {
|
|
632
|
+
key: p.name,
|
|
633
|
+
size: 'small',
|
|
634
|
+
color: p.envVarsSet ? 'positive' : 'secondary',
|
|
635
|
+
}, p.name),
|
|
636
|
+
),
|
|
637
|
+
),
|
|
638
|
+
),
|
|
639
|
+
),
|
|
312
640
|
),
|
|
641
|
+
|
|
313
642
|
);
|
|
314
643
|
}
|
|
315
644
|
|