@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,157 @@
1
+ /**
2
+ * ProfileCard — displays a bot profile summary with capabilities, preferences,
3
+ * instance count, and budget info. Used in the Config tab of the swarm dashboard.
4
+ *
5
+ * Pattern: CommonJS require for platform deps, React.createElement throughout.
6
+ */
7
+ const React = require('react');
8
+ const {
9
+ Flex, Typography, Icon, Chip, IconButton, Tag,
10
+ } = require('@fw/plugin-ui-kit');
11
+
12
+ // ---------------------------------------------------------------------------
13
+ // Types
14
+ // ---------------------------------------------------------------------------
15
+
16
+ interface Capability {
17
+ name: string;
18
+ description: string;
19
+ }
20
+
21
+ interface ProfilePreferences {
22
+ costStrategy: 'frugal' | 'balanced' | 'performance';
23
+ maxCostPerRun?: number;
24
+ maxCostPerTask?: number;
25
+ requireApproval: boolean;
26
+ instructions?: string;
27
+ }
28
+
29
+ interface Profile {
30
+ id: string;
31
+ name: string;
32
+ description: string;
33
+ icon: string;
34
+ color: string;
35
+ botId: string;
36
+ capabilities: Capability[];
37
+ preferences: ProfilePreferences;
38
+ minInstances: number;
39
+ maxInstances: number;
40
+ }
41
+
42
+ interface ProfileCardProps {
43
+ profile: Profile;
44
+ activeInstances: number;
45
+ onEdit?: (id: string) => void;
46
+ onDelete?: (id: string) => void;
47
+ }
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // Component
51
+ // ---------------------------------------------------------------------------
52
+
53
+ function ProfileCard({ profile, activeInstances, onEdit, onDelete }: ProfileCardProps) {
54
+ const {
55
+ id, name, description, icon, color, capabilities,
56
+ preferences, maxInstances,
57
+ } = profile;
58
+
59
+ const budgetParts: string[] = [];
60
+ if (preferences.maxCostPerRun) budgetParts.push(`$${preferences.maxCostPerRun.toFixed(2)}/run`);
61
+ if (preferences.maxCostPerTask) budgetParts.push(`$${preferences.maxCostPerTask.toFixed(2)}/task`);
62
+ const budgetText = budgetParts.length > 0 ? budgetParts.join(' / ') : null;
63
+
64
+ return React.createElement(Flex, {
65
+ variant: 'column-stretch-start-nowrap-6',
66
+ style: {
67
+ padding: '10px 12px',
68
+ borderRadius: '8px',
69
+ border: '1px solid var(--color-border-default)',
70
+ backgroundColor: 'var(--color-surface-elevated)',
71
+ },
72
+ },
73
+ // Row 1: Icon + Name + Edit/Delete buttons
74
+ React.createElement(Flex, { variant: 'row-center-start-nowrap-8' },
75
+ React.createElement(Flex, {
76
+ variant: 'row-center-center-nowrap-0',
77
+ style: { color: `var(--${color})`, flexShrink: 0 },
78
+ },
79
+ React.createElement(Icon, { name: icon || 'smartToy', size: 18 }),
80
+ ),
81
+ React.createElement(Typography, {
82
+ variant: 'caption-thick',
83
+ color: 'color-text-high',
84
+ style: { flex: 1, minWidth: 0 },
85
+ }, name),
86
+ onEdit && React.createElement(IconButton, {
87
+ icon: 'edit', size: 'xs', variant: 'clear',
88
+ onClick: () => onEdit(id),
89
+ title: 'Edit profile',
90
+ }),
91
+ onDelete && React.createElement(IconButton, {
92
+ icon: 'delete', size: 'xs', variant: 'clear', color: 'danger',
93
+ onClick: () => onDelete(id),
94
+ title: 'Delete profile',
95
+ }),
96
+ ),
97
+
98
+ // Description
99
+ description && React.createElement(Typography, {
100
+ variant: 'smallCaption-regular',
101
+ color: 'color-text-medium',
102
+ }, description),
103
+
104
+ // Divider
105
+ React.createElement(Flex, {
106
+ variant: 'row-center-start-nowrap-0',
107
+ style: { borderTop: '1px solid var(--color-border-default)', margin: '2px 0' },
108
+ }),
109
+
110
+ // Capabilities
111
+ capabilities.length > 0 && React.createElement(Flex, { variant: 'row-center-start-wrap-4' },
112
+ React.createElement(Typography, {
113
+ variant: 'smallCaption-regular',
114
+ color: 'color-text-subtle',
115
+ style: { flexShrink: 0 },
116
+ }, 'Capabilities:'),
117
+ ...capabilities.map((cap: Capability) =>
118
+ React.createElement('span', { key: cap.name, title: cap.description },
119
+ React.createElement(Chip, { label: cap.name, size: 'small', color: 'color-brand-main' }),
120
+ ),
121
+ ),
122
+ ),
123
+
124
+ // Cost strategy
125
+ React.createElement(Flex, { variant: 'row-center-start-wrap-12' },
126
+ React.createElement(Typography, {
127
+ variant: 'smallCaption-regular', color: 'color-text-subtle',
128
+ }, `Cost strategy: ${preferences.costStrategy}`),
129
+ ),
130
+
131
+ // Instances + Approval + Budget
132
+ React.createElement(Flex, { variant: 'row-center-start-wrap-12' },
133
+ React.createElement(Typography, {
134
+ variant: 'smallCaption-regular', color: 'color-text-subtle',
135
+ }, `Instances: ${activeInstances}/${maxInstances} active`),
136
+ React.createElement(Tag, {
137
+ size: 'small',
138
+ color: preferences.requireApproval ? 'caution' : 'positive',
139
+ }, preferences.requireApproval ? 'Approval required' : 'Auto-approve'),
140
+ ),
141
+
142
+ // Budget line
143
+ budgetText && React.createElement(Typography, {
144
+ variant: 'smallCaption-regular', color: 'color-text-subtle',
145
+ }, `Budget: ${budgetText}`),
146
+
147
+ // Instructions
148
+ preferences.instructions && React.createElement(Typography, {
149
+ variant: 'smallCaption-regular', color: 'color-text-subtle',
150
+ style: { fontStyle: 'italic' },
151
+ }, preferences.instructions),
152
+ );
153
+ }
154
+
155
+ export { ProfileCard };
156
+ export default ProfileCard;
157
+ module.exports = ProfileCard;
@@ -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;