@synergenius/flow-weaver-pack-weaver 0.9.159 → 0.9.164
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-chat-provider.d.ts.map +1 -1
- package/dist/ai-chat-provider.js +17 -11
- package/dist/ai-chat-provider.js.map +1 -1
- package/dist/bot/ai-router.js +5 -5
- package/dist/bot/ai-router.js.map +1 -1
- package/dist/bot/assistant-tools.d.ts.map +1 -1
- package/dist/bot/assistant-tools.js +6 -7
- package/dist/bot/assistant-tools.js.map +1 -1
- package/dist/bot/capability-registry.d.ts.map +1 -1
- package/dist/bot/capability-registry.js +37 -14
- package/dist/bot/capability-registry.js.map +1 -1
- package/dist/bot/dashboard.js +1 -1
- package/dist/bot/dashboard.js.map +1 -1
- package/dist/bot/index.d.ts +1 -1
- package/dist/bot/index.d.ts.map +1 -1
- package/dist/bot/index.js.map +1 -1
- package/dist/bot/instance-manager.js +3 -3
- package/dist/bot/instance-manager.js.map +1 -1
- package/dist/bot/profile-store.d.ts.map +1 -1
- package/dist/bot/profile-store.js +11 -9
- package/dist/bot/profile-store.js.map +1 -1
- package/dist/bot/profile-types.d.ts +2 -2
- package/dist/bot/profile-types.d.ts.map +1 -1
- package/dist/bot/runner.d.ts +1 -0
- package/dist/bot/runner.d.ts.map +1 -1
- package/dist/bot/runner.js +6 -2
- package/dist/bot/runner.js.map +1 -1
- package/dist/bot/step-executor.d.ts.map +1 -1
- package/dist/bot/step-executor.js +10 -0
- package/dist/bot/step-executor.js.map +1 -1
- package/dist/bot/swarm-controller.d.ts +3 -5
- package/dist/bot/swarm-controller.d.ts.map +1 -1
- package/dist/bot/swarm-controller.js +157 -74
- package/dist/bot/swarm-controller.js.map +1 -1
- package/dist/bot/task-prompt-builder.d.ts +2 -3
- package/dist/bot/task-prompt-builder.d.ts.map +1 -1
- package/dist/bot/task-prompt-builder.js +81 -67
- package/dist/bot/task-prompt-builder.js.map +1 -1
- package/dist/bot/task-store.d.ts +3 -3
- package/dist/bot/task-store.d.ts.map +1 -1
- package/dist/bot/task-store.js +89 -75
- package/dist/bot/task-store.js.map +1 -1
- package/dist/bot/task-types.d.ts +54 -26
- package/dist/bot/task-types.d.ts.map +1 -1
- package/dist/bot/task-types.js +6 -2
- package/dist/bot/task-types.js.map +1 -1
- package/dist/bot/types.d.ts +2 -0
- package/dist/bot/types.d.ts.map +1 -1
- package/dist/bot/weaver-tools.d.ts.map +1 -1
- package/dist/bot/weaver-tools.js +10 -0
- package/dist/bot/weaver-tools.js.map +1 -1
- package/dist/cli-handlers.d.ts +0 -1
- package/dist/cli-handlers.d.ts.map +1 -1
- package/dist/cli-handlers.js +5 -9
- package/dist/cli-handlers.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/node-types/agent-execute.d.ts.map +1 -1
- package/dist/node-types/agent-execute.js +95 -63
- package/dist/node-types/agent-execute.js.map +1 -1
- package/dist/node-types/plan-task.js +8 -8
- package/dist/node-types/plan-task.js.map +1 -1
- package/dist/ui/bot-panel.js +1 -1
- package/dist/ui/capability-editor.js +37 -14
- package/dist/ui/chat-task-result.js +1 -7
- package/dist/ui/profile-editor.js +37 -14
- package/dist/ui/swarm-controls.js +2 -2
- package/dist/ui/swarm-dashboard.js +72 -109
- package/dist/ui/task-detail-view.js +21 -42
- package/dist/ui/task-editor.js +13 -50
- package/dist/ui/task-pool-list.js +0 -2
- package/flowweaver.manifest.json +1 -1
- package/package.json +1 -1
- package/src/ai-chat-provider.ts +15 -11
- package/src/bot/ai-router.ts +5 -5
- package/src/bot/assistant-tools.ts +6 -7
- package/src/bot/capability-registry.ts +37 -14
- package/src/bot/dashboard.ts +1 -1
- package/src/bot/index.ts +5 -1
- package/src/bot/instance-manager.ts +3 -3
- package/src/bot/profile-store.ts +12 -10
- package/src/bot/profile-types.ts +2 -2
- package/src/bot/runner.ts +6 -2
- package/src/bot/step-executor.ts +11 -0
- package/src/bot/swarm-controller.ts +164 -78
- package/src/bot/task-prompt-builder.ts +86 -71
- package/src/bot/task-store.ts +101 -78
- package/src/bot/task-types.ts +81 -36
- package/src/bot/types.ts +2 -0
- package/src/bot/weaver-tools.ts +11 -0
- package/src/cli-handlers.ts +5 -9
- package/src/index.ts +6 -0
- package/src/node-types/agent-execute.ts +99 -62
- package/src/node-types/plan-task.ts +8 -8
- package/src/ui/bot-panel.tsx +3 -3
- package/src/ui/chat-task-result.tsx +5 -14
- package/src/ui/swarm-controls.tsx +3 -3
- package/src/ui/swarm-dashboard.tsx +3 -3
- package/src/ui/task-detail-view.tsx +29 -52
- package/src/ui/task-editor.tsx +14 -51
- package/src/ui/task-pool-list.tsx +1 -3
package/src/bot/task-store.ts
CHANGED
|
@@ -2,9 +2,9 @@ import * as fs from 'node:fs';
|
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import * as crypto from 'node:crypto';
|
|
4
4
|
import { AsyncMutex } from './async-mutex.js';
|
|
5
|
-
import type { Task, TaskFilter, CreateTaskInput,
|
|
5
|
+
import type { Task, TaskFilter, CreateTaskInput, RunProgress } from './task-types.js';
|
|
6
6
|
|
|
7
|
-
/** Don't re-queue tasks completed
|
|
7
|
+
/** Don't re-queue tasks completed within this window (ms). */
|
|
8
8
|
const DEDUP_WINDOW_MS = 3_600_000; // 1 hour
|
|
9
9
|
|
|
10
10
|
export class TaskStore {
|
|
@@ -43,17 +43,11 @@ export class TaskStore {
|
|
|
43
43
|
isParent: true,
|
|
44
44
|
parentId: input.parentId,
|
|
45
45
|
dependsOn: input.dependsOn ?? [],
|
|
46
|
-
|
|
47
|
-
currentRunId: undefined,
|
|
48
|
-
context: { files: [], notes: '', runSummaries: [] },
|
|
49
|
-
runs: [],
|
|
50
|
-
attempt: 0,
|
|
51
|
-
maxAttempts: input.maxAttempts ?? 3,
|
|
52
|
-
budgetTokens: input.budgetTokens,
|
|
46
|
+
context: { files: [], notes: '', runHistory: [], stagnationCount: 0 },
|
|
53
47
|
budgetCost: input.budgetCost,
|
|
54
48
|
tokensUsed: 0,
|
|
55
49
|
costUsed: 0,
|
|
56
|
-
|
|
50
|
+
acceptance: input.acceptance,
|
|
57
51
|
complexity: input.complexity,
|
|
58
52
|
assignedProfile: input.assignedProfile,
|
|
59
53
|
createdBy: input.createdBy ?? 'user',
|
|
@@ -82,16 +76,11 @@ export class TaskStore {
|
|
|
82
76
|
isParent: false,
|
|
83
77
|
parentId: parentId,
|
|
84
78
|
dependsOn: resolvedDeps,
|
|
85
|
-
|
|
86
|
-
currentRunId: undefined,
|
|
87
|
-
context: { files: [], notes: '', runSummaries: [] },
|
|
88
|
-
runs: [],
|
|
89
|
-
attempt: 0,
|
|
90
|
-
maxAttempts: input.maxAttempts ?? 3,
|
|
91
|
-
budgetTokens: input.budgetTokens,
|
|
79
|
+
context: { files: [], notes: '', runHistory: [], stagnationCount: 0 },
|
|
92
80
|
budgetCost: input.budgetCost,
|
|
93
81
|
tokensUsed: 0,
|
|
94
82
|
costUsed: 0,
|
|
83
|
+
acceptance: sub.acceptance ?? input.acceptance,
|
|
95
84
|
complexity: sub.complexity ?? input.complexity,
|
|
96
85
|
assignedProfile: sub.assignedProfile ?? input.assignedProfile,
|
|
97
86
|
createdBy: input.createdBy ?? 'user',
|
|
@@ -120,17 +109,11 @@ export class TaskStore {
|
|
|
120
109
|
isParent: false,
|
|
121
110
|
parentId: input.parentId,
|
|
122
111
|
dependsOn: input.dependsOn ?? [],
|
|
123
|
-
|
|
124
|
-
currentRunId: undefined,
|
|
125
|
-
context: { files: [], notes: '', runSummaries: [] },
|
|
126
|
-
runs: [],
|
|
127
|
-
attempt: 0,
|
|
128
|
-
maxAttempts: input.maxAttempts ?? 3,
|
|
129
|
-
budgetTokens: input.budgetTokens,
|
|
112
|
+
context: { files: [], notes: '', runHistory: [], stagnationCount: 0 },
|
|
130
113
|
budgetCost: input.budgetCost,
|
|
131
114
|
tokensUsed: 0,
|
|
132
115
|
costUsed: 0,
|
|
133
|
-
|
|
116
|
+
acceptance: input.acceptance,
|
|
134
117
|
complexity: input.complexity,
|
|
135
118
|
assignedProfile: input.assignedProfile,
|
|
136
119
|
createdBy: input.createdBy ?? 'user',
|
|
@@ -163,7 +146,7 @@ export class TaskStore {
|
|
|
163
146
|
tasks = tasks.filter(t => t.parentId === filter.parentId);
|
|
164
147
|
}
|
|
165
148
|
if (filter.botId !== undefined) {
|
|
166
|
-
tasks = tasks.filter(t => t.
|
|
149
|
+
tasks = tasks.filter(t => t.assignedProfile === filter.botId);
|
|
167
150
|
}
|
|
168
151
|
if (filter.limit !== undefined && filter.limit > 0) {
|
|
169
152
|
tasks = tasks.slice(0, filter.limit);
|
|
@@ -181,27 +164,36 @@ export class TaskStore {
|
|
|
181
164
|
if (idx === -1) throw new Error(`Task not found: ${id}`);
|
|
182
165
|
|
|
183
166
|
const task = tasks[idx];
|
|
167
|
+
|
|
168
|
+
// Reset stagnation when profile is reassigned (fresh start for new approach)
|
|
169
|
+
if (patch.assignedProfile && patch.assignedProfile !== task.assignedProfile) {
|
|
170
|
+
task.context.stagnationCount = 0;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Trigger parent effects when status changes to done or cancelled
|
|
174
|
+
const statusChanged = patch.status && patch.status !== task.status;
|
|
175
|
+
|
|
184
176
|
Object.assign(task, patch, { updatedAt: new Date().toISOString() });
|
|
185
177
|
tasks[idx] = task;
|
|
178
|
+
|
|
179
|
+
if (statusChanged && (task.status === 'done' || task.status === 'cancelled')) {
|
|
180
|
+
this._handleDependencyEffects(tasks, task);
|
|
181
|
+
this._handleParentEffects(tasks, task);
|
|
182
|
+
}
|
|
183
|
+
|
|
186
184
|
this._writeAll(tasks);
|
|
187
185
|
return task;
|
|
188
186
|
});
|
|
189
187
|
}
|
|
190
188
|
|
|
191
|
-
async updateContext(id: string,
|
|
189
|
+
async updateContext(id: string, runProgress: RunProgress): Promise<Task> {
|
|
192
190
|
return this.mutex.runExclusive(async () => {
|
|
193
191
|
const tasks = this._readAll();
|
|
194
192
|
const idx = tasks.findIndex(t => t.id === id);
|
|
195
193
|
if (idx === -1) throw new Error(`Task not found: ${id}`);
|
|
196
194
|
|
|
197
195
|
const task = tasks[idx];
|
|
198
|
-
task.context.
|
|
199
|
-
|
|
200
|
-
if (runSummary.outcome === 'failed' || runSummary.outcome === 'error') {
|
|
201
|
-
task.context.lastError = runSummary.error;
|
|
202
|
-
} else {
|
|
203
|
-
task.context.lastError = undefined;
|
|
204
|
-
}
|
|
196
|
+
task.context.runHistory.push(runProgress);
|
|
205
197
|
|
|
206
198
|
task.updatedAt = new Date().toISOString();
|
|
207
199
|
tasks[idx] = task;
|
|
@@ -233,14 +225,13 @@ export class TaskStore {
|
|
|
233
225
|
|
|
234
226
|
const task = tasks[idx];
|
|
235
227
|
const assignable =
|
|
236
|
-
|
|
237
|
-
task.attempt < task.maxAttempts;
|
|
228
|
+
task.status === 'open' && !task.context.budgetExhausted;
|
|
238
229
|
if (!assignable) {
|
|
239
|
-
throw new Error(`Task ${taskId} is not assignable (status: ${task.status}
|
|
230
|
+
throw new Error(`Task ${taskId} is not assignable (status: ${task.status})`);
|
|
240
231
|
}
|
|
241
232
|
|
|
242
233
|
task.status = 'in-progress';
|
|
243
|
-
task.
|
|
234
|
+
task.activeRunId = instanceId; // will be replaced with actual runId by swarm controller
|
|
244
235
|
task.assignedProfile = profileId;
|
|
245
236
|
task.updatedAt = new Date().toISOString();
|
|
246
237
|
|
|
@@ -254,7 +245,7 @@ export class TaskStore {
|
|
|
254
245
|
// Release
|
|
255
246
|
// ---------------------------------------------------------------------------
|
|
256
247
|
|
|
257
|
-
async release(taskId: string, status: 'done' | '
|
|
248
|
+
async release(taskId: string, status: 'done' | 'open', runProgress: RunProgress): Promise<Task> {
|
|
258
249
|
return this.mutex.runExclusive(async () => {
|
|
259
250
|
const tasks = this._readAll();
|
|
260
251
|
const idx = tasks.findIndex(t => t.id === taskId);
|
|
@@ -262,33 +253,59 @@ export class TaskStore {
|
|
|
262
253
|
|
|
263
254
|
const task = tasks[idx];
|
|
264
255
|
|
|
265
|
-
// Append run
|
|
266
|
-
task.context.
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
256
|
+
// Append run progress
|
|
257
|
+
task.context.runHistory.push(runProgress);
|
|
258
|
+
|
|
259
|
+
// Accumulate usage
|
|
260
|
+
task.tokensUsed += runProgress.tokensUsed;
|
|
261
|
+
task.costUsed += runProgress.cost;
|
|
262
|
+
|
|
263
|
+
// Update stagnation count
|
|
264
|
+
const prevRun = task.context.runHistory.length >= 2
|
|
265
|
+
? task.context.runHistory[task.context.runHistory.length - 2]
|
|
266
|
+
: undefined;
|
|
267
|
+
if (prevRun && 'filesCreated' in prevRun && 'filesModified' in prevRun) {
|
|
268
|
+
const prevFiles = new Set([...(prevRun.filesCreated as string[]), ...(prevRun.filesModified as string[])]);
|
|
269
|
+
const newFiles = new Set([...runProgress.filesCreated, ...runProgress.filesModified]);
|
|
270
|
+
const hasNewFiles = [...newFiles].some(f => !prevFiles.has(f));
|
|
271
|
+
task.context.stagnationCount = hasNewFiles ? 0 : task.context.stagnationCount + 1;
|
|
272
272
|
} else {
|
|
273
|
-
task.context.
|
|
273
|
+
task.context.stagnationCount = 0;
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
-
//
|
|
277
|
-
task.
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
task.
|
|
284
|
-
task.currentBotId = undefined;
|
|
285
|
-
task.currentRunId = undefined;
|
|
276
|
+
// Check budget exhaustion
|
|
277
|
+
if (task.budgetCost && task.costUsed >= task.budgetCost) {
|
|
278
|
+
task.context.budgetExhausted = true;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Update status
|
|
282
|
+
task.status = status;
|
|
283
|
+
task.activeRunId = undefined;
|
|
286
284
|
task.updatedAt = new Date().toISOString();
|
|
287
285
|
|
|
288
286
|
if (status === 'done') {
|
|
289
287
|
task.completedAt = new Date().toISOString();
|
|
290
288
|
}
|
|
291
|
-
|
|
289
|
+
|
|
290
|
+
// Compact old runs: keep last 5 full, older become audit-only
|
|
291
|
+
if (task.context.runHistory.length > 5) {
|
|
292
|
+
const cutoff = task.context.runHistory.length - 5;
|
|
293
|
+
for (let i = 0; i < cutoff; i++) {
|
|
294
|
+
const old = task.context.runHistory[i];
|
|
295
|
+
if ('filesCreated' in old) {
|
|
296
|
+
task.context.runHistory[i] = {
|
|
297
|
+
runId: old.runId,
|
|
298
|
+
botId: old.botId,
|
|
299
|
+
profileId: old.profileId,
|
|
300
|
+
outcome: old.outcome,
|
|
301
|
+
tokensUsed: old.tokensUsed,
|
|
302
|
+
cost: old.cost,
|
|
303
|
+
durationMs: old.durationMs,
|
|
304
|
+
endedAt: old.endedAt,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
292
309
|
|
|
293
310
|
tasks[idx] = task;
|
|
294
311
|
|
|
@@ -306,20 +323,21 @@ export class TaskStore {
|
|
|
306
323
|
// ---------------------------------------------------------------------------
|
|
307
324
|
|
|
308
325
|
private _handleDependencyEffects(tasks: Task[], changedTask: Task): void {
|
|
309
|
-
if (changedTask.status
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
326
|
+
if (changedTask.status !== 'done' && changedTask.status !== 'cancelled') return;
|
|
327
|
+
|
|
328
|
+
// A dep is resolved when it's done OR cancelled.
|
|
329
|
+
const resolvedIds = new Set(
|
|
330
|
+
tasks.filter(t => t.status === 'done' || t.status === 'cancelled').map(t => t.id),
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
for (const t of tasks) {
|
|
334
|
+
if (t.status === 'open' && t.dependsOn.includes(changedTask.id)) {
|
|
335
|
+
if (t.dependsOn.every(depId => resolvedIds.has(depId))) {
|
|
336
|
+
// All deps resolved — bump updatedAt as a "ready" signal for the dispatcher.
|
|
337
|
+
t.updatedAt = new Date().toISOString();
|
|
318
338
|
}
|
|
319
339
|
}
|
|
320
340
|
}
|
|
321
|
-
// Tasks don't fail — runs fail. Dependents stay blocked until dep
|
|
322
|
-
// succeeds (status=done). No terminal 'failed' state to handle.
|
|
323
341
|
}
|
|
324
342
|
|
|
325
343
|
private _handleParentEffects(tasks: Task[], changedTask: Task): void {
|
|
@@ -333,16 +351,21 @@ export class TaskStore {
|
|
|
333
351
|
|
|
334
352
|
const subtasks = tasks.filter(t => t.parentId === parent.id);
|
|
335
353
|
|
|
336
|
-
// All done => parent
|
|
337
|
-
|
|
354
|
+
// All subtasks resolved (done or cancelled) => parent resolved
|
|
355
|
+
const allResolved = subtasks.every(s => s.status === 'done' || s.status === 'cancelled');
|
|
356
|
+
if (!allResolved) return;
|
|
357
|
+
|
|
358
|
+
// If ALL subtasks were cancelled (none done), parent is cancelled too
|
|
359
|
+
const anyDone = subtasks.some(s => s.status === 'done');
|
|
360
|
+
if (anyDone) {
|
|
338
361
|
parent.status = 'done';
|
|
339
362
|
parent.completedAt = new Date().toISOString();
|
|
340
|
-
|
|
341
|
-
|
|
363
|
+
} else {
|
|
364
|
+
parent.status = 'cancelled';
|
|
365
|
+
parent.cancelledAt = new Date().toISOString();
|
|
366
|
+
parent.cancelReason = 'All subtasks cancelled';
|
|
342
367
|
}
|
|
343
|
-
|
|
344
|
-
// Tasks don't fail. Parent stays open until all subtasks are done
|
|
345
|
-
// or user cancels. No terminal 'failed' state for parent.
|
|
368
|
+
parent.updatedAt = new Date().toISOString();
|
|
346
369
|
}
|
|
347
370
|
|
|
348
371
|
// ---------------------------------------------------------------------------
|
|
@@ -373,9 +396,9 @@ export class TaskStore {
|
|
|
373
396
|
// ---------------------------------------------------------------------------
|
|
374
397
|
|
|
375
398
|
private _findDuplicate(tasks: Task[], title: string, description: string): Task | null {
|
|
376
|
-
// Check open
|
|
399
|
+
// Check open duplicates
|
|
377
400
|
const openDup = tasks.find(
|
|
378
|
-
t =>
|
|
401
|
+
t => t.status === 'open' && t.title === title && t.description === description,
|
|
379
402
|
);
|
|
380
403
|
if (openDup) return openDup;
|
|
381
404
|
|
package/src/bot/task-types.ts
CHANGED
|
@@ -1,44 +1,94 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Task entity types for the
|
|
3
|
-
*
|
|
2
|
+
* Task entity types for the Convergent Swarm.
|
|
3
|
+
*
|
|
4
|
+
* Core principles:
|
|
5
|
+
* - Tasks never fail. Runs contribute.
|
|
6
|
+
* - Acceptance criteria are checked by code, not AI.
|
|
7
|
+
* - Budget is the only hard limit.
|
|
4
8
|
*/
|
|
5
9
|
|
|
6
|
-
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Run progress — what a single run contributed
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
export type RunOutcomeStatus = 'contributed' | 'completed' | 'stalled' | 'crashed';
|
|
15
|
+
|
|
16
|
+
export interface RunProgress {
|
|
7
17
|
runId: string;
|
|
8
18
|
botId: string;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
19
|
+
profileId: string;
|
|
20
|
+
outcome: RunOutcomeStatus;
|
|
21
|
+
filesCreated: string[];
|
|
12
22
|
filesModified: string[];
|
|
13
|
-
|
|
23
|
+
summary: string;
|
|
24
|
+
remainingWork?: string;
|
|
25
|
+
blockers?: string[];
|
|
26
|
+
checks?: Record<string, string>;
|
|
27
|
+
tokensUsed: number;
|
|
28
|
+
cost: number;
|
|
14
29
|
durationMs: number;
|
|
30
|
+
endedAt: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface CompactedRun {
|
|
34
|
+
runId: string;
|
|
35
|
+
botId: string;
|
|
36
|
+
profileId: string;
|
|
37
|
+
outcome: RunOutcomeStatus;
|
|
15
38
|
tokensUsed: number;
|
|
16
39
|
cost: number;
|
|
40
|
+
durationMs: number;
|
|
41
|
+
endedAt: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Acceptance criteria — checked by code, not AI
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
export interface AcceptanceCheck {
|
|
49
|
+
name: string;
|
|
50
|
+
command: string;
|
|
17
51
|
}
|
|
18
52
|
|
|
53
|
+
export interface AcceptanceCriteria {
|
|
54
|
+
checks: AcceptanceCheck[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface AcceptanceResult {
|
|
58
|
+
met: boolean;
|
|
59
|
+
results: Array<{ name: string; pass: boolean; detail?: string }>;
|
|
60
|
+
checkedAt: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Task context
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
|
|
19
67
|
export interface TaskContext {
|
|
20
68
|
files: string[];
|
|
21
69
|
notes: string;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
70
|
+
runHistory: (RunProgress | CompactedRun)[];
|
|
71
|
+
stagnationCount: number;
|
|
72
|
+
budgetExhausted?: boolean;
|
|
25
73
|
projectBrief?: string;
|
|
26
74
|
}
|
|
27
75
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
export type TaskStatus = 'open' | '
|
|
33
|
-
|
|
34
|
-
//
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// Task status
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
export type TaskStatus = 'open' | 'in-progress' | 'done' | 'cancelled';
|
|
81
|
+
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
// Task
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
35
85
|
|
|
36
86
|
export interface Task {
|
|
37
87
|
id: string;
|
|
38
88
|
title: string;
|
|
39
89
|
description: string;
|
|
40
90
|
status: TaskStatus;
|
|
41
|
-
priority: number;
|
|
91
|
+
priority: number;
|
|
42
92
|
|
|
43
93
|
// Hierarchy
|
|
44
94
|
isParent: boolean;
|
|
@@ -46,36 +96,32 @@ export interface Task {
|
|
|
46
96
|
dependsOn: string[];
|
|
47
97
|
|
|
48
98
|
// Assignment
|
|
49
|
-
|
|
50
|
-
|
|
99
|
+
assignedProfile?: string;
|
|
100
|
+
activeRunId?: string;
|
|
101
|
+
|
|
102
|
+
// Acceptance
|
|
103
|
+
acceptance?: AcceptanceCriteria;
|
|
104
|
+
lastAcceptanceCheck?: AcceptanceResult;
|
|
51
105
|
|
|
52
106
|
// Context
|
|
53
107
|
context: TaskContext;
|
|
54
108
|
|
|
55
|
-
// Execution
|
|
56
|
-
runs: string[];
|
|
57
|
-
attempt: number;
|
|
58
|
-
maxAttempts: number;
|
|
59
|
-
|
|
60
109
|
// Budget
|
|
61
|
-
budgetTokens?: number;
|
|
62
110
|
budgetCost?: number;
|
|
63
111
|
tokensUsed: number;
|
|
64
112
|
costUsed: number;
|
|
65
113
|
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// Orchestrator routing
|
|
70
|
-
assignedProfile?: string; // profile ID assigned by orchestrator
|
|
71
|
-
routingReason?: string; // why this profile was chosen
|
|
72
|
-
complexity?: 'trivial' | 'simple' | 'moderate' | 'complex'; // task complexity estimate
|
|
114
|
+
// Routing
|
|
115
|
+
routingReason?: string;
|
|
116
|
+
complexity?: 'trivial' | 'simple' | 'moderate' | 'complex';
|
|
73
117
|
|
|
74
118
|
// Metadata
|
|
75
119
|
createdBy: 'user' | 'ai';
|
|
76
120
|
createdAt: string;
|
|
77
121
|
updatedAt: string;
|
|
78
122
|
completedAt?: string;
|
|
123
|
+
cancelledAt?: string;
|
|
124
|
+
cancelReason?: string;
|
|
79
125
|
}
|
|
80
126
|
|
|
81
127
|
export interface TaskFilter {
|
|
@@ -91,13 +137,11 @@ export interface CreateTaskInput {
|
|
|
91
137
|
priority?: number;
|
|
92
138
|
parentId?: string;
|
|
93
139
|
dependsOn?: string[];
|
|
94
|
-
budgetTokens?: number;
|
|
95
140
|
budgetCost?: number;
|
|
96
|
-
timeoutMs?: number;
|
|
97
|
-
maxAttempts?: number;
|
|
98
141
|
createdBy?: 'user' | 'ai';
|
|
99
142
|
complexity?: 'trivial' | 'simple' | 'moderate' | 'complex';
|
|
100
143
|
assignedProfile?: string;
|
|
144
|
+
acceptance?: AcceptanceCriteria;
|
|
101
145
|
subtasks?: Array<{
|
|
102
146
|
title: string;
|
|
103
147
|
description?: string;
|
|
@@ -105,5 +149,6 @@ export interface CreateTaskInput {
|
|
|
105
149
|
priority?: number;
|
|
106
150
|
complexity?: 'trivial' | 'simple' | 'moderate' | 'complex';
|
|
107
151
|
assignedProfile?: string;
|
|
152
|
+
acceptance?: AcceptanceCriteria;
|
|
108
153
|
}>;
|
|
109
154
|
}
|
package/src/bot/types.ts
CHANGED
|
@@ -169,6 +169,8 @@ export interface WorkflowResult {
|
|
|
169
169
|
outcome: string;
|
|
170
170
|
/** Optional markdown report for rich UI display. */
|
|
171
171
|
report?: string;
|
|
172
|
+
/** Structured run progress JSON from agent-execute (convergent swarm). */
|
|
173
|
+
runProgressJson?: string;
|
|
172
174
|
functionName?: string;
|
|
173
175
|
executionTime?: number;
|
|
174
176
|
cost?: RunCostSummary;
|
package/src/bot/weaver-tools.ts
CHANGED
|
@@ -119,12 +119,23 @@ export function createWeaverExecutor(projectDir: string) {
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
// Parse acceptance criteria if provided
|
|
123
|
+
const rawAcc = args.acceptance as Record<string, unknown> | undefined;
|
|
124
|
+
const acceptance = rawAcc?.checks ? {
|
|
125
|
+
checks: (rawAcc.checks as Array<{ name: string; command: string }>).map(c => ({
|
|
126
|
+
name: String(c.name ?? ''),
|
|
127
|
+
command: String(c.command ?? ''),
|
|
128
|
+
})).filter(c => c.name && c.command),
|
|
129
|
+
} : undefined;
|
|
130
|
+
|
|
122
131
|
const task = await store.create({
|
|
123
132
|
title,
|
|
124
133
|
description: String(args.description ?? ''),
|
|
125
134
|
priority: (args.priority as number) ?? 0,
|
|
126
135
|
parentId,
|
|
127
136
|
dependsOn: (args.dependsOn as string[]) ?? [],
|
|
137
|
+
assignedProfile: args.assignedProfile as string | undefined,
|
|
138
|
+
acceptance,
|
|
128
139
|
createdBy: 'ai',
|
|
129
140
|
});
|
|
130
141
|
// Set files if provided
|
package/src/cli-handlers.ts
CHANGED
|
@@ -70,7 +70,6 @@ export interface ParsedArgs {
|
|
|
70
70
|
confirmFlag?: boolean;
|
|
71
71
|
taskDescription?: string;
|
|
72
72
|
complexity?: string;
|
|
73
|
-
autoRetry?: boolean;
|
|
74
73
|
// assistant
|
|
75
74
|
assistantNew?: boolean;
|
|
76
75
|
assistantResume?: string;
|
|
@@ -341,8 +340,6 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
341
340
|
} else if (arg === '--complexity' && i + 1 < args.length) {
|
|
342
341
|
i++;
|
|
343
342
|
result.complexity = args[i];
|
|
344
|
-
} else if (arg === '--auto-retry') {
|
|
345
|
-
result.autoRetry = true;
|
|
346
343
|
} else if (arg === '--status' && i + 1 < args.length) {
|
|
347
344
|
i++;
|
|
348
345
|
result.historyOutcome = args[i];
|
|
@@ -1443,7 +1440,6 @@ export async function handleSwarm(opts: ParsedArgs): Promise<void> {
|
|
|
1443
1440
|
maxConcurrent: opts.maxConcurrent,
|
|
1444
1441
|
workspaceBudgetTokens: opts.budgetTokens,
|
|
1445
1442
|
workspaceBudgetCost: opts.budgetCost,
|
|
1446
|
-
autoRetry: opts.autoRetry,
|
|
1447
1443
|
}, projectDir);
|
|
1448
1444
|
console.log('\x1b[32m→ Config updated\x1b[0m');
|
|
1449
1445
|
break;
|
|
@@ -1483,7 +1479,7 @@ export async function handleTask(opts: ParsedArgs): Promise<void> {
|
|
|
1483
1479
|
return;
|
|
1484
1480
|
}
|
|
1485
1481
|
for (const t of tasks) {
|
|
1486
|
-
const icon = t.status === 'done' ? '\x1b[32m✓\x1b[0m' : t.status === '
|
|
1482
|
+
const icon = t.status === 'done' ? '\x1b[32m✓\x1b[0m' : t.status === 'cancelled' ? '\x1b[31m✗\x1b[0m' : t.status === 'in-progress' ? '\x1b[36m▸\x1b[0m' : '○';
|
|
1487
1483
|
console.log(` ${icon} ${(t.id ?? '').slice(0, 8)} ${t.title ?? ''} [${t.status ?? ''}] ${t.assignedProfile ? `→ ${t.assignedProfile}` : ''}`);
|
|
1488
1484
|
}
|
|
1489
1485
|
break;
|
|
@@ -1755,12 +1751,12 @@ export async function handleStatus(opts: ParsedArgs): Promise<void> {
|
|
|
1755
1751
|
const pending = tasks.filter((t: { status: string }) => t.status === 'pending').length;
|
|
1756
1752
|
const running = tasks.filter((t: { status: string }) => t.status === 'in-progress').length;
|
|
1757
1753
|
const done = tasks.filter((t: { status: string }) => t.status === 'done').length;
|
|
1758
|
-
const
|
|
1759
|
-
console.log(` Tasks: ${pending}
|
|
1754
|
+
const cancelled = tasks.filter((t: { status: string }) => t.status === 'cancelled').length;
|
|
1755
|
+
console.log(` Tasks: ${pending} open, ${running} running, ${done} done, ${cancelled} cancelled`);
|
|
1760
1756
|
|
|
1761
1757
|
console.log(`\n Recent tasks:`);
|
|
1762
1758
|
for (const t of tasks.slice(0, 5)) {
|
|
1763
|
-
const icon = t.status === 'done' ? '\x1b[32m✓\x1b[0m' : t.status === '
|
|
1759
|
+
const icon = t.status === 'done' ? '\x1b[32m✓\x1b[0m' : t.status === 'cancelled' ? '\x1b[31m✗\x1b[0m' : t.status === 'in-progress' ? '\x1b[36m▸\x1b[0m' : '○';
|
|
1764
1760
|
console.log(` ${icon} ${(t.id ?? '').slice(0, 8)} [${t.status}] ${(t.title ?? '').slice(0, 60)}`);
|
|
1765
1761
|
}
|
|
1766
1762
|
} else {
|
|
@@ -2243,7 +2239,7 @@ export async function handleDoctor(opts: ParsedArgs): Promise<void> {
|
|
|
2243
2239
|
const { TaskStore } = await import('./bot/task-store.js');
|
|
2244
2240
|
const taskStore = new TaskStore(dir);
|
|
2245
2241
|
const tasks = await taskStore.list();
|
|
2246
|
-
const pending = tasks.filter(t => t.status === '
|
|
2242
|
+
const pending = tasks.filter(t => t.status === 'open').length;
|
|
2247
2243
|
const inProgress = tasks.filter(t => t.status === 'in-progress').length;
|
|
2248
2244
|
checks.push({ label: 'Queue', status: 'ok', detail: `${pending} pending, ${inProgress} in-progress` });
|
|
2249
2245
|
} catch {
|
package/src/index.ts
CHANGED