@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
@@ -15,17 +15,15 @@ import { runWorkflow } from './bot/runner.js';
15
15
  import { RunStore } from './bot/run-store.js';
16
16
  import { CostStore } from './bot/cost-store.js';
17
17
  import { EventLog } from './bot/event-log.js';
18
- import { SessionStore } from './bot/session-state.js';
19
- import { TaskQueue } from './bot/task-queue.js';
20
- import { SteeringController } from './bot/steering.js';
21
18
  import { defaultRegistry, discoverProviders } from './bot/provider-registry.js';
22
19
  import { runRegistry } from './bot/run-registry.js';
23
20
  import { ProjectModelStore } from './bot/project-model.js';
24
21
  import { InsightEngine } from './bot/insight-engine.js';
25
22
  import { buildSystemPrompt } from './bot/system-prompt.js';
26
23
  import { BotRegistry } from './bot/bot-registry.js';
27
- /** Throttle orphan recovery to max once per 30s. */
28
- let lastOrphanRecoveryTime = 0;
24
+ import { TaskStore } from './bot/task-store.js';
25
+ import { SwarmController } from './bot/swarm-controller.js';
26
+ import { SwarmEventLog } from './bot/swarm-event-log.js';
29
27
  // ---------------------------------------------------------------------------
30
28
  // Tool handlers — reuse the same logic as MCP tools
31
29
  // ---------------------------------------------------------------------------
@@ -48,15 +46,21 @@ const toolHandlers = {
48
46
  const record = store.get(args.id);
49
47
  return record ? JSON.stringify(record, null, 2) : `No run found matching "${args.id}"`;
50
48
  }
49
+ const taskId = args.taskId;
50
+ const botId = args.botId;
51
51
  const records = store.list({
52
52
  outcome: args.outcome,
53
53
  workflowFile: args.workflowFile,
54
54
  limit: args.limit ?? 20,
55
+ taskId,
56
+ botId,
55
57
  });
56
58
  return records.length === 0 ? 'No runs recorded yet.' : JSON.stringify(records, null, 2);
57
59
  },
58
60
  async fw_weaver_costs(args) {
59
61
  const store = new CostStore();
62
+ const _taskId = args.taskId;
63
+ const _botId = args.botId;
60
64
  let sinceTs;
61
65
  if (args.since) {
62
66
  const spec = args.since;
@@ -73,6 +77,7 @@ const toolHandlers = {
73
77
  sinceTs = ts;
74
78
  }
75
79
  }
80
+ // TODO: pass taskId/botId to CostStore once it supports per-task/per-bot filtering
76
81
  const summary = store.summarize({ since: sinceTs, model: args.model });
77
82
  return JSON.stringify(summary, null, 2);
78
83
  },
@@ -89,170 +94,33 @@ const toolHandlers = {
89
94
  }));
90
95
  return JSON.stringify(result, null, 2);
91
96
  },
92
- async fw_weaver_bot(args, ctx) {
93
- // Resolve bot from registry if botId is provided
97
+ async fw_weaver_steer(args, ctx) {
94
98
  const botId = args.botId;
95
- let registryWorkflowFile;
96
- if (botId) {
97
- const registry = new BotRegistry(ctx.workspacePath);
98
- const bot = registry.get(botId);
99
- if (!bot) {
100
- return JSON.stringify({ error: `Bot '${botId}' not found` });
101
- }
102
- const validation = registry.validate(botId);
103
- if (!validation.valid) {
104
- return JSON.stringify({ error: `Bot '${botId}' is invalid: ${validation.error}` });
105
- }
106
- registryWorkflowFile = path.isAbsolute(bot.filePath)
107
- ? bot.filePath
108
- : path.join(ctx.workspacePath, bot.filePath);
109
- }
110
- const task = {
111
- instruction: args.task,
112
- mode: args.mode ?? 'create',
113
- targets: args.targets,
114
- options: {
115
- template: args.template,
116
- dryRun: args.dryRun,
117
- autoApprove: args.autoApprove ?? false,
118
- },
119
- };
120
- const packRoot = new URL('..', import.meta.url);
121
- let workflowPath;
122
- if (registryWorkflowFile) {
123
- workflowPath = registryWorkflowFile;
124
- }
125
- else {
126
- workflowPath = fileURLToPath(new URL('src/workflows/weaver-bot.ts', packRoot));
127
- if (!fs.existsSync(workflowPath)) {
128
- workflowPath = fileURLToPath(new URL('dist/workflows/weaver-bot.js', packRoot));
129
- }
130
- }
131
- const projectDir = ctx.workspacePath || process.cwd();
132
- const runId = RunStore.newId();
133
- const eventLog = new EventLog(runId);
134
- // Clean up old event logs
135
- EventLog.cleanup();
136
- // Fire-and-forget: start workflow in background, events stream via EventLog
137
- eventLog.emit({ type: 'bot-started', timestamp: Date.now(), data: { instruction: task.instruction, mode: task.mode, runId } });
138
- // Register in run registry BEFORE calling runWorkflow to prevent the race condition
139
- // where fw_weaver_events self-healing sees the run as dead and writes .done prematurely.
140
- // The runner's finally block will remove it when execution actually completes.
141
- const earlyPromise = new Promise(() => { }); // placeholder, replaced by runner
142
- runRegistry.register(runId, { file: workflowPath, startedAt: new Date().toISOString(), promise: earlyPromise });
143
- // Use timeout-auto approval so the UI can show the approval card
144
- // and the file-based decision flow works
145
- const useAutoApprove = task.options?.autoApprove === true;
146
- const runPromise = runWorkflow(workflowPath, {
147
- runId,
148
- params: { taskJson: JSON.stringify(task), projectDir },
149
- dryRun: args.dryRun,
150
- config: useAutoApprove ? undefined : { provider: 'auto', approval: { mode: 'timeout-auto', timeoutSeconds: 300 } },
151
- eventLog,
152
- }).catch((err) => {
153
- try {
154
- eventLog.done();
155
- }
156
- catch { /* already done */ }
157
- console.error('[weaver-bot] Background execution failed:', err);
158
- }).finally(() => {
159
- // Ensure registry is cleaned up even if runner's finally didn't run
160
- runRegistry.remove(runId);
161
- });
162
- void runPromise;
163
- return JSON.stringify({ runId, instruction: task.instruction, status: 'started' });
164
- },
165
- async fw_weaver_steer(args) {
166
- const controller = new SteeringController();
167
- await controller.write({
99
+ if (!botId)
100
+ return JSON.stringify({ error: 'botId required' });
101
+ const weaverDir = path.join(ctx.workspacePath, '.weaver');
102
+ fs.mkdirSync(weaverDir, { recursive: true });
103
+ const steerPath = path.join(weaverDir, `steer-${botId}.json`);
104
+ fs.writeFileSync(steerPath, JSON.stringify({
168
105
  command: args.command,
169
106
  payload: args.payload,
170
107
  timestamp: Date.now(),
171
- });
172
- return `Steering command sent: ${args.command}`;
173
- },
174
- async fw_weaver_queue(args) {
175
- const queue = new TaskQueue();
176
- switch (args.action) {
177
- case 'add': {
178
- if (!args.task)
179
- return 'Error: task instruction required';
180
- const { id, duplicate } = await queue.add({ instruction: args.task, priority: 0 });
181
- return duplicate ? `Task already queued (${id})` : `Task added: ${id}`;
182
- }
183
- case 'list':
184
- return JSON.stringify(await queue.list(), null, 2);
185
- case 'clear': {
186
- const count = await queue.clear();
187
- return `Cleared ${count} task(s)`;
188
- }
189
- case 'remove': {
190
- if (!args.id)
191
- return 'Error: task ID required';
192
- const removed = await queue.remove(args.id);
193
- return removed ? `Removed ${args.id}` : `Not found: ${args.id}`;
194
- }
195
- case 'reorder': {
196
- if (!args.id)
197
- return 'Error: task ID required';
198
- if (args.priority === undefined)
199
- return 'Error: priority required';
200
- const reordered = await queue.reorder(args.id, args.priority);
201
- return reordered ? `Reordered ${args.id} to priority ${args.priority}` : `Not found: ${args.id}`;
202
- }
203
- default:
204
- return `Unknown action: ${args.action}`;
205
- }
108
+ }, null, 2), 'utf-8');
109
+ return JSON.stringify({ sent: true, botId, command: args.command });
206
110
  },
207
- async fw_weaver_status() {
208
- const store = new SessionStore();
209
- const state = store.load();
210
- if (!state) {
211
- return JSON.stringify({ status: 'no active session' }, null, 2);
212
- }
213
- // Startup cleanup: on first call after process restart, the registry is empty.
214
- // If session.json claims 'executing' but nothing is in the registry, heal it.
215
- if (!runRegistry.startupCleanupDone) {
216
- runRegistry.markStartupCleanupDone();
217
- if (state.status !== 'idle' && runRegistry.size === 0) {
218
- // Orphaned session from a previous process — heal it
219
- const orphanRunId = state.currentRunId;
220
- if (orphanRunId) {
221
- try {
222
- new EventLog(orphanRunId).done();
223
- }
224
- catch { /* non-fatal */ }
225
- }
226
- await store.update({ status: 'idle', currentTask: null, currentRunId: null });
227
- }
228
- }
229
- // Self-heal orphaned queue tasks: whenever session is idle and no runs are active,
230
- // reset any 'running' queue tasks back to 'pending'.
231
- // Throttled to max once per 30s to avoid excessive file I/O from polling.
232
- if (state.status === 'idle' && runRegistry.size === 0) {
233
- const now = Date.now();
234
- if (now - lastOrphanRecoveryTime > 30_000) {
235
- lastOrphanRecoveryTime = now;
236
- try {
237
- const queue = new TaskQueue();
238
- await queue.recoverOrphans(new Set(runRegistry.activeRunIds()));
239
- }
240
- catch { /* non-fatal */ }
241
- }
242
- }
243
- // Check if the current run is actually alive in the registry
244
- const currentRunId = state.currentRunId ?? null;
245
- const alive = currentRunId ? runRegistry.isAlive(currentRunId) : state.status === 'idle' ? null : runRegistry.size > 0;
246
- // Self-heal: if status says executing but run is dead, clean up
247
- if (currentRunId && alive === false && state.status !== 'idle') {
248
- try {
249
- new EventLog(currentRunId).done();
250
- }
251
- catch { /* non-fatal */ }
252
- await store.update({ status: 'idle', currentTask: null, currentRunId: null });
253
- return JSON.stringify({ ...state, status: 'idle', currentTask: null, currentRunId: null, alive: false }, null, 2);
254
- }
255
- return JSON.stringify({ ...state, alive }, null, 2);
111
+ async fw_weaver_status(_args, ctx) {
112
+ const controller = SwarmController.getInstance(ctx.workspacePath);
113
+ const state = controller.getStatus();
114
+ return JSON.stringify({
115
+ swarmStatus: state.status,
116
+ activeBots: Object.values(state.instances).filter(i => i.status === 'executing').length,
117
+ totalBots: Object.keys(state.instances).length,
118
+ tasksCompleted: state.tasksCompleted,
119
+ tasksFailed: state.tasksFailed,
120
+ totalTokensUsed: state.totalTokensUsed,
121
+ totalCost: state.totalCost,
122
+ budgets: state.budgets,
123
+ }, null, 2);
256
124
  },
257
125
  async fw_weaver_events(args) {
258
126
  const runId = args.runId;
@@ -324,156 +192,6 @@ const toolHandlers = {
324
192
  });
325
193
  return JSON.stringify(result, null, 2);
326
194
  },
327
- async fw_weaver_session(args, ctx) {
328
- const action = args.action;
329
- const store = new SessionStore();
330
- if (action === 'status') {
331
- const state = store.load();
332
- if (!state)
333
- return JSON.stringify({ status: 'no active session' });
334
- const crid = state.currentRunId ?? null;
335
- const alive = crid ? runRegistry.isAlive(crid) : null;
336
- return JSON.stringify({ ...state, alive }, null, 2);
337
- }
338
- if (action === 'stop') {
339
- const controller = new SteeringController();
340
- await controller.write({ command: 'cancel', timestamp: Date.now() });
341
- await store.update({ status: 'idle', currentTask: null });
342
- return JSON.stringify({ stopped: true });
343
- }
344
- if (action === 'start') {
345
- const queue = new TaskQueue();
346
- const tasks = await queue.list();
347
- const pending = tasks.filter(t => t.status === 'pending');
348
- if (pending.length === 0) {
349
- return JSON.stringify({ error: 'No pending tasks in queue' });
350
- }
351
- const maxTasks = args.maxTasks ?? pending.length;
352
- const sessionId = RunStore.newId().slice(0, 8);
353
- const sessionLog = new EventLog(`session-${sessionId}`);
354
- // Create session state (save, not update — update requires existing file)
355
- await store.save({
356
- sessionId,
357
- status: 'executing',
358
- currentTask: null,
359
- completedTasks: 0,
360
- totalCost: 0,
361
- startedAt: Date.now(),
362
- lastActivity: Date.now(),
363
- });
364
- sessionLog.emit({ type: 'session:started', timestamp: Date.now(), data: {
365
- sessionId, queueSize: pending.length, maxTasks,
366
- } });
367
- const projectDir = ctx.workspacePath || process.cwd();
368
- const packRoot = new URL('..', import.meta.url);
369
- let workflowPath = fileURLToPath(new URL('src/workflows/weaver-bot.ts', packRoot));
370
- if (!fs.existsSync(workflowPath)) {
371
- workflowPath = fileURLToPath(new URL('dist/workflows/weaver-bot.js', packRoot));
372
- }
373
- // Process tasks sequentially in background
374
- (async () => {
375
- let completed = 0;
376
- let failed = 0;
377
- try {
378
- for (let i = 0; i < Math.min(maxTasks, pending.length); i++) {
379
- const task = pending[i];
380
- // Check for cancel signal
381
- try {
382
- const ctrl = new SteeringController();
383
- const signal = await ctrl.check();
384
- if (signal?.command === 'cancel') {
385
- sessionLog.emit({ type: 'session:cancelled', timestamp: Date.now() });
386
- break;
387
- }
388
- }
389
- catch { /* non-fatal */ }
390
- const runId = RunStore.newId();
391
- const taskLog = new EventLog(runId);
392
- // Mark task as running in the queue
393
- try {
394
- await queue.markRunning(task.id);
395
- }
396
- catch { /* non-fatal */ }
397
- await store.update({
398
- status: 'executing',
399
- currentTask: task.instruction,
400
- currentRunId: runId,
401
- lastActivity: Date.now(),
402
- });
403
- sessionLog.emit({ type: 'session:task-start', timestamp: Date.now(), data: {
404
- taskId: runId, instruction: task.instruction, index: i, total: Math.min(maxTasks, pending.length),
405
- } });
406
- taskLog.emit({ type: 'bot-started', timestamp: Date.now(), data: {
407
- instruction: task.instruction, mode: 'create', runId,
408
- } });
409
- // Register in run registry BEFORE calling runWorkflow to prevent
410
- // fw_weaver_events self-healing from writing premature .done
411
- runRegistry.register(runId, { file: workflowPath, startedAt: new Date().toISOString(), promise: Promise.resolve() });
412
- try {
413
- const result = await runWorkflow(workflowPath, {
414
- runId,
415
- params: {
416
- taskJson: JSON.stringify({
417
- instruction: task.instruction,
418
- mode: 'create',
419
- options: { autoApprove: false },
420
- }),
421
- projectDir,
422
- },
423
- config: { provider: 'auto', approval: { mode: 'timeout-auto', timeoutSeconds: 300 } },
424
- eventLog: taskLog,
425
- });
426
- const success = result.success;
427
- completed += success ? 1 : 0;
428
- failed += success ? 0 : 1;
429
- sessionLog.emit({ type: 'session:task-complete', timestamp: Date.now(), data: {
430
- taskId: runId, outcome: result.outcome, duration: result.executionTime,
431
- cost: result.cost?.totalCost,
432
- } });
433
- // Mark task in queue
434
- try {
435
- await queue.remove(task.id);
436
- }
437
- catch { /* non-fatal */ }
438
- }
439
- catch (err) {
440
- failed++;
441
- sessionLog.emit({ type: 'session:task-complete', timestamp: Date.now(), data: {
442
- taskId: runId, outcome: 'error', error: String(err),
443
- } });
444
- }
445
- await store.update({
446
- completedTasks: completed + failed,
447
- currentRunId: null,
448
- lastActivity: Date.now(),
449
- });
450
- }
451
- sessionLog.emit({ type: 'session:ended', timestamp: Date.now(), data: {
452
- reason: 'complete', completed, failed, total: completed + failed,
453
- } });
454
- }
455
- catch (err) {
456
- console.error('[weaver-session] Session failed:', err);
457
- sessionLog.emit({ type: 'session:ended', timestamp: Date.now(), data: {
458
- reason: 'error', error: String(err),
459
- } });
460
- }
461
- finally {
462
- // GUARANTEED: session log finalized and state reset, even on crash
463
- try {
464
- sessionLog.done();
465
- }
466
- catch { /* already done */ }
467
- try {
468
- await store.update({ status: 'idle', currentTask: null, currentRunId: null });
469
- }
470
- catch { /* non-fatal */ }
471
- }
472
- })();
473
- return JSON.stringify({ started: true, sessionId, taskCount: Math.min(maxTasks, pending.length) });
474
- }
475
- return JSON.stringify({ error: `Unknown action: ${action}` });
476
- },
477
195
  async fw_weaver_insights(args, ctx) {
478
196
  const projectDir = args.projectDir || ctx.workspacePath;
479
197
  const pms = new ProjectModelStore(projectDir);
@@ -539,23 +257,261 @@ const toolHandlers = {
539
257
  const ejected = registry.eject(id, newFilePath);
540
258
  return JSON.stringify({ ejected, id, filePath: newFilePath });
541
259
  },
260
+ // ---------------------------------------------------------------------------
261
+ // Task CRUD tools (Swarm)
262
+ // ---------------------------------------------------------------------------
263
+ async fw_weaver_task_create(args, ctx) {
264
+ const store = new TaskStore(ctx.workspacePath);
265
+ const input = {
266
+ title: args.title,
267
+ description: args.description ?? '',
268
+ priority: args.priority,
269
+ parentId: args.parentId,
270
+ dependsOn: args.dependsOn,
271
+ budgetTokens: args.budgetTokens,
272
+ budgetCost: args.budgetCost,
273
+ timeoutMs: args.timeoutMs,
274
+ maxAttempts: args.maxAttempts,
275
+ createdBy: 'ai',
276
+ assignedProfile: args.assignedProfile,
277
+ complexity: args.complexity,
278
+ subtasks: args.subtasks,
279
+ };
280
+ const task = await store.create(input);
281
+ const subtasks = task.isParent ? await store.getSubtasks(task.id) : [];
282
+ return JSON.stringify({ task, subtasks }, null, 2);
283
+ },
284
+ async fw_weaver_task_list(args, ctx) {
285
+ const store = new TaskStore(ctx.workspacePath);
286
+ const tasks = await store.list({
287
+ status: args.status,
288
+ parentId: args.parentId,
289
+ botId: args.botId,
290
+ limit: args.limit,
291
+ });
292
+ return JSON.stringify(tasks, null, 2);
293
+ },
294
+ async fw_weaver_task_get(args, ctx) {
295
+ const store = new TaskStore(ctx.workspacePath);
296
+ const task = await store.get(args.id);
297
+ if (!task)
298
+ return JSON.stringify({ error: 'Task not found' });
299
+ const subtasks = task.isParent ? await store.getSubtasks(task.id) : [];
300
+ return JSON.stringify({ task, subtasks }, null, 2);
301
+ },
302
+ async fw_weaver_task_update(args, ctx) {
303
+ const store = new TaskStore(ctx.workspacePath);
304
+ const { id, ...patch } = args;
305
+ const task = await store.update(id, patch);
306
+ return JSON.stringify(task, null, 2);
307
+ },
308
+ async fw_weaver_task_cancel(args, ctx) {
309
+ const store = new TaskStore(ctx.workspacePath);
310
+ const task = await store.update(args.id, { status: 'cancelled' });
311
+ return JSON.stringify({ cancelled: true, task });
312
+ },
313
+ async fw_weaver_task_retry(args, ctx) {
314
+ const store = new TaskStore(ctx.workspacePath);
315
+ const task = await store.get(args.id);
316
+ if (!task)
317
+ return JSON.stringify({ error: 'Task not found' });
318
+ const updated = await store.update(args.id, {
319
+ status: 'pending',
320
+ attempt: 0,
321
+ });
322
+ return JSON.stringify({ retried: true, task: updated });
323
+ },
324
+ // ---------------------------------------------------------------------------
325
+ // Swarm control tools
326
+ // ---------------------------------------------------------------------------
327
+ async fw_weaver_swarm_start(args, ctx) {
328
+ const controller = SwarmController.getInstance(ctx.workspacePath);
329
+ await controller.start({
330
+ maxConcurrent: args.maxConcurrent,
331
+ sessionBudgetTokens: args.sessionBudgetTokens,
332
+ sessionBudgetCost: args.sessionBudgetCost,
333
+ });
334
+ return JSON.stringify(controller.getStatus(), null, 2);
335
+ },
336
+ async fw_weaver_swarm_pause(_args, ctx) {
337
+ const controller = SwarmController.getInstance(ctx.workspacePath);
338
+ await controller.pause();
339
+ return JSON.stringify(controller.getStatus(), null, 2);
340
+ },
341
+ async fw_weaver_swarm_stop(_args, ctx) {
342
+ const controller = SwarmController.getInstance(ctx.workspacePath);
343
+ await controller.stop();
344
+ return JSON.stringify(controller.getStatus(), null, 2);
345
+ },
346
+ async fw_weaver_swarm_status(_args, ctx) {
347
+ const controller = SwarmController.getInstance(ctx.workspacePath);
348
+ return JSON.stringify(controller.getStatus(), null, 2);
349
+ },
350
+ async fw_weaver_swarm_config(args, ctx) {
351
+ const controller = SwarmController.getInstance(ctx.workspacePath);
352
+ controller.configure({
353
+ maxConcurrent: args.maxConcurrent,
354
+ workspaceBudgetTokens: args.workspaceBudgetTokens,
355
+ workspaceBudgetCost: args.workspaceBudgetCost,
356
+ sessionBudgetTokens: args.sessionBudgetTokens,
357
+ sessionBudgetCost: args.sessionBudgetCost,
358
+ autoRetry: args.autoRetry,
359
+ maxAttemptsDefault: args.maxAttemptsDefault,
360
+ });
361
+ return JSON.stringify(controller.getStatus(), null, 2);
362
+ },
363
+ async fw_weaver_swarm_events(args, ctx) {
364
+ const log = new SwarmEventLog(ctx.workspacePath);
365
+ const offset = args.offset ?? 0;
366
+ const events = log.tail(offset);
367
+ const controller = SwarmController.getInstance(ctx.workspacePath);
368
+ const done = controller.getStatus().status === 'idle';
369
+ return JSON.stringify({ events, done });
370
+ },
371
+ // ---------------------------------------------------------------------------
372
+ // Profile CRUD tools
373
+ // ---------------------------------------------------------------------------
374
+ async fw_weaver_profile_list(_args, ctx) {
375
+ const controller = SwarmController.getInstance(ctx.workspacePath);
376
+ const profileStore = controller.getProfileStore();
377
+ const instanceManager = controller.getInstanceManager();
378
+ const profiles = profileStore.list();
379
+ const enriched = profiles.map(p => ({
380
+ ...p,
381
+ activeInstances: instanceManager.listByProfile(p.id).filter(i => i.status === 'executing').length,
382
+ idleInstances: instanceManager.findIdle(p.id).length,
383
+ totalInstances: instanceManager.listByProfile(p.id).length,
384
+ }));
385
+ return JSON.stringify(enriched, null, 2);
386
+ },
387
+ async fw_weaver_profile_create(args, ctx) {
388
+ const controller = SwarmController.getInstance(ctx.workspacePath);
389
+ const profileStore = controller.getProfileStore();
390
+ const input = {
391
+ name: args.name,
392
+ botId: args.botId,
393
+ capabilities: args.capabilities,
394
+ preferences: {
395
+ costStrategy: args.costStrategy ?? 'balanced',
396
+ maxCostPerRun: args.maxCostPerRun,
397
+ maxCostPerTask: args.maxCostPerTask,
398
+ requireApproval: args.requireApproval,
399
+ instructions: args.instructions,
400
+ },
401
+ maxInstances: args.maxInstances,
402
+ description: args.description,
403
+ icon: args.icon,
404
+ color: args.color,
405
+ };
406
+ const profile = profileStore.create(input);
407
+ return JSON.stringify(profile, null, 2);
408
+ },
409
+ async fw_weaver_profile_update(args, ctx) {
410
+ const controller = SwarmController.getInstance(ctx.workspacePath);
411
+ const profileStore = controller.getProfileStore();
412
+ const { id, ...rest } = args;
413
+ // Build a typed patch from the remaining args
414
+ const patch = {};
415
+ if (rest.name !== undefined)
416
+ patch.name = rest.name;
417
+ if (rest.description !== undefined)
418
+ patch.description = rest.description;
419
+ if (rest.icon !== undefined)
420
+ patch.icon = rest.icon;
421
+ if (rest.color !== undefined)
422
+ patch.color = rest.color;
423
+ if (rest.capabilities !== undefined)
424
+ patch.capabilities = rest.capabilities;
425
+ if (rest.maxInstances !== undefined)
426
+ patch.maxInstances = rest.maxInstances;
427
+ if (rest.minInstances !== undefined)
428
+ patch.minInstances = rest.minInstances;
429
+ if (rest.costStrategy !== undefined || rest.maxCostPerRun !== undefined || rest.maxCostPerTask !== undefined || rest.requireApproval !== undefined || rest.instructions !== undefined) {
430
+ patch.preferences = {
431
+ ...(rest.costStrategy !== undefined && { costStrategy: rest.costStrategy }),
432
+ ...(rest.maxCostPerRun !== undefined && { maxCostPerRun: rest.maxCostPerRun }),
433
+ ...(rest.maxCostPerTask !== undefined && { maxCostPerTask: rest.maxCostPerTask }),
434
+ ...(rest.requireApproval !== undefined && { requireApproval: rest.requireApproval }),
435
+ ...(rest.instructions !== undefined && { instructions: rest.instructions }),
436
+ };
437
+ }
438
+ const profile = profileStore.update(id, patch);
439
+ return JSON.stringify(profile, null, 2);
440
+ },
441
+ async fw_weaver_profile_delete(args, ctx) {
442
+ const controller = SwarmController.getInstance(ctx.workspacePath);
443
+ const profileStore = controller.getProfileStore();
444
+ const deleted = profileStore.delete(args.id);
445
+ return JSON.stringify({ deleted });
446
+ },
447
+ // ---------------------------------------------------------------------------
448
+ // Orchestrator tools
449
+ // ---------------------------------------------------------------------------
450
+ async fw_weaver_orchestrator_status(_args, ctx) {
451
+ const controller = SwarmController.getInstance(ctx.workspacePath);
452
+ const { decisions, stats } = controller.getOrchestratorStatus();
453
+ const instanceManager = controller.getInstanceManager();
454
+ const allInstances = instanceManager.listAll();
455
+ return JSON.stringify({
456
+ recentDecisions: decisions.slice(-20),
457
+ routingStats: stats,
458
+ activeInstances: allInstances.filter(i => i.status === 'executing').length,
459
+ totalInstances: allInstances.length,
460
+ }, null, 2);
461
+ },
462
+ async fw_weaver_orchestrator_hint(args, ctx) {
463
+ const hint = args.hint;
464
+ if (!hint)
465
+ return JSON.stringify({ error: 'hint is required' });
466
+ const weaverDir = path.join(ctx.workspacePath, '.weaver');
467
+ fs.mkdirSync(weaverDir, { recursive: true });
468
+ const hintsPath = path.join(weaverDir, 'orchestrator-hints.json');
469
+ let hints = [];
470
+ try {
471
+ if (fs.existsSync(hintsPath)) {
472
+ const raw = fs.readFileSync(hintsPath, 'utf-8');
473
+ const parsed = JSON.parse(raw);
474
+ if (Array.isArray(parsed))
475
+ hints = parsed;
476
+ }
477
+ }
478
+ catch { /* corrupt file — start fresh */ }
479
+ hints.push({ hint, timestamp: Date.now() });
480
+ fs.writeFileSync(hintsPath, JSON.stringify(hints, null, 2), 'utf-8');
481
+ return JSON.stringify({ stored: true });
482
+ },
542
483
  };
543
484
  // ---------------------------------------------------------------------------
485
+ // Shared handler — used by both ai-chat-provider and mcp-tools
486
+ // ---------------------------------------------------------------------------
487
+ /**
488
+ * Execute a weaver tool by name. Shared core for ai-chat-provider and MCP.
489
+ *
490
+ * @param toolName One of the fw_weaver_* tool names
491
+ * @param args Tool arguments (as parsed from the caller)
492
+ * @param projectDir Workspace / project directory
493
+ * @returns The JSON-serialised result string
494
+ */
495
+ export async function handleWeaverTool(toolName, args, projectDir) {
496
+ const handler = toolHandlers[toolName];
497
+ if (!handler) {
498
+ return { result: `Unknown weaver tool: ${toolName}`, isError: true };
499
+ }
500
+ try {
501
+ const ctx = { workspacePath: projectDir, userId: 'mcp' };
502
+ const result = await handler(args, ctx);
503
+ return { result, isError: false };
504
+ }
505
+ catch (err) {
506
+ return { result: err instanceof Error ? err.message : String(err), isError: true };
507
+ }
508
+ }
509
+ // ---------------------------------------------------------------------------
544
510
  // Provider implementation
545
511
  // ---------------------------------------------------------------------------
546
512
  export default {
547
513
  async executeTool(name, args, context) {
548
- const handler = toolHandlers[name];
549
- if (!handler) {
550
- return { result: `Unknown weaver tool: ${name}`, isError: true };
551
- }
552
- try {
553
- const result = await handler(args, context);
554
- return { result, isError: false };
555
- }
556
- catch (err) {
557
- return { result: err instanceof Error ? err.message : String(err), isError: true };
558
- }
514
+ return handleWeaverTool(name, args, context.workspacePath);
559
515
  },
560
516
  async getSystemPromptSections(context) {
561
517
  const sections = [];
@@ -575,20 +531,35 @@ export default {
575
531
  catch {
576
532
  // system-prompt module not available — skip
577
533
  }
578
- // Weaver-specific capabilities beyond core Flow Weaver tools
534
+ // Weaver-specific capabilities swarm-aware tool listing
579
535
  sections.push({
580
536
  id: 'weaver-capabilities',
581
537
  title: 'Weaver Capabilities',
582
- content: `Beyond standard Flow Weaver tools, you also have:
538
+ content: `Beyond standard Flow Weaver tools, you have swarm-based task and bot management:
539
+
540
+ **Task Management:**
541
+ - \`fw_weaver_task_create\`: Create tasks (with optional subtasks via \`subtasks\` array, \`^prev\` shorthand for dependency chaining, \`assignedProfile\` to target specific profiles)
542
+ - \`fw_weaver_task_list\`: List tasks (filter by status, parentId, botId)
543
+ - \`fw_weaver_task_get\`: Get task details including subtasks and context
544
+ - \`fw_weaver_task_update\`: Update task fields (title, description, priority, status)
545
+ - \`fw_weaver_task_cancel\`: Cancel a pending or running task
546
+ - \`fw_weaver_task_retry\`: Reset a failed task back to pending for retry
583
547
 
584
- - \`fw_weaver_bot\`: Autonomous workflow creation/modification from natural language tasks (supports \`botId\` to run registered bots)
585
- - \`fw_weaver_steer\`: Control running bots (pause, resume, cancel, redirect)
586
- - \`fw_weaver_status\`: Current bot session state
587
- - \`fw_weaver_queue\`: Background task queue management
548
+ **Swarm Control:**
549
+ - \`fw_weaver_swarm_start\`: Start the swarm (spawns bot loops that claim and execute tasks)
550
+ - \`fw_weaver_swarm_pause\`: Pause all bot loops (tasks finish current step, then wait)
551
+ - \`fw_weaver_swarm_stop\`: Stop the swarm gracefully
552
+ - \`fw_weaver_swarm_status\`: Full swarm state with per-bot details
553
+ - \`fw_weaver_swarm_config\`: Configure max concurrency, budgets, auto-retry
554
+ - \`fw_weaver_swarm_events\`: Stream swarm-level events for live updates
555
+
556
+ **Bot & Workflow:**
557
+ - \`fw_weaver_steer\`: Send steering command to a specific bot (requires \`botId\`) — pause, resume, cancel
558
+ - \`fw_weaver_status\`: Quick swarm status summary
588
559
  - \`fw_weaver_run\`: Execute any workflow directly
589
560
  - \`fw_weaver_insights\`: Project health, trust level, cost summary, recommendations
590
- - \`fw_weaver_costs\`: Token usage and cost breakdown
591
- - \`fw_weaver_history\`: Past workflow execution history
561
+ - \`fw_weaver_costs\`: Token usage and cost breakdown (supports \`taskId\`/\`botId\` filters)
562
+ - \`fw_weaver_history\`: Past workflow execution history (supports \`taskId\`/\`botId\` filters)
592
563
  - \`fw_weaver_providers\`: Available AI providers
593
564
  - \`fw_weaver_genesis\`: Self-evolution cycle for workflows
594
565
  - \`fw_weaver_list_bots\`: List all registered bots with validation status
@@ -597,6 +568,16 @@ export default {
597
568
  - \`fw_weaver_validate_bot\`: Validate a bot's file and export
598
569
  - \`fw_weaver_eject_bot\`: Mark a bot as ejected with a new file path
599
570
 
571
+ **Profiles & Orchestrator:**
572
+ - \`fw_weaver_profile_list\`: List all bot profiles with capabilities, instances, and routing info
573
+ - \`fw_weaver_profile_create\`: Create a new bot profile with free-form capabilities (name + description pairs) and preferences (costStrategy, maxCostPerRun, maxCostPerTask, requireApproval, instructions)
574
+ - \`fw_weaver_profile_update\`: Update a bot profile's capabilities, preferences, or scaling config
575
+ - \`fw_weaver_profile_delete\`: Delete a bot profile
576
+ - \`fw_weaver_orchestrator_status\`: Get orchestrator routing status — recent decisions, stats, active instances
577
+ - \`fw_weaver_orchestrator_hint\`: Provide a routing hint to influence orchestrator decisions
578
+
579
+ **Workflow:** To delegate work, create tasks with \`fw_weaver_task_create\`, then start the swarm with \`fw_weaver_swarm_start\`. The orchestrator automatically routes tasks to the best available bot instance based on free-form capability matching. Use \`fw_weaver_steer\` with a \`botId\` to control individual bots. Use \`fw_weaver_profile_create\` to create specialized profiles with capabilities (name + description pairs) and preferences (costStrategy, maxCostPerRun, instructions), then route tasks via \`assignedProfile\`.
580
+
600
581
  Proactively offer these when relevant.`,
601
582
  priority: 15,
602
583
  });
@@ -641,7 +622,7 @@ Proactively offer these when relevant.`,
641
622
  // InsightEngine not available — skip
642
623
  }
643
624
  }
644
- // Registered bots
625
+ // Registered bots — swarm-aware: instruct AI to use task_create, not fw_weaver_bot
645
626
  if (context.workspacePath) {
646
627
  try {
647
628
  const registry = new BotRegistry(context.workspacePath);
@@ -651,13 +632,48 @@ Proactively offer these when relevant.`,
651
632
  sections.push({
652
633
  id: 'registered-bots',
653
634
  title: 'Registered Bots',
654
- content: `The user has registered bot workflows. To run a bot, use \`fw_weaver_bot\` with \`botId\` parameter.\n\n${botLines}`,
635
+ content: `The user has registered bot workflows available for swarm execution. To assign work, use \`fw_weaver_task_create\` with \`assignedProfile\` to route tasks to the right profile.\n\n${botLines}`,
655
636
  priority: 25,
656
637
  });
657
638
  }
658
639
  }
659
640
  catch { /* non-fatal */ }
660
641
  }
642
+ // Bot profiles — capabilities, scaling, routing info
643
+ if (context.workspacePath) {
644
+ try {
645
+ const controller = SwarmController.getInstance(context.workspacePath);
646
+ const profileStore = controller.getProfileStore();
647
+ const profiles = profileStore.list();
648
+ if (profiles.length > 0) {
649
+ const profileLines = profiles.map(p => `- ${p.name} (${p.id}): capabilities=[${p.capabilities.map(c => c.name).join(', ')}], maxInstances=${p.maxInstances}, costStrategy=${p.preferences.costStrategy}`).join('\n');
650
+ sections.push({
651
+ id: 'bot-profiles',
652
+ title: 'Bot Profiles',
653
+ content: `${profileLines}\n\nUse fw_weaver_profile_create to create new profiles. Use fw_weaver_task_create with assignedProfile to route tasks to the right profiles.`,
654
+ priority: 22,
655
+ });
656
+ }
657
+ }
658
+ catch { /* non-fatal */ }
659
+ }
660
+ // Swarm status — include if swarm is active
661
+ if (context.workspacePath) {
662
+ try {
663
+ const controller = SwarmController.getInstance(context.workspacePath);
664
+ const swarmState = controller.getStatus();
665
+ if (swarmState.status !== 'idle') {
666
+ const activeInstances = Object.values(swarmState.instances).filter(i => i.status === 'executing').length;
667
+ sections.push({
668
+ id: 'swarm-status',
669
+ title: 'Swarm Status',
670
+ content: `The swarm is currently **${swarmState.status}** with ${activeInstances} active instance(s) out of ${Object.keys(swarmState.instances).length} total. Tasks completed: ${swarmState.tasksCompleted}, failed: ${swarmState.tasksFailed}. Total cost: $${swarmState.totalCost.toFixed(4)}, tokens: ${swarmState.totalTokensUsed}.`,
671
+ priority: 12,
672
+ });
673
+ }
674
+ }
675
+ catch { /* non-fatal */ }
676
+ }
661
677
  return sections;
662
678
  },
663
679
  };