@synergenius/flow-weaver-pack-weaver 0.9.59 → 0.9.77

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/dist/ai-chat-provider.d.ts +12 -0
  2. package/dist/ai-chat-provider.d.ts.map +1 -1
  3. package/dist/ai-chat-provider.js +351 -335
  4. package/dist/ai-chat-provider.js.map +1 -1
  5. package/dist/bot/agent-loop.d.ts +20 -0
  6. package/dist/bot/agent-loop.d.ts.map +1 -0
  7. package/dist/bot/agent-loop.js +331 -0
  8. package/dist/bot/agent-loop.js.map +1 -0
  9. package/dist/bot/ai-router.d.ts +19 -0
  10. package/dist/bot/ai-router.d.ts.map +1 -0
  11. package/dist/bot/ai-router.js +104 -0
  12. package/dist/bot/ai-router.js.map +1 -0
  13. package/dist/bot/assistant-tools.d.ts.map +1 -1
  14. package/dist/bot/assistant-tools.js +49 -33
  15. package/dist/bot/assistant-tools.js.map +1 -1
  16. package/dist/bot/async-mutex.d.ts +13 -0
  17. package/dist/bot/async-mutex.d.ts.map +1 -0
  18. package/dist/bot/async-mutex.js +37 -0
  19. package/dist/bot/async-mutex.js.map +1 -0
  20. package/dist/bot/bot-manager.d.ts +2 -2
  21. package/dist/bot/bot-manager.d.ts.map +1 -1
  22. package/dist/bot/bot-manager.js +3 -3
  23. package/dist/bot/bot-manager.js.map +1 -1
  24. package/dist/bot/bot-registry.js +2 -2
  25. package/dist/bot/bot-registry.js.map +1 -1
  26. package/dist/bot/conversation-store.d.ts +1 -0
  27. package/dist/bot/conversation-store.d.ts.map +1 -1
  28. package/dist/bot/conversation-store.js.map +1 -1
  29. package/dist/bot/dashboard.d.ts.map +1 -1
  30. package/dist/bot/dashboard.js +17 -8
  31. package/dist/bot/dashboard.js.map +1 -1
  32. package/dist/bot/improve-loop.js.map +1 -1
  33. package/dist/bot/index.d.ts +2 -4
  34. package/dist/bot/index.d.ts.map +1 -1
  35. package/dist/bot/index.js +1 -2
  36. package/dist/bot/index.js.map +1 -1
  37. package/dist/bot/instance-manager.d.ts +31 -0
  38. package/dist/bot/instance-manager.d.ts.map +1 -0
  39. package/dist/bot/instance-manager.js +115 -0
  40. package/dist/bot/instance-manager.js.map +1 -0
  41. package/dist/bot/orchestrator.d.ts +36 -0
  42. package/dist/bot/orchestrator.d.ts.map +1 -0
  43. package/dist/bot/orchestrator.js +176 -0
  44. package/dist/bot/orchestrator.js.map +1 -0
  45. package/dist/bot/profile-store.d.ts +36 -0
  46. package/dist/bot/profile-store.d.ts.map +1 -0
  47. package/dist/bot/profile-store.js +208 -0
  48. package/dist/bot/profile-store.js.map +1 -0
  49. package/dist/bot/profile-types.d.ts +126 -0
  50. package/dist/bot/profile-types.d.ts.map +1 -0
  51. package/dist/bot/profile-types.js +7 -0
  52. package/dist/bot/profile-types.js.map +1 -0
  53. package/dist/bot/run-store.d.ts.map +1 -1
  54. package/dist/bot/run-store.js +8 -0
  55. package/dist/bot/run-store.js.map +1 -1
  56. package/dist/bot/runner.d.ts +4 -0
  57. package/dist/bot/runner.d.ts.map +1 -1
  58. package/dist/bot/runner.js +5 -1
  59. package/dist/bot/runner.js.map +1 -1
  60. package/dist/bot/swarm-controller.d.ts +109 -0
  61. package/dist/bot/swarm-controller.d.ts.map +1 -0
  62. package/dist/bot/swarm-controller.js +640 -0
  63. package/dist/bot/swarm-controller.js.map +1 -0
  64. package/dist/bot/swarm-event-log.d.ts +28 -0
  65. package/dist/bot/swarm-event-log.d.ts.map +1 -0
  66. package/dist/bot/swarm-event-log.js +54 -0
  67. package/dist/bot/swarm-event-log.js.map +1 -0
  68. package/dist/bot/task-prompt-builder.d.ts +22 -0
  69. package/dist/bot/task-prompt-builder.d.ts.map +1 -0
  70. package/dist/bot/task-prompt-builder.js +240 -0
  71. package/dist/bot/task-prompt-builder.js.map +1 -0
  72. package/dist/bot/task-store.d.ts +21 -0
  73. package/dist/bot/task-store.d.ts.map +1 -0
  74. package/dist/bot/task-store.js +364 -0
  75. package/dist/bot/task-store.js.map +1 -0
  76. package/dist/bot/task-types.d.ts +79 -0
  77. package/dist/bot/task-types.d.ts.map +1 -0
  78. package/dist/bot/task-types.js +6 -0
  79. package/dist/bot/task-types.js.map +1 -0
  80. package/dist/bot/types.d.ts +8 -0
  81. package/dist/bot/types.d.ts.map +1 -1
  82. package/dist/cli-handlers.d.ts.map +1 -1
  83. package/dist/cli-handlers.js +79 -54
  84. package/dist/cli-handlers.js.map +1 -1
  85. package/dist/cli.d.ts +3 -0
  86. package/dist/cli.d.ts.map +1 -0
  87. package/dist/cli.js +749 -0
  88. package/dist/cli.js.map +1 -0
  89. package/dist/docs/docs/weaver-bot-usage.md +35 -18
  90. package/dist/docs/docs/weaver-config.md +20 -0
  91. package/dist/docs/docs/weaver-task-queue.md +31 -19
  92. package/dist/docs/weaver-config.md +15 -9
  93. package/dist/index.d.ts +2 -2
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +1 -1
  96. package/dist/index.js.map +1 -1
  97. package/dist/mcp-tools.d.ts +17 -0
  98. package/dist/mcp-tools.d.ts.map +1 -1
  99. package/dist/mcp-tools.js +98 -279
  100. package/dist/mcp-tools.js.map +1 -1
  101. package/dist/node-types/bot-report.d.ts.map +1 -1
  102. package/dist/node-types/bot-report.js +6 -24
  103. package/dist/node-types/bot-report.js.map +1 -1
  104. package/dist/node-types/orchestrator-dispatch.d.ts +17 -0
  105. package/dist/node-types/orchestrator-dispatch.d.ts.map +1 -0
  106. package/dist/node-types/orchestrator-dispatch.js +63 -0
  107. package/dist/node-types/orchestrator-dispatch.js.map +1 -0
  108. package/dist/node-types/orchestrator-load-state.d.ts +16 -0
  109. package/dist/node-types/orchestrator-load-state.d.ts.map +1 -0
  110. package/dist/node-types/orchestrator-load-state.js +60 -0
  111. package/dist/node-types/orchestrator-load-state.js.map +1 -0
  112. package/dist/node-types/orchestrator-route.d.ts +16 -0
  113. package/dist/node-types/orchestrator-route.d.ts.map +1 -0
  114. package/dist/node-types/orchestrator-route.js +28 -0
  115. package/dist/node-types/orchestrator-route.js.map +1 -0
  116. package/dist/node-types/receive-task.d.ts +2 -3
  117. package/dist/node-types/receive-task.d.ts.map +1 -1
  118. package/dist/node-types/receive-task.js +3 -48
  119. package/dist/node-types/receive-task.js.map +1 -1
  120. package/dist/templates/weaver-template.d.ts +11 -0
  121. package/dist/templates/weaver-template.d.ts.map +1 -0
  122. package/dist/templates/weaver-template.js +53 -0
  123. package/dist/templates/weaver-template.js.map +1 -0
  124. package/dist/ui/bot-activity.js +2 -2
  125. package/dist/ui/bot-constants.d.ts +14 -0
  126. package/dist/ui/bot-constants.d.ts.map +1 -0
  127. package/dist/ui/bot-constants.js +189 -0
  128. package/dist/ui/bot-constants.js.map +1 -0
  129. package/dist/ui/bot-panel.js +207 -245
  130. package/dist/ui/bot-slot-card.js +141 -0
  131. package/dist/ui/budget-bar.js +59 -0
  132. package/dist/ui/chat-task-result.js +178 -0
  133. package/dist/ui/decision-log.js +136 -0
  134. package/dist/ui/profile-card.js +158 -0
  135. package/dist/ui/profile-editor.js +597 -0
  136. package/dist/ui/swarm-controls.js +245 -0
  137. package/dist/ui/swarm-dashboard.js +3012 -0
  138. package/dist/ui/task-create-form.js +98 -0
  139. package/dist/ui/task-detail-view.js +1044 -0
  140. package/dist/ui/task-pool-list.js +156 -0
  141. package/dist/workflows/orchestrator.d.ts +21 -0
  142. package/dist/workflows/orchestrator.d.ts.map +1 -0
  143. package/dist/workflows/orchestrator.js +281 -0
  144. package/dist/workflows/orchestrator.js.map +1 -0
  145. package/dist/workflows/weaver-bot-session.d.ts +65 -0
  146. package/dist/workflows/weaver-bot-session.d.ts.map +1 -0
  147. package/dist/workflows/weaver-bot-session.js +68 -0
  148. package/dist/workflows/weaver-bot-session.js.map +1 -0
  149. package/dist/workflows/weaver.d.ts +24 -0
  150. package/dist/workflows/weaver.d.ts.map +1 -0
  151. package/dist/workflows/weaver.js +28 -0
  152. package/dist/workflows/weaver.js.map +1 -0
  153. package/flowweaver.manifest.json +547 -133
  154. package/package.json +1 -1
  155. package/src/ai-chat-provider.ts +378 -371
  156. package/src/bot/ai-router.ts +132 -0
  157. package/src/bot/assistant-tools.ts +47 -29
  158. package/src/bot/async-mutex.ts +37 -0
  159. package/src/bot/bot-manager.ts +3 -3
  160. package/src/bot/bot-registry.ts +2 -2
  161. package/src/bot/conversation-store.ts +2 -1
  162. package/src/bot/dashboard.ts +17 -8
  163. package/src/bot/improve-loop.ts +6 -6
  164. package/src/bot/index.ts +2 -4
  165. package/src/bot/instance-manager.ts +128 -0
  166. package/src/bot/orchestrator.ts +244 -0
  167. package/src/bot/profile-store.ts +225 -0
  168. package/src/bot/profile-types.ts +141 -0
  169. package/src/bot/run-store.ts +8 -0
  170. package/src/bot/runner.ts +9 -1
  171. package/src/bot/swarm-controller.ts +780 -0
  172. package/src/bot/swarm-event-log.ts +57 -0
  173. package/src/bot/task-prompt-builder.ts +309 -0
  174. package/src/bot/task-store.ts +407 -0
  175. package/src/bot/task-types.ts +100 -0
  176. package/src/bot/types.ts +8 -0
  177. package/src/cli-handlers.ts +78 -53
  178. package/src/docs/weaver-bot-usage.md +35 -18
  179. package/src/docs/weaver-config.md +20 -0
  180. package/src/docs/weaver-task-queue.md +31 -19
  181. package/src/index.ts +5 -4
  182. package/src/mcp-tools.ts +129 -372
  183. package/src/node-types/bot-report.ts +6 -24
  184. package/src/node-types/orchestrator-dispatch.ts +71 -0
  185. package/src/node-types/orchestrator-load-state.ts +66 -0
  186. package/src/node-types/orchestrator-route.ts +33 -0
  187. package/src/node-types/receive-task.ts +3 -57
  188. package/src/ui/bot-activity.tsx +2 -2
  189. package/src/ui/bot-constants.ts +192 -0
  190. package/src/ui/bot-panel.tsx +213 -247
  191. package/src/ui/bot-slot-card.tsx +139 -0
  192. package/src/ui/budget-bar.tsx +30 -0
  193. package/src/ui/chat-task-result.tsx +236 -0
  194. package/src/ui/decision-log.tsx +148 -0
  195. package/src/ui/profile-card.tsx +157 -0
  196. package/src/ui/profile-editor.tsx +384 -0
  197. package/src/ui/swarm-controls.tsx +260 -0
  198. package/src/ui/swarm-dashboard.tsx +647 -0
  199. package/src/ui/task-create-form.tsx +87 -0
  200. package/src/ui/task-detail-view.tsx +841 -0
  201. package/src/ui/task-pool-list.tsx +187 -0
  202. package/src/workflows/orchestrator.ts +302 -0
  203. package/dist/docs/weaver-bot-usage.md +0 -34
  204. package/dist/docs/weaver-genesis.md +0 -32
  205. package/dist/docs/weaver-task-queue.md +0 -34
  206. package/dist/ui/bot-workspace.js +0 -1015
  207. package/dist/ui/chat-bot-result.js +0 -71
  208. package/dist/ui/queue-input.js +0 -82
  209. package/dist/ui/session-bar.js +0 -174
  210. package/src/bot/error-guide.ts +0 -4
  211. package/src/bot/retry-utils.ts +0 -4
  212. package/src/bot/session-state.ts +0 -116
  213. package/src/bot/task-queue.ts +0 -262
  214. package/src/ui/bot-workspace.tsx +0 -442
  215. package/src/ui/chat-bot-result.tsx +0 -81
  216. package/src/ui/queue-input.tsx +0 -56
  217. package/src/ui/session-bar.tsx +0 -157
@@ -0,0 +1,407 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import * as crypto from 'node:crypto';
4
+ import { AsyncMutex } from './async-mutex.js';
5
+ import type { Task, TaskFilter, CreateTaskInput, RunSummary } from './task-types.js';
6
+
7
+ /** Don't re-queue tasks completed/failed within this window (ms). */
8
+ const DEDUP_WINDOW_MS = 3_600_000; // 1 hour
9
+
10
+ export class TaskStore {
11
+ private readonly filePath: string;
12
+ private readonly mutex = new AsyncMutex();
13
+
14
+ constructor(private readonly projectDir: string) {
15
+ const dir = path.join(projectDir, '.weaver');
16
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
17
+ this.filePath = path.join(dir, 'tasks.json');
18
+ }
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // CRUD
22
+ // ---------------------------------------------------------------------------
23
+
24
+ async create(input: CreateTaskInput): Promise<Task> {
25
+ return this.mutex.runExclusive(async () => {
26
+ const tasks = this._readAll();
27
+
28
+ // --- Subtask creation ---
29
+ if (input.subtasks && input.subtasks.length > 0) {
30
+ // Check dedup for the parent
31
+ const existing = this._findDuplicate(tasks, input.title, input.description);
32
+ if (existing) return existing;
33
+
34
+ const parentId = crypto.randomUUID().slice(0, 8);
35
+ const now = new Date().toISOString();
36
+
37
+ const parent: Task = {
38
+ id: parentId,
39
+ title: input.title,
40
+ description: input.description,
41
+ status: 'pending',
42
+ priority: input.priority ?? 0,
43
+ isParent: true,
44
+ parentId: input.parentId,
45
+ dependsOn: input.dependsOn ?? [],
46
+ currentBotId: undefined,
47
+ currentRunId: undefined,
48
+ context: { files: [], notes: '', runSummaries: [] },
49
+ runs: [],
50
+ attempt: 0,
51
+ maxAttempts: input.maxAttempts ?? 3,
52
+ budgetTokens: input.budgetTokens,
53
+ budgetCost: input.budgetCost,
54
+ tokensUsed: 0,
55
+ costUsed: 0,
56
+ timeoutMs: input.timeoutMs,
57
+ complexity: input.complexity,
58
+ assignedProfile: input.assignedProfile,
59
+ createdBy: input.createdBy ?? 'user',
60
+ createdAt: now,
61
+ updatedAt: now,
62
+ };
63
+ tasks.push(parent);
64
+
65
+ // Create subtasks with ^prev resolution
66
+ let prevSubtaskId: string | undefined;
67
+ for (const sub of input.subtasks) {
68
+ const subId = crypto.randomUUID().slice(0, 8);
69
+ const resolvedDeps = (sub.dependsOn ?? [])
70
+ .map(dep => {
71
+ if (dep === '^prev') return prevSubtaskId; // undefined if first
72
+ return dep;
73
+ })
74
+ .filter((d): d is string => d !== undefined);
75
+
76
+ const subtask: Task = {
77
+ id: subId,
78
+ title: sub.title,
79
+ description: sub.description ?? '',
80
+ status: 'pending',
81
+ priority: sub.priority ?? input.priority ?? 0,
82
+ isParent: false,
83
+ parentId: parentId,
84
+ dependsOn: resolvedDeps,
85
+ currentBotId: undefined,
86
+ currentRunId: undefined,
87
+ context: { files: [], notes: '', runSummaries: [] },
88
+ runs: [],
89
+ attempt: 0,
90
+ maxAttempts: input.maxAttempts ?? 3,
91
+ budgetTokens: input.budgetTokens,
92
+ budgetCost: input.budgetCost,
93
+ tokensUsed: 0,
94
+ costUsed: 0,
95
+ complexity: sub.complexity ?? input.complexity,
96
+ assignedProfile: sub.assignedProfile ?? input.assignedProfile,
97
+ createdBy: input.createdBy ?? 'user',
98
+ createdAt: now,
99
+ updatedAt: now,
100
+ };
101
+ tasks.push(subtask);
102
+ prevSubtaskId = subId;
103
+ }
104
+
105
+ this._writeAll(tasks);
106
+ return parent;
107
+ }
108
+
109
+ // --- Single task creation ---
110
+ const existing = this._findDuplicate(tasks, input.title, input.description);
111
+ if (existing) return existing;
112
+
113
+ const now = new Date().toISOString();
114
+ const task: Task = {
115
+ id: crypto.randomUUID().slice(0, 8),
116
+ title: input.title,
117
+ description: input.description,
118
+ status: 'pending',
119
+ priority: input.priority ?? 0,
120
+ isParent: false,
121
+ parentId: input.parentId,
122
+ dependsOn: input.dependsOn ?? [],
123
+ currentBotId: undefined,
124
+ currentRunId: undefined,
125
+ context: { files: [], notes: '', runSummaries: [] },
126
+ runs: [],
127
+ attempt: 0,
128
+ maxAttempts: input.maxAttempts ?? 3,
129
+ budgetTokens: input.budgetTokens,
130
+ budgetCost: input.budgetCost,
131
+ tokensUsed: 0,
132
+ costUsed: 0,
133
+ timeoutMs: input.timeoutMs,
134
+ complexity: input.complexity,
135
+ assignedProfile: input.assignedProfile,
136
+ createdBy: input.createdBy ?? 'user',
137
+ createdAt: now,
138
+ updatedAt: now,
139
+ };
140
+ tasks.push(task);
141
+ this._writeAll(tasks);
142
+ return task;
143
+ });
144
+ }
145
+
146
+ async get(id: string): Promise<Task | null> {
147
+ return this.mutex.runExclusive(async () => {
148
+ const tasks = this._readAll();
149
+ return tasks.find(t => t.id === id) ?? null;
150
+ });
151
+ }
152
+
153
+ async list(filter?: TaskFilter): Promise<Task[]> {
154
+ return this.mutex.runExclusive(async () => {
155
+ let tasks = this._readAll();
156
+
157
+ if (filter) {
158
+ if (filter.status !== undefined) {
159
+ const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];
160
+ tasks = tasks.filter(t => statuses.includes(t.status));
161
+ }
162
+ if (filter.parentId !== undefined) {
163
+ tasks = tasks.filter(t => t.parentId === filter.parentId);
164
+ }
165
+ if (filter.botId !== undefined) {
166
+ tasks = tasks.filter(t => t.currentBotId === filter.botId);
167
+ }
168
+ if (filter.limit !== undefined && filter.limit > 0) {
169
+ tasks = tasks.slice(0, filter.limit);
170
+ }
171
+ }
172
+
173
+ return tasks;
174
+ });
175
+ }
176
+
177
+ async update(id: string, patch: Partial<Task>): Promise<Task> {
178
+ return this.mutex.runExclusive(async () => {
179
+ const tasks = this._readAll();
180
+ const idx = tasks.findIndex(t => t.id === id);
181
+ if (idx === -1) throw new Error(`Task not found: ${id}`);
182
+
183
+ const task = tasks[idx];
184
+ Object.assign(task, patch, { updatedAt: new Date().toISOString() });
185
+ tasks[idx] = task;
186
+ this._writeAll(tasks);
187
+ return task;
188
+ });
189
+ }
190
+
191
+ async updateContext(id: string, runSummary: RunSummary): Promise<Task> {
192
+ return this.mutex.runExclusive(async () => {
193
+ const tasks = this._readAll();
194
+ const idx = tasks.findIndex(t => t.id === id);
195
+ if (idx === -1) throw new Error(`Task not found: ${id}`);
196
+
197
+ const task = tasks[idx];
198
+ task.context.runSummaries.push(runSummary);
199
+
200
+ if (runSummary.outcome === 'failed' || runSummary.outcome === 'error') {
201
+ task.context.lastError = runSummary.error;
202
+ } else {
203
+ task.context.lastError = undefined;
204
+ }
205
+
206
+ task.updatedAt = new Date().toISOString();
207
+ tasks[idx] = task;
208
+ this._writeAll(tasks);
209
+ return task;
210
+ });
211
+ }
212
+
213
+ // ---------------------------------------------------------------------------
214
+ // Subtasks
215
+ // ---------------------------------------------------------------------------
216
+
217
+ async getSubtasks(parentId: string): Promise<Task[]> {
218
+ return this.mutex.runExclusive(async () => {
219
+ const tasks = this._readAll();
220
+ return tasks.filter(t => t.parentId === parentId);
221
+ });
222
+ }
223
+
224
+ // ---------------------------------------------------------------------------
225
+ // Push-based assignment (orchestrator)
226
+ // ---------------------------------------------------------------------------
227
+
228
+ async assignToInstance(taskId: string, instanceId: string, profileId: string): Promise<Task> {
229
+ return this.mutex.runExclusive(async () => {
230
+ const tasks = this._readAll();
231
+ const idx = tasks.findIndex(t => t.id === taskId);
232
+ if (idx === -1) throw new Error(`Task not found: ${taskId}`);
233
+
234
+ const task = tasks[idx];
235
+ const assignable =
236
+ task.status === 'pending' ||
237
+ (task.status === 'failed' && task.attempt < task.maxAttempts);
238
+ if (!assignable) {
239
+ throw new Error(`Task ${taskId} is not assignable (status: ${task.status}, attempt: ${task.attempt}/${task.maxAttempts})`);
240
+ }
241
+
242
+ task.status = 'in-progress';
243
+ task.currentBotId = instanceId;
244
+ task.assignedProfile = profileId;
245
+ task.updatedAt = new Date().toISOString();
246
+
247
+ tasks[idx] = task;
248
+ this._writeAll(tasks);
249
+ return task;
250
+ });
251
+ }
252
+
253
+ // ---------------------------------------------------------------------------
254
+ // Release
255
+ // ---------------------------------------------------------------------------
256
+
257
+ async release(taskId: string, status: 'done' | 'failed', runSummary: RunSummary): Promise<Task> {
258
+ return this.mutex.runExclusive(async () => {
259
+ const tasks = this._readAll();
260
+ const idx = tasks.findIndex(t => t.id === taskId);
261
+ if (idx === -1) throw new Error(`Task not found: ${taskId}`);
262
+
263
+ const task = tasks[idx];
264
+
265
+ // Append run summary
266
+ task.context.runSummaries.push(runSummary);
267
+ if (runSummary.outcome === 'failed' || runSummary.outcome === 'error') {
268
+ task.context.lastError = runSummary.error;
269
+ } else {
270
+ task.context.lastError = undefined;
271
+ }
272
+
273
+ // Accumulate usage
274
+ task.tokensUsed += runSummary.tokensUsed;
275
+ task.costUsed += runSummary.cost;
276
+
277
+ // Update execution tracking
278
+ task.attempt += 1;
279
+ task.status = status;
280
+ task.currentBotId = undefined;
281
+ task.currentRunId = undefined;
282
+ task.updatedAt = new Date().toISOString();
283
+
284
+ if (status === 'done') {
285
+ task.completedAt = new Date().toISOString();
286
+ }
287
+ if (status === 'failed') {
288
+ task.completedAt = new Date().toISOString();
289
+ }
290
+
291
+ tasks[idx] = task;
292
+
293
+ // --- Post-release effects ---
294
+ this._handleDependencyEffects(tasks, task);
295
+ this._handleParentEffects(tasks, task);
296
+
297
+ this._writeAll(tasks);
298
+ return task;
299
+ });
300
+ }
301
+
302
+ // ---------------------------------------------------------------------------
303
+ // Dependency + parent cascading effects
304
+ // ---------------------------------------------------------------------------
305
+
306
+ private _handleDependencyEffects(tasks: Task[], changedTask: Task): void {
307
+ if (changedTask.status === 'done') {
308
+ // Unblock dependents: set blocked tasks to pending if all deps are now done
309
+ const doneIds = new Set(tasks.filter(t => t.status === 'done').map(t => t.id));
310
+ for (const t of tasks) {
311
+ if (t.status === 'blocked' && t.dependsOn.includes(changedTask.id)) {
312
+ if (t.dependsOn.every(depId => doneIds.has(depId))) {
313
+ t.status = 'pending';
314
+ t.updatedAt = new Date().toISOString();
315
+ }
316
+ }
317
+ }
318
+ } else if (changedTask.status === 'failed') {
319
+ // Check if failure is terminal (no retries left)
320
+ if (changedTask.attempt >= changedTask.maxAttempts) {
321
+ // Block dependents
322
+ for (const t of tasks) {
323
+ if (t.status === 'pending' && t.dependsOn.includes(changedTask.id)) {
324
+ t.status = 'blocked';
325
+ t.updatedAt = new Date().toISOString();
326
+ }
327
+ }
328
+ }
329
+ }
330
+ }
331
+
332
+ private _handleParentEffects(tasks: Task[], changedTask: Task): void {
333
+ if (!changedTask.parentId) return;
334
+
335
+ const parentIdx = tasks.findIndex(t => t.id === changedTask.parentId);
336
+ if (parentIdx === -1) return;
337
+
338
+ const parent = tasks[parentIdx];
339
+ if (!parent.isParent) return;
340
+
341
+ const subtasks = tasks.filter(t => t.parentId === parent.id);
342
+
343
+ // All done => parent done
344
+ if (subtasks.every(s => s.status === 'done')) {
345
+ parent.status = 'done';
346
+ parent.completedAt = new Date().toISOString();
347
+ parent.updatedAt = new Date().toISOString();
348
+ return;
349
+ }
350
+
351
+ // Any terminal failure => parent failed
352
+ const hasTerminalFailure = subtasks.some(
353
+ s => s.status === 'failed' && s.attempt >= s.maxAttempts,
354
+ );
355
+ if (hasTerminalFailure) {
356
+ parent.status = 'failed';
357
+ parent.updatedAt = new Date().toISOString();
358
+ }
359
+ }
360
+
361
+ // ---------------------------------------------------------------------------
362
+ // Deduplication
363
+ // ---------------------------------------------------------------------------
364
+
365
+ private _findDuplicate(tasks: Task[], title: string, description: string): Task | null {
366
+ // Check pending duplicates
367
+ const pendingDup = tasks.find(
368
+ t => t.status === 'pending' && t.title === title && t.description === description,
369
+ );
370
+ if (pendingDup) return pendingDup;
371
+
372
+ // Check recently completed/failed (within dedup window)
373
+ const now = Date.now();
374
+ const recentDup = tasks.find(
375
+ t =>
376
+ (t.status === 'done' || t.status === 'failed') &&
377
+ t.title === title &&
378
+ t.description === description &&
379
+ t.completedAt !== undefined &&
380
+ now - Date.parse(t.completedAt) < DEDUP_WINDOW_MS,
381
+ );
382
+ if (recentDup) return recentDup;
383
+
384
+ return null;
385
+ }
386
+
387
+ // ---------------------------------------------------------------------------
388
+ // File I/O — atomic write via temp + rename
389
+ // ---------------------------------------------------------------------------
390
+
391
+ private _readAll(): Task[] {
392
+ if (!fs.existsSync(this.filePath)) return [];
393
+ try {
394
+ const raw = fs.readFileSync(this.filePath, 'utf-8');
395
+ const data = JSON.parse(raw);
396
+ return Array.isArray(data) ? data : [];
397
+ } catch {
398
+ return [];
399
+ }
400
+ }
401
+
402
+ private _writeAll(tasks: Task[]): void {
403
+ const tmpPath = this.filePath + '.tmp';
404
+ fs.writeFileSync(tmpPath, JSON.stringify(tasks, null, 2), 'utf-8');
405
+ fs.renameSync(tmpPath, this.filePath);
406
+ }
407
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Task entity types for the Weaver Swarm.
3
+ * Tasks are first-class entities with hierarchy, context, and budget.
4
+ */
5
+
6
+ export interface RunSummary {
7
+ runId: string;
8
+ botId: string;
9
+ outcome: 'success' | 'failed' | 'error';
10
+ summary: string;
11
+ filesModified: string[];
12
+ error?: string;
13
+ durationMs: number;
14
+ tokensUsed: number;
15
+ cost: number;
16
+ }
17
+
18
+ export interface TaskContext {
19
+ files: string[];
20
+ notes: string;
21
+ runSummaries: RunSummary[];
22
+ lastError?: string;
23
+ }
24
+
25
+ export type TaskStatus = 'pending' | 'in-progress' | 'blocked' | 'done' | 'failed' | 'cancelled';
26
+
27
+ export interface Task {
28
+ id: string;
29
+ title: string;
30
+ description: string;
31
+ status: TaskStatus;
32
+ priority: number; // higher = higher priority
33
+
34
+ // Hierarchy
35
+ isParent: boolean;
36
+ parentId?: string;
37
+ dependsOn: string[];
38
+
39
+ // Assignment
40
+ currentBotId?: string;
41
+ currentRunId?: string;
42
+
43
+ // Context
44
+ context: TaskContext;
45
+
46
+ // Execution
47
+ runs: string[];
48
+ attempt: number;
49
+ maxAttempts: number;
50
+
51
+ // Budget
52
+ budgetTokens?: number;
53
+ budgetCost?: number;
54
+ tokensUsed: number;
55
+ costUsed: number;
56
+
57
+ // Timeout
58
+ timeoutMs?: number;
59
+
60
+ // Orchestrator routing
61
+ assignedProfile?: string; // profile ID assigned by orchestrator
62
+ routingReason?: string; // why this profile was chosen
63
+ complexity?: 'trivial' | 'simple' | 'moderate' | 'complex'; // task complexity estimate
64
+
65
+ // Metadata
66
+ createdBy: 'user' | 'ai';
67
+ createdAt: string;
68
+ updatedAt: string;
69
+ completedAt?: string;
70
+ }
71
+
72
+ export interface TaskFilter {
73
+ status?: TaskStatus | TaskStatus[];
74
+ parentId?: string;
75
+ botId?: string;
76
+ limit?: number;
77
+ }
78
+
79
+ export interface CreateTaskInput {
80
+ title: string;
81
+ description: string;
82
+ priority?: number;
83
+ parentId?: string;
84
+ dependsOn?: string[];
85
+ budgetTokens?: number;
86
+ budgetCost?: number;
87
+ timeoutMs?: number;
88
+ maxAttempts?: number;
89
+ createdBy?: 'user' | 'ai';
90
+ complexity?: 'trivial' | 'simple' | 'moderate' | 'complex';
91
+ assignedProfile?: string;
92
+ subtasks?: Array<{
93
+ title: string;
94
+ description?: string;
95
+ dependsOn?: string[];
96
+ priority?: number;
97
+ complexity?: 'trivial' | 'simple' | 'moderate' | 'complex';
98
+ assignedProfile?: string;
99
+ }>;
100
+ }
package/src/bot/types.ts CHANGED
@@ -205,6 +205,10 @@ export interface RunRecord {
205
205
  cost?: RunCostSummary;
206
206
  /** Lifecycle audit events (plan-created, approval, step-start/complete, etc.). */
207
207
  auditTrail?: AuditEvent[];
208
+ /** Swarm task this run belongs to. */
209
+ taskId?: string;
210
+ /** Bot slot that executed this run. */
211
+ botId?: string;
208
212
  }
209
213
 
210
214
  export interface RunFilter {
@@ -214,6 +218,10 @@ export interface RunFilter {
214
218
  since?: string;
215
219
  before?: string;
216
220
  limit?: number;
221
+ /** Filter runs belonging to a specific swarm task. */
222
+ taskId?: string;
223
+ /** Filter runs executed by a specific bot slot. */
224
+ botId?: string;
217
225
  }
218
226
 
219
227
  export interface RetentionPolicy {