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