@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.
Files changed (102) hide show
  1. package/dist/ai-chat-provider.d.ts.map +1 -1
  2. package/dist/ai-chat-provider.js +17 -11
  3. package/dist/ai-chat-provider.js.map +1 -1
  4. package/dist/bot/ai-router.js +5 -5
  5. package/dist/bot/ai-router.js.map +1 -1
  6. package/dist/bot/assistant-tools.d.ts.map +1 -1
  7. package/dist/bot/assistant-tools.js +6 -7
  8. package/dist/bot/assistant-tools.js.map +1 -1
  9. package/dist/bot/capability-registry.d.ts.map +1 -1
  10. package/dist/bot/capability-registry.js +37 -14
  11. package/dist/bot/capability-registry.js.map +1 -1
  12. package/dist/bot/dashboard.js +1 -1
  13. package/dist/bot/dashboard.js.map +1 -1
  14. package/dist/bot/index.d.ts +1 -1
  15. package/dist/bot/index.d.ts.map +1 -1
  16. package/dist/bot/index.js.map +1 -1
  17. package/dist/bot/instance-manager.js +3 -3
  18. package/dist/bot/instance-manager.js.map +1 -1
  19. package/dist/bot/profile-store.d.ts.map +1 -1
  20. package/dist/bot/profile-store.js +11 -9
  21. package/dist/bot/profile-store.js.map +1 -1
  22. package/dist/bot/profile-types.d.ts +2 -2
  23. package/dist/bot/profile-types.d.ts.map +1 -1
  24. package/dist/bot/runner.d.ts +1 -0
  25. package/dist/bot/runner.d.ts.map +1 -1
  26. package/dist/bot/runner.js +6 -2
  27. package/dist/bot/runner.js.map +1 -1
  28. package/dist/bot/step-executor.d.ts.map +1 -1
  29. package/dist/bot/step-executor.js +10 -0
  30. package/dist/bot/step-executor.js.map +1 -1
  31. package/dist/bot/swarm-controller.d.ts +3 -5
  32. package/dist/bot/swarm-controller.d.ts.map +1 -1
  33. package/dist/bot/swarm-controller.js +157 -74
  34. package/dist/bot/swarm-controller.js.map +1 -1
  35. package/dist/bot/task-prompt-builder.d.ts +2 -3
  36. package/dist/bot/task-prompt-builder.d.ts.map +1 -1
  37. package/dist/bot/task-prompt-builder.js +81 -67
  38. package/dist/bot/task-prompt-builder.js.map +1 -1
  39. package/dist/bot/task-store.d.ts +3 -3
  40. package/dist/bot/task-store.d.ts.map +1 -1
  41. package/dist/bot/task-store.js +89 -75
  42. package/dist/bot/task-store.js.map +1 -1
  43. package/dist/bot/task-types.d.ts +54 -26
  44. package/dist/bot/task-types.d.ts.map +1 -1
  45. package/dist/bot/task-types.js +6 -2
  46. package/dist/bot/task-types.js.map +1 -1
  47. package/dist/bot/types.d.ts +2 -0
  48. package/dist/bot/types.d.ts.map +1 -1
  49. package/dist/bot/weaver-tools.d.ts.map +1 -1
  50. package/dist/bot/weaver-tools.js +10 -0
  51. package/dist/bot/weaver-tools.js.map +1 -1
  52. package/dist/cli-handlers.d.ts +0 -1
  53. package/dist/cli-handlers.d.ts.map +1 -1
  54. package/dist/cli-handlers.js +5 -9
  55. package/dist/cli-handlers.js.map +1 -1
  56. package/dist/index.d.ts +1 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js.map +1 -1
  59. package/dist/node-types/agent-execute.d.ts.map +1 -1
  60. package/dist/node-types/agent-execute.js +95 -63
  61. package/dist/node-types/agent-execute.js.map +1 -1
  62. package/dist/node-types/plan-task.js +8 -8
  63. package/dist/node-types/plan-task.js.map +1 -1
  64. package/dist/ui/bot-panel.js +1 -1
  65. package/dist/ui/capability-editor.js +37 -14
  66. package/dist/ui/chat-task-result.js +1 -7
  67. package/dist/ui/profile-editor.js +37 -14
  68. package/dist/ui/swarm-controls.js +2 -2
  69. package/dist/ui/swarm-dashboard.js +72 -109
  70. package/dist/ui/task-detail-view.js +21 -42
  71. package/dist/ui/task-editor.js +13 -50
  72. package/dist/ui/task-pool-list.js +0 -2
  73. package/flowweaver.manifest.json +1 -1
  74. package/package.json +1 -1
  75. package/src/ai-chat-provider.ts +15 -11
  76. package/src/bot/ai-router.ts +5 -5
  77. package/src/bot/assistant-tools.ts +6 -7
  78. package/src/bot/capability-registry.ts +37 -14
  79. package/src/bot/dashboard.ts +1 -1
  80. package/src/bot/index.ts +5 -1
  81. package/src/bot/instance-manager.ts +3 -3
  82. package/src/bot/profile-store.ts +12 -10
  83. package/src/bot/profile-types.ts +2 -2
  84. package/src/bot/runner.ts +6 -2
  85. package/src/bot/step-executor.ts +11 -0
  86. package/src/bot/swarm-controller.ts +164 -78
  87. package/src/bot/task-prompt-builder.ts +86 -71
  88. package/src/bot/task-store.ts +101 -78
  89. package/src/bot/task-types.ts +81 -36
  90. package/src/bot/types.ts +2 -0
  91. package/src/bot/weaver-tools.ts +11 -0
  92. package/src/cli-handlers.ts +5 -9
  93. package/src/index.ts +6 -0
  94. package/src/node-types/agent-execute.ts +99 -62
  95. package/src/node-types/plan-task.ts +8 -8
  96. package/src/ui/bot-panel.tsx +3 -3
  97. package/src/ui/chat-task-result.tsx +5 -14
  98. package/src/ui/swarm-controls.tsx +3 -3
  99. package/src/ui/swarm-dashboard.tsx +3 -3
  100. package/src/ui/task-detail-view.tsx +29 -52
  101. package/src/ui/task-editor.tsx +14 -51
  102. package/src/ui/task-pool-list.tsx +1 -3
@@ -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, RunSummary } from './task-types.js';
5
+ import type { Task, TaskFilter, CreateTaskInput, RunProgress } from './task-types.js';
6
6
 
7
- /** Don't re-queue tasks completed/failed within this window (ms). */
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
- currentBotId: undefined,
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
- timeoutMs: input.timeoutMs,
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
- currentBotId: undefined,
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
- currentBotId: undefined,
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
- timeoutMs: input.timeoutMs,
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.currentBotId === filter.botId);
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, runSummary: RunSummary): Promise<Task> {
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.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
- }
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
- (task.status === 'open' || task.status === 'pending') &&
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}, attempt: ${task.attempt}/${task.maxAttempts})`);
230
+ throw new Error(`Task ${taskId} is not assignable (status: ${task.status})`);
240
231
  }
241
232
 
242
233
  task.status = 'in-progress';
243
- task.currentBotId = instanceId;
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' | 'failed' | 'open', runSummary: RunSummary): Promise<Task> {
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 summary and track runId
266
- task.context.runSummaries.push(runSummary);
267
- if (runSummary.runId && !task.runs.includes(runSummary.runId)) {
268
- task.runs.push(runSummary.runId);
269
- }
270
- if (runSummary.outcome === 'failed' || runSummary.outcome === 'error') {
271
- task.context.lastError = runSummary.error;
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.lastError = undefined;
273
+ task.context.stagnationCount = 0;
274
274
  }
275
275
 
276
- // Accumulate usage
277
- task.tokensUsed += runSummary.tokensUsed;
278
- task.costUsed += runSummary.cost;
279
-
280
- // Update execution tracking
281
- task.attempt += 1;
282
- // Tasks don't fail — runs fail. Map 'failed' to 'open' for backward compat.
283
- task.status = status === 'failed' ? 'open' : status;
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
- // 'open' and 'failed' → task stays open, no completedAt
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 === 'done') {
310
- // Unblock dependents: set blocked tasks to open if all deps are now done
311
- const doneIds = new Set(tasks.filter(t => t.status === 'done').map(t => t.id));
312
- for (const t of tasks) {
313
- if (t.status === 'blocked' && t.dependsOn.includes(changedTask.id)) {
314
- if (t.dependsOn.every(depId => doneIds.has(depId))) {
315
- t.status = 'open';
316
- t.updatedAt = new Date().toISOString();
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 done
337
- if (subtasks.every(s => s.status === 'done')) {
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
- parent.updatedAt = new Date().toISOString();
341
- return;
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/pending duplicates
399
+ // Check open duplicates
377
400
  const openDup = tasks.find(
378
- t => (t.status === 'open' || t.status === 'pending') && t.title === title && t.description === description,
401
+ t => t.status === 'open' && t.title === title && t.description === description,
379
402
  );
380
403
  if (openDup) return openDup;
381
404
 
@@ -1,44 +1,94 @@
1
1
  /**
2
- * Task entity types for the Weaver Swarm.
3
- * Tasks are first-class entities with hierarchy, context, and budget.
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
- export interface RunSummary {
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
- outcome: 'success' | 'failed' | 'error';
10
- summary: string;
11
- report?: string;
19
+ profileId: string;
20
+ outcome: RunOutcomeStatus;
21
+ filesCreated: string[];
12
22
  filesModified: string[];
13
- error?: string;
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
- runSummaries: RunSummary[];
23
- lastError?: string;
24
- /** Project brief propagated from parent task for cross-task coherence. */
70
+ runHistory: (RunProgress | CompactedRun)[];
71
+ stagnationCount: number;
72
+ budgetExhausted?: boolean;
25
73
  projectBrief?: string;
26
74
  }
27
75
 
28
- /**
29
- * Task status. Tasks NEVER go to 'failed' — runs fail, tasks stay open.
30
- * 'open' replaces 'pending'. A task is open until done or cancelled.
31
- */
32
- export type TaskStatus = 'open' | 'pending' | 'in-progress' | 'blocked' | 'done' | 'cancelled';
33
- // NOTE: 'pending' kept for backward compatibility with existing data.
34
- // New code should use 'open'. Both are treated as assignable.
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; // higher = higher priority
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
- currentBotId?: string;
50
- currentRunId?: string;
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
- // Timeout
67
- timeoutMs?: number;
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;
@@ -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
@@ -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 === 'failed' ? '\x1b[31m✗\x1b[0m' : t.status === 'in-progress' ? '\x1b[36m▸\x1b[0m' : '○';
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 failed = tasks.filter((t: { status: string }) => t.status === 'failed').length;
1759
- console.log(` Tasks: ${pending} pending, ${running} running, ${done} done, ${failed} failed`);
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 === 'failed' ? '\x1b[31m✗\x1b[0m' : t.status === 'in-progress' ? '\x1b[36m▸\x1b[0m' : '○';
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 === 'pending').length;
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
@@ -113,6 +113,12 @@ export type {
113
113
  TaskFilter,
114
114
  CreateTaskInput,
115
115
  TaskStatus,
116
+ RunProgress,
117
+ RunOutcomeStatus,
118
+ CompactedRun,
119
+ AcceptanceCheck,
120
+ AcceptanceCriteria,
121
+ AcceptanceResult,
116
122
  BotTask,
117
123
  BotPlan,
118
124
  BotPlanStep,