@synergenius/flow-weaver-pack-weaver 0.9.152 → 0.9.154
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.js +4 -4
- package/dist/ai-chat-provider.js.map +1 -1
- package/dist/bot/ai-client.d.ts +30 -0
- package/dist/bot/ai-client.d.ts.map +1 -1
- package/dist/bot/ai-client.js +37 -0
- package/dist/bot/ai-client.js.map +1 -1
- package/dist/bot/behavior-defaults.d.ts.map +1 -1
- package/dist/bot/behavior-defaults.js +7 -2
- package/dist/bot/behavior-defaults.js.map +1 -1
- package/dist/bot/capability-registry.d.ts.map +1 -1
- package/dist/bot/capability-registry.js +46 -33
- package/dist/bot/capability-registry.js.map +1 -1
- package/dist/bot/file-validator.d.ts +7 -0
- package/dist/bot/file-validator.d.ts.map +1 -1
- package/dist/bot/file-validator.js +76 -0
- package/dist/bot/file-validator.js.map +1 -1
- package/dist/bot/instance-manager.d.ts +22 -7
- package/dist/bot/instance-manager.d.ts.map +1 -1
- package/dist/bot/instance-manager.js +69 -7
- package/dist/bot/instance-manager.js.map +1 -1
- package/dist/bot/orchestrator.d.ts +11 -9
- package/dist/bot/orchestrator.d.ts.map +1 -1
- package/dist/bot/orchestrator.js +56 -107
- package/dist/bot/orchestrator.js.map +1 -1
- package/dist/bot/runner.d.ts +29 -0
- package/dist/bot/runner.d.ts.map +1 -1
- package/dist/bot/runner.js +114 -73
- package/dist/bot/runner.js.map +1 -1
- package/dist/bot/step-executor.d.ts.map +1 -1
- package/dist/bot/step-executor.js +28 -9
- package/dist/bot/step-executor.js.map +1 -1
- package/dist/bot/swarm-controller.d.ts +7 -6
- package/dist/bot/swarm-controller.d.ts.map +1 -1
- package/dist/bot/swarm-controller.js +64 -74
- package/dist/bot/swarm-controller.js.map +1 -1
- package/dist/bot/system-prompt.d.ts.map +1 -1
- package/dist/bot/system-prompt.js +2 -0
- package/dist/bot/system-prompt.js.map +1 -1
- package/dist/bot/task-types.d.ts +1 -0
- package/dist/bot/task-types.d.ts.map +1 -1
- package/dist/bot/weaver-tools.d.ts +1 -1
- package/dist/bot/weaver-tools.d.ts.map +1 -1
- package/dist/bot/weaver-tools.js +12 -1
- package/dist/bot/weaver-tools.js.map +1 -1
- package/dist/node-types/agent-execute.js +2 -2
- package/dist/node-types/agent-execute.js.map +1 -1
- package/dist/node-types/bot-report.d.ts.map +1 -1
- package/dist/node-types/bot-report.js +5 -2
- package/dist/node-types/bot-report.js.map +1 -1
- package/dist/node-types/build-context.d.ts.map +1 -1
- package/dist/node-types/build-context.js +13 -1
- package/dist/node-types/build-context.js.map +1 -1
- package/dist/node-types/exec-validate-retry.d.ts +3 -3
- package/dist/node-types/exec-validate-retry.d.ts.map +1 -1
- package/dist/node-types/exec-validate-retry.js +13 -184
- package/dist/node-types/exec-validate-retry.js.map +1 -1
- package/dist/node-types/load-config.d.ts +1 -0
- package/dist/node-types/load-config.d.ts.map +1 -1
- package/dist/node-types/load-config.js +1 -0
- package/dist/node-types/load-config.js.map +1 -1
- package/dist/node-types/plan-task.d.ts +7 -5
- package/dist/node-types/plan-task.d.ts.map +1 -1
- package/dist/node-types/plan-task.js +282 -83
- package/dist/node-types/plan-task.js.map +1 -1
- package/dist/ui/bot-panel.js +1 -1
- package/dist/ui/capability-editor.js +46 -33
- package/dist/ui/chat-task-result.js +7 -7
- package/dist/ui/profile-editor.js +44 -31
- package/dist/ui/swarm-dashboard.js +80 -47
- package/dist/ui/task-detail-view.js +31 -11
- package/dist/ui/task-editor.js +1 -1
- package/dist/ui/task-pool-list.js +1 -1
- package/dist/workflows/weaver-bot.d.ts +5 -4
- package/dist/workflows/weaver-bot.d.ts.map +1 -1
- package/dist/workflows/weaver-bot.js +8 -7
- package/dist/workflows/weaver-bot.js.map +1 -1
- package/flowweaver.manifest.json +1 -1
- package/package.json +1 -1
- package/src/ai-chat-provider.ts +4 -4
- package/src/bot/ai-client.ts +65 -0
- package/src/bot/behavior-defaults.ts +5 -2
- package/src/bot/capability-registry.ts +46 -33
- package/src/bot/file-validator.ts +97 -0
- package/src/bot/instance-manager.ts +77 -7
- package/src/bot/orchestrator.ts +63 -126
- package/src/bot/runner.ts +124 -70
- package/src/bot/step-executor.ts +30 -9
- package/src/bot/swarm-controller.ts +65 -76
- package/src/bot/system-prompt.ts +2 -0
- package/src/bot/task-types.ts +1 -0
- package/src/bot/weaver-tools.ts +14 -1
- package/src/node-types/agent-execute.ts +2 -2
- package/src/node-types/bot-report.ts +5 -2
- package/src/node-types/build-context.ts +13 -1
- package/src/node-types/exec-validate-retry.ts +14 -203
- package/src/node-types/load-config.ts +1 -0
- package/src/node-types/plan-task.ts +313 -88
- package/src/ui/bot-panel.tsx +1 -1
- package/src/ui/chat-task-result.tsx +10 -8
- package/src/ui/swarm-dashboard.tsx +4 -4
- package/src/ui/task-detail-view.tsx +35 -12
- package/src/ui/task-editor.tsx +2 -2
- package/src/ui/task-pool-list.tsx +2 -2
- package/src/workflows/weaver-bot.ts +8 -7
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SwarmController — multi-bot orchestrator for the Weaver Swarm.
|
|
3
3
|
*
|
|
4
|
-
* Uses a
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Uses a dynamic worker pool: N generic worker slots (controlled by maxConcurrent).
|
|
5
|
+
* Any worker can run any profile's task. The profile is loaded per-task, not per-slot.
|
|
6
|
+
*
|
|
7
|
+
* Worker naming: `worker-0`, `worker-1`, `worker-2` (no per-profile instances).
|
|
7
8
|
*
|
|
8
9
|
* Singleton per workspace via `SwarmController.getInstance(projectDir)`.
|
|
9
10
|
*/
|
|
@@ -42,7 +43,7 @@ export interface SwarmState {
|
|
|
42
43
|
status: 'idle' | 'running' | 'paused' | 'stopping';
|
|
43
44
|
startedAt?: string;
|
|
44
45
|
|
|
45
|
-
/** Active
|
|
46
|
+
/** Active worker slots (generic pool). Keyed by instanceId (worker-0, worker-1, ...). */
|
|
46
47
|
instances: Record<string, BotInstance>;
|
|
47
48
|
maxConcurrent: number;
|
|
48
49
|
|
|
@@ -105,7 +106,7 @@ export class SwarmController {
|
|
|
105
106
|
private dispatchAbort: AbortController | null = null;
|
|
106
107
|
/** Promise for the dispatch loop (resolved when the loop exits). */
|
|
107
108
|
private dispatchLoopPromise: Promise<void> | null = null;
|
|
108
|
-
/** Promises for currently executing tasks (
|
|
109
|
+
/** Promises for currently executing tasks (workerId -> Promise). */
|
|
109
110
|
private executionPromises = new Map<string, Promise<void>>();
|
|
110
111
|
/** Track which promises have settled (resolved or rejected). */
|
|
111
112
|
private settledPromises = new Set<string>();
|
|
@@ -189,23 +190,11 @@ export class SwarmController {
|
|
|
189
190
|
if (bot) this.profileBotMap.set(profile.id, bot);
|
|
190
191
|
}
|
|
191
192
|
|
|
192
|
-
// Stop any existing
|
|
193
|
+
// Stop any existing workers
|
|
193
194
|
this.instanceManager.stopAll();
|
|
194
195
|
|
|
195
|
-
// Spawn
|
|
196
|
-
|
|
197
|
-
for (const profile of profiles) {
|
|
198
|
-
if (totalSpawned >= this.state.maxConcurrent) break;
|
|
199
|
-
|
|
200
|
-
// Spawn at least minInstances, or 1 if minInstances is 0
|
|
201
|
-
const toSpawn = Math.max(profile.minInstances, 1);
|
|
202
|
-
const clamped = Math.min(toSpawn, this.state.maxConcurrent - totalSpawned);
|
|
203
|
-
|
|
204
|
-
for (let i = 0; i < clamped; i++) {
|
|
205
|
-
this.instanceManager.spawn(profile);
|
|
206
|
-
totalSpawned++;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
196
|
+
// Spawn generic worker pool based on maxConcurrent
|
|
197
|
+
this.instanceManager.spawnPool(this.state.maxConcurrent);
|
|
209
198
|
|
|
210
199
|
// Populate state fields
|
|
211
200
|
this._syncInstancesState();
|
|
@@ -259,7 +248,7 @@ export class SwarmController {
|
|
|
259
248
|
const execPromises = Array.from(this.executionPromises.values());
|
|
260
249
|
await Promise.allSettled(execPromises);
|
|
261
250
|
|
|
262
|
-
// Stop all
|
|
251
|
+
// Stop all workers
|
|
263
252
|
this.instanceManager.stopAll();
|
|
264
253
|
|
|
265
254
|
// Update state
|
|
@@ -277,7 +266,7 @@ export class SwarmController {
|
|
|
277
266
|
}
|
|
278
267
|
|
|
279
268
|
getStatus(): SwarmState {
|
|
280
|
-
// Sync
|
|
269
|
+
// Sync workers from InstanceManager before returning
|
|
281
270
|
this._syncInstancesState();
|
|
282
271
|
return { ...this.state, instances: { ...this.state.instances } };
|
|
283
272
|
}
|
|
@@ -298,11 +287,11 @@ export class SwarmController {
|
|
|
298
287
|
// -----------------------------------------------------------------------
|
|
299
288
|
|
|
300
289
|
async recover(): Promise<void> {
|
|
301
|
-
// Reset all
|
|
302
|
-
const
|
|
303
|
-
for (const
|
|
304
|
-
if (
|
|
305
|
-
this.instanceManager.markIdle(
|
|
290
|
+
// Reset all workers to idle
|
|
291
|
+
const allWorkers = this.instanceManager.listAll();
|
|
292
|
+
for (const w of allWorkers) {
|
|
293
|
+
if (w.status === 'executing') {
|
|
294
|
+
this.instanceManager.markIdle(w.instanceId, false);
|
|
306
295
|
}
|
|
307
296
|
}
|
|
308
297
|
|
|
@@ -332,11 +321,11 @@ export class SwarmController {
|
|
|
332
321
|
// Token/cost recording
|
|
333
322
|
// -----------------------------------------------------------------------
|
|
334
323
|
|
|
335
|
-
recordTokenUsage(
|
|
336
|
-
// Update
|
|
324
|
+
recordTokenUsage(workerId: string, _taskId: string, tokensUsed: number, cost: number): void {
|
|
325
|
+
// Update worker in InstanceManager
|
|
337
326
|
try {
|
|
338
|
-
this.instanceManager.recordUsage(
|
|
339
|
-
} catch { /*
|
|
327
|
+
this.instanceManager.recordUsage(workerId, tokensUsed, cost);
|
|
328
|
+
} catch { /* worker may not exist if stopped */ }
|
|
340
329
|
|
|
341
330
|
// Update session budgets
|
|
342
331
|
this.state.budgets.session.usedTokens += tokensUsed;
|
|
@@ -355,7 +344,7 @@ export class SwarmController {
|
|
|
355
344
|
this.eventLog.emit({
|
|
356
345
|
type: 'budget-updated',
|
|
357
346
|
timestamp: Date.now(),
|
|
358
|
-
data: { botId:
|
|
347
|
+
data: { botId: workerId, tokensUsed, cost },
|
|
359
348
|
});
|
|
360
349
|
}
|
|
361
350
|
|
|
@@ -423,10 +412,10 @@ export class SwarmController {
|
|
|
423
412
|
break;
|
|
424
413
|
}
|
|
425
414
|
|
|
426
|
-
// Check
|
|
427
|
-
const
|
|
428
|
-
for (const
|
|
429
|
-
await this._checkSteering(
|
|
415
|
+
// Check worker-level steering
|
|
416
|
+
const allWorkers = this.instanceManager.listAll();
|
|
417
|
+
for (const w of allWorkers) {
|
|
418
|
+
await this._checkSteering(w.instanceId);
|
|
430
419
|
}
|
|
431
420
|
|
|
432
421
|
// Route unassigned tasks: top-level → orchestrator, subtasks → developer (fallback)
|
|
@@ -478,7 +467,7 @@ export class SwarmController {
|
|
|
478
467
|
|
|
479
468
|
// Build orchestrator input
|
|
480
469
|
const profiles = this.profileStore.list();
|
|
481
|
-
const
|
|
470
|
+
const workers = this.instanceManager.listAll();
|
|
482
471
|
const { workspace, session } = this.state.budgets;
|
|
483
472
|
|
|
484
473
|
const budgetRemaining = {
|
|
@@ -502,11 +491,11 @@ export class SwarmController {
|
|
|
502
491
|
},
|
|
503
492
|
})),
|
|
504
493
|
profiles,
|
|
505
|
-
instances,
|
|
494
|
+
instances: workers,
|
|
506
495
|
budgetRemaining,
|
|
507
496
|
};
|
|
508
497
|
|
|
509
|
-
// Route via orchestrator
|
|
498
|
+
// Route via orchestrator
|
|
510
499
|
const output = await this.orchestrator.route(input);
|
|
511
500
|
|
|
512
501
|
// Log routing decisions to event log for dashboard visibility
|
|
@@ -518,20 +507,14 @@ export class SwarmController {
|
|
|
518
507
|
});
|
|
519
508
|
}
|
|
520
509
|
|
|
521
|
-
//
|
|
522
|
-
for (const sa of output.scaleActions) {
|
|
523
|
-
const profile = profiles.find((p) => p.id === sa.profileId);
|
|
524
|
-
if (profile) {
|
|
525
|
-
this.instanceManager.scaleTo(profile, sa.targetInstances);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
510
|
+
// No scale actions in pool model — pool size is fixed at maxConcurrent
|
|
528
511
|
|
|
529
512
|
// Build file conflict map: which files are being modified by currently executing tasks
|
|
530
513
|
const activeFiles = new Map<string, string>(); // filepath → taskId
|
|
531
|
-
for (const [
|
|
532
|
-
const
|
|
533
|
-
if (
|
|
534
|
-
const runningTask = await this.taskStore.get(
|
|
514
|
+
for (const [workerId] of this.executionPromises) {
|
|
515
|
+
const w = this.instanceManager.listAll().find(i => i.instanceId === workerId);
|
|
516
|
+
if (w?.currentTaskId) {
|
|
517
|
+
const runningTask = await this.taskStore.get(w.currentTaskId);
|
|
535
518
|
if (runningTask?.context.files) {
|
|
536
519
|
for (const f of runningTask.context.files) {
|
|
537
520
|
activeFiles.set(f, runningTask.id);
|
|
@@ -564,9 +547,9 @@ export class SwarmController {
|
|
|
564
547
|
// Assign task in TaskStore
|
|
565
548
|
await this.taskStore.assignToInstance(assignment.taskId, assignment.instanceId, assignment.profileId);
|
|
566
549
|
|
|
567
|
-
// Mark
|
|
550
|
+
// Mark worker as executing with the task's profileId
|
|
568
551
|
const runId = RunStore.newId();
|
|
569
|
-
this.instanceManager.markExecuting(assignment.instanceId, assignment.taskId, runId);
|
|
552
|
+
this.instanceManager.markExecuting(assignment.instanceId, assignment.taskId, runId, assignment.profileId);
|
|
570
553
|
await this.taskStore.update(assignment.taskId, { currentRunId: runId });
|
|
571
554
|
|
|
572
555
|
// Sync state for dashboard
|
|
@@ -581,10 +564,10 @@ export class SwarmController {
|
|
|
581
564
|
|
|
582
565
|
// Spawn execution (non-blocking).
|
|
583
566
|
// Track settlement so the cleanup loop can remove finished promises.
|
|
584
|
-
const
|
|
567
|
+
const workerIdForClosure = assignment.instanceId;
|
|
585
568
|
const execPromise = this._executeTask(assignment.instanceId, assignment.taskId, runId, bot, profile)
|
|
586
569
|
.catch(() => { /* errors handled inside _executeTask */ })
|
|
587
|
-
.finally(() => { this.settledPromises.add(
|
|
570
|
+
.finally(() => { this.settledPromises.add(workerIdForClosure); });
|
|
588
571
|
this.executionPromises.set(assignment.instanceId, execPromise);
|
|
589
572
|
} catch {
|
|
590
573
|
// Assignment failed (task may have been grabbed by another cycle) — skip
|
|
@@ -605,10 +588,10 @@ export class SwarmController {
|
|
|
605
588
|
// Clean up completed execution promises.
|
|
606
589
|
// Use a status-tracking wrapper instead of Promise.race with an
|
|
607
590
|
// immediately-resolved promise (which always returns 'pending').
|
|
608
|
-
for (const [
|
|
609
|
-
if (this.settledPromises.has(
|
|
610
|
-
this.executionPromises.delete(
|
|
611
|
-
this.settledPromises.delete(
|
|
591
|
+
for (const [workerId] of this.executionPromises) {
|
|
592
|
+
if (this.settledPromises.has(workerId)) {
|
|
593
|
+
this.executionPromises.delete(workerId);
|
|
594
|
+
this.settledPromises.delete(workerId);
|
|
612
595
|
}
|
|
613
596
|
}
|
|
614
597
|
|
|
@@ -634,7 +617,7 @@ export class SwarmController {
|
|
|
634
617
|
// -----------------------------------------------------------------------
|
|
635
618
|
|
|
636
619
|
private async _executeTask(
|
|
637
|
-
|
|
620
|
+
workerId: string,
|
|
638
621
|
taskId: string,
|
|
639
622
|
runId: string,
|
|
640
623
|
bot: BotRegistration,
|
|
@@ -655,10 +638,15 @@ export class SwarmController {
|
|
|
655
638
|
// Wrap in the JSON envelope that receive-task and downstream nodes expect.
|
|
656
639
|
// autoApprove: true because the user consented by starting the swarm.
|
|
657
640
|
const taskJson = JSON.stringify({
|
|
641
|
+
id: task.id,
|
|
642
|
+
parentId: task.parentId,
|
|
658
643
|
instruction: prompt,
|
|
659
644
|
mode: task.context.files.length > 0 ? 'modify' : 'create',
|
|
660
645
|
targets: task.context.files.length > 0 ? task.context.files : undefined,
|
|
661
646
|
options: { autoApprove: true },
|
|
647
|
+
attempt: task.attempt,
|
|
648
|
+
lastError: task.context.lastError,
|
|
649
|
+
runSummaries: task.context.runSummaries,
|
|
662
650
|
});
|
|
663
651
|
|
|
664
652
|
// Build behavior config from profile preferences, adjusted for task complexity.
|
|
@@ -683,7 +671,7 @@ export class SwarmController {
|
|
|
683
671
|
runWorkflow(workflowPath, {
|
|
684
672
|
runId,
|
|
685
673
|
taskId,
|
|
686
|
-
botId:
|
|
674
|
+
botId: workerId,
|
|
687
675
|
config: { provider: 'auto' },
|
|
688
676
|
params: { taskJson, projectDir: this.projectDir, behaviorJson },
|
|
689
677
|
eventLog: runEventLog,
|
|
@@ -708,9 +696,10 @@ export class SwarmController {
|
|
|
708
696
|
|
|
709
697
|
const runSummary: RunSummary = {
|
|
710
698
|
runId,
|
|
711
|
-
botId:
|
|
699
|
+
botId: workerId,
|
|
712
700
|
outcome: result.success ? 'success' : 'failed',
|
|
713
701
|
summary: result.summary || (result.success ? 'Completed' : 'Failed'),
|
|
702
|
+
report: result.report,
|
|
714
703
|
filesModified: [],
|
|
715
704
|
error: result.success ? undefined : (result.summary || 'Unknown error'),
|
|
716
705
|
durationMs,
|
|
@@ -723,7 +712,7 @@ export class SwarmController {
|
|
|
723
712
|
await this.taskStore.release(taskId, releaseStatus, runSummary);
|
|
724
713
|
|
|
725
714
|
// Record token usage
|
|
726
|
-
this.recordTokenUsage(
|
|
715
|
+
this.recordTokenUsage(workerId, taskId, tokensUsed, costUsed);
|
|
727
716
|
|
|
728
717
|
// Update stats
|
|
729
718
|
if (result.success) {
|
|
@@ -732,16 +721,16 @@ export class SwarmController {
|
|
|
732
721
|
this.state.tasksFailed += 1;
|
|
733
722
|
}
|
|
734
723
|
|
|
735
|
-
// Mark
|
|
724
|
+
// Mark worker as idle (clears profile association)
|
|
736
725
|
try {
|
|
737
|
-
this.instanceManager.markIdle(
|
|
738
|
-
} catch { /*
|
|
726
|
+
this.instanceManager.markIdle(workerId, result.success);
|
|
727
|
+
} catch { /* worker may have been stopped */ }
|
|
739
728
|
|
|
740
729
|
// Emit swarm-level task event
|
|
741
730
|
this.eventLog.emit({
|
|
742
731
|
type: result.success ? 'task-done' : 'task-failed',
|
|
743
732
|
timestamp: Date.now(),
|
|
744
|
-
data: { botId:
|
|
733
|
+
data: { botId: workerId, taskId, outcome: runSummary.outcome },
|
|
745
734
|
});
|
|
746
735
|
|
|
747
736
|
// Emit hierarchy-scoped event so sibling tasks can see what happened
|
|
@@ -755,7 +744,7 @@ export class SwarmController {
|
|
|
755
744
|
data: {
|
|
756
745
|
summary: runSummary.summary,
|
|
757
746
|
filesModified: runSummary.filesModified,
|
|
758
|
-
botId:
|
|
747
|
+
botId: workerId,
|
|
759
748
|
attempt: task.attempt,
|
|
760
749
|
},
|
|
761
750
|
});
|
|
@@ -802,8 +791,8 @@ export class SwarmController {
|
|
|
802
791
|
// Steering
|
|
803
792
|
// -----------------------------------------------------------------------
|
|
804
793
|
|
|
805
|
-
private async _checkSteering(
|
|
806
|
-
const steerPath = path.join(this.weaverDir, `steer-${
|
|
794
|
+
private async _checkSteering(workerId: string): Promise<void> {
|
|
795
|
+
const steerPath = path.join(this.weaverDir, `steer-${workerId}.json`);
|
|
807
796
|
try {
|
|
808
797
|
if (!fs.existsSync(steerPath)) return;
|
|
809
798
|
const raw = fs.readFileSync(steerPath, 'utf-8');
|
|
@@ -812,28 +801,28 @@ export class SwarmController {
|
|
|
812
801
|
|
|
813
802
|
switch (command.command) {
|
|
814
803
|
case 'pause':
|
|
815
|
-
this.eventLog.emit({ type: 'bot-paused', timestamp: Date.now(), data: { botId:
|
|
804
|
+
this.eventLog.emit({ type: 'bot-paused', timestamp: Date.now(), data: { botId: workerId } });
|
|
816
805
|
break;
|
|
817
806
|
case 'resume':
|
|
818
807
|
break;
|
|
819
808
|
case 'cancel':
|
|
820
|
-
this.instanceManager.stop(
|
|
809
|
+
this.instanceManager.stop(workerId);
|
|
821
810
|
break;
|
|
822
811
|
}
|
|
823
812
|
} catch { /* steer file read error — skip */ }
|
|
824
813
|
}
|
|
825
814
|
|
|
826
815
|
// -----------------------------------------------------------------------
|
|
827
|
-
// State sync — instances field
|
|
816
|
+
// State sync — instances field (workers mapped to instances for API compat)
|
|
828
817
|
// -----------------------------------------------------------------------
|
|
829
818
|
|
|
830
819
|
private _syncInstancesState(): void {
|
|
831
|
-
const
|
|
820
|
+
const allWorkers = this.instanceManager.listAll();
|
|
832
821
|
|
|
833
|
-
// Build instances record
|
|
822
|
+
// Build instances record from worker pool
|
|
834
823
|
this.state.instances = {};
|
|
835
|
-
for (const
|
|
836
|
-
this.state.instances[
|
|
824
|
+
for (const w of allWorkers) {
|
|
825
|
+
this.state.instances[w.instanceId] = w;
|
|
837
826
|
}
|
|
838
827
|
}
|
|
839
828
|
|
package/src/bot/system-prompt.ts
CHANGED
|
@@ -208,6 +208,8 @@ USE TOOLS to complete tasks. Do NOT describe what you would do — actually do i
|
|
|
208
208
|
|
|
209
209
|
### Knowledge tools
|
|
210
210
|
- learn(key, value), recall(query)
|
|
211
|
+
- When you discover project facts (test runner, build tool, conventions, common patterns), use learn() to persist them for future runs
|
|
212
|
+
- Previous knowledge is auto-loaded into your context — avoid re-discovering what's already known
|
|
211
213
|
|
|
212
214
|
### Web & interaction tools
|
|
213
215
|
- web_fetch(url), ask_user(question)
|
package/src/bot/task-types.ts
CHANGED
package/src/bot/weaver-tools.ts
CHANGED
|
@@ -30,9 +30,15 @@ const OPERATION_MAP: Record<string, string> = {
|
|
|
30
30
|
*/
|
|
31
31
|
export function createWeaverExecutor(projectDir: string) {
|
|
32
32
|
return async (
|
|
33
|
-
|
|
33
|
+
rawName: string,
|
|
34
34
|
args: Record<string, unknown>,
|
|
35
35
|
): Promise<{ result: string; isError: boolean }> => {
|
|
36
|
+
// Strip MCP server prefix from tool names.
|
|
37
|
+
// The Claude CLI registers MCP tools as `mcp__<server>__<tool>` and the
|
|
38
|
+
// API streams back tool_use events with the prefixed name. The agent loop
|
|
39
|
+
// passes this name to the executor, but our switch cases use unprefixed names.
|
|
40
|
+
const name = rawName.replace(/^mcp__[a-zA-Z0-9_-]+__/, '');
|
|
41
|
+
|
|
36
42
|
// Handle new tools that bypass step-executor
|
|
37
43
|
switch (name) {
|
|
38
44
|
case 'web_fetch': {
|
|
@@ -179,6 +185,13 @@ export function createWeaverExecutor(projectDir: string) {
|
|
|
179
185
|
break;
|
|
180
186
|
}
|
|
181
187
|
|
|
188
|
+
// Ignore Claude Code built-in tools that leak through when disallowed list is incomplete.
|
|
189
|
+
// These are no-ops — the bot should use the MCP bridge tools instead.
|
|
190
|
+
const IGNORED_BUILTINS = new Set(['TodoWrite', 'TodoRead', 'Agent', 'WebFetch', 'WebSearch', 'MultiTool', 'Skill']);
|
|
191
|
+
if (IGNORED_BUILTINS.has(name) || IGNORED_BUILTINS.has(rawName)) {
|
|
192
|
+
return { result: `Tool "${name}" is not available. Use the provided tools instead.`, isError: true };
|
|
193
|
+
}
|
|
194
|
+
|
|
182
195
|
// Existing step-executor-based tools
|
|
183
196
|
const operation = OPERATION_MAP[name];
|
|
184
197
|
if (!operation) {
|
|
@@ -153,8 +153,8 @@ export async function weaverAgentExecute(
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
const taskPrompt = task.instruction.startsWith('## Task:')
|
|
156
|
-
? `${task.instruction}
|
|
157
|
-
: `Task: ${task.instruction}\
|
|
156
|
+
? `${task.instruction}`
|
|
157
|
+
: `Task: ${task.instruction}\n${task.targets ? 'Target files: ' + task.targets.join(', ') : ''}`;
|
|
158
158
|
|
|
159
159
|
// Create renderer — single source of all terminal output
|
|
160
160
|
const { TerminalRenderer } = await import('../bot/terminal-renderer.js');
|
|
@@ -27,7 +27,10 @@ export async function weaverBotReport(
|
|
|
27
27
|
abortCtx?: string,
|
|
28
28
|
failCtx?: string,
|
|
29
29
|
): Promise<{ onSuccess: boolean; onFailure: boolean; summary: string; report: string; reportJson: string }> {
|
|
30
|
-
|
|
30
|
+
// Pick the first ctx with meaningful content (not just '{}').
|
|
31
|
+
// An empty merge result '{}' must not shadow a real ctx from failCtx/abortCtx.
|
|
32
|
+
const candidates = [mainCtx, readCtx, abortCtx, failCtx];
|
|
33
|
+
const ctxStr = candidates.find(c => c != null && c !== '{}' && c.length > 2) ?? candidates.find(c => c != null);
|
|
31
34
|
|
|
32
35
|
if (!execute) {
|
|
33
36
|
const reportObj = { task: {}, path: 'unknown', result: null, filesModified: [], gitResult: null, timestamp: Date.now() };
|
|
@@ -96,7 +99,7 @@ export async function weaverBotReport(
|
|
|
96
99
|
} catch { /* ignore parse errors */ }
|
|
97
100
|
}
|
|
98
101
|
|
|
99
|
-
const success = result?.success !== false && pathName !== 'abort' && reviewPassed;
|
|
102
|
+
const success = result?.success !== false && pathName !== 'abort' && pathName !== 'failed' && context.allValid !== false && reviewPassed;
|
|
100
103
|
|
|
101
104
|
if (result) {
|
|
102
105
|
parts.push(`Outcome: ${result.outcome ?? (success ? 'completed' : 'failed')}`);
|
|
@@ -45,6 +45,17 @@ export function weaverBuildContext(ctx: string): { ctx: string } {
|
|
|
45
45
|
}
|
|
46
46
|
} catch { /* non-fatal — memory is best-effort */ }
|
|
47
47
|
|
|
48
|
+
// Auto-recall learned knowledge from previous bot runs
|
|
49
|
+
try {
|
|
50
|
+
const { KnowledgeStore } = require('../bot/knowledge-store.js') as typeof import('../bot/knowledge-store.js');
|
|
51
|
+
const knowledge = new KnowledgeStore(projectDir);
|
|
52
|
+
const entries = knowledge.list();
|
|
53
|
+
if (entries.length > 0) {
|
|
54
|
+
const knowledgeLines = entries.map((e: { key: string; value: string }) => `- **${e.key}**: ${e.value}`);
|
|
55
|
+
sections.push(`## Learned Knowledge\n\nFacts discovered by previous runs — use these instead of re-discovering:\n${knowledgeLines.join('\n')}`);
|
|
56
|
+
}
|
|
57
|
+
} catch { /* non-fatal — knowledge recall is best-effort */ }
|
|
58
|
+
|
|
48
59
|
// Include sibling context from hierarchy events (previous task completions in same hierarchy)
|
|
49
60
|
try {
|
|
50
61
|
const parsedTask = JSON.parse(context.taskJson!) as { parentId?: string };
|
|
@@ -92,10 +103,11 @@ function buildModifyContext(projectDir: string, targets: string[]): string[] {
|
|
|
92
103
|
const includedFiles = new Set<string>();
|
|
93
104
|
for (const target of targets) {
|
|
94
105
|
const filePath = path.isAbsolute(target) ? target : path.resolve(projectDir, target);
|
|
106
|
+
const displayTarget = path.isAbsolute(target) ? path.relative(projectDir, target) : target;
|
|
95
107
|
try {
|
|
96
108
|
if (!fs.existsSync(filePath)) continue;
|
|
97
109
|
const source = fs.readFileSync(filePath, 'utf-8');
|
|
98
|
-
sections.push(`## Target: ${
|
|
110
|
+
sections.push(`## Target: ${displayTarget}\n\n\`\`\`typescript\n${source}\n\`\`\``);
|
|
99
111
|
includedFiles.add(filePath);
|
|
100
112
|
|
|
101
113
|
// Extract import paths to find referenced node type files
|