@synergenius/flow-weaver-pack-weaver 0.9.34 → 0.9.35
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 +140 -79
- package/dist/ai-chat-provider.js.map +1 -1
- package/dist/bot/agent-loop.d.ts +20 -0
- package/dist/bot/agent-loop.d.ts.map +1 -0
- package/dist/bot/agent-loop.js +331 -0
- package/dist/bot/agent-loop.js.map +1 -0
- package/dist/bot/run-registry.d.ts +41 -0
- package/dist/bot/run-registry.d.ts.map +1 -0
- package/dist/bot/run-registry.js +53 -0
- package/dist/bot/run-registry.js.map +1 -0
- package/dist/bot/runner.d.ts.map +1 -1
- package/dist/bot/runner.js +12 -2
- package/dist/bot/runner.js.map +1 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +749 -0
- package/dist/cli.js.map +1 -0
- package/dist/docs/weaver-config.md +15 -9
- package/dist/templates/weaver-template.d.ts +11 -0
- package/dist/templates/weaver-template.d.ts.map +1 -0
- package/dist/templates/weaver-template.js +53 -0
- package/dist/templates/weaver-template.js.map +1 -0
- package/dist/workflows/weaver-bot-session.d.ts +65 -0
- package/dist/workflows/weaver-bot-session.d.ts.map +1 -0
- package/dist/workflows/weaver-bot-session.js +68 -0
- package/dist/workflows/weaver-bot-session.js.map +1 -0
- package/dist/workflows/weaver.d.ts +24 -0
- package/dist/workflows/weaver.d.ts.map +1 -0
- package/dist/workflows/weaver.js +28 -0
- package/dist/workflows/weaver.js.map +1 -0
- package/package.json +1 -1
- package/src/ai-chat-provider.ts +129 -81
- package/src/bot/run-registry.ts +71 -0
- package/src/bot/runner.ts +10 -2
- package/dist/docs/weaver-bot-usage.md +0 -34
- package/dist/docs/weaver-genesis.md +0 -32
- package/dist/docs/weaver-task-queue.md +0 -34
package/src/ai-chat-provider.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { runWorkflow } from './bot/runner.js';
|
|
|
12
12
|
import { RunStore } from './bot/run-store.js';
|
|
13
13
|
import { CostStore } from './bot/cost-store.js';
|
|
14
14
|
import { defaultRegistry, discoverProviders } from './bot/provider-registry.js';
|
|
15
|
+
import { runRegistry } from './bot/run-registry.js';
|
|
15
16
|
|
|
16
17
|
interface AiChatToolContext {
|
|
17
18
|
workspacePath: string;
|
|
@@ -137,17 +138,21 @@ const toolHandlers: Record<
|
|
|
137
138
|
// Fire-and-forget: start workflow in background, events stream via EventLog
|
|
138
139
|
eventLog.emit({ type: 'bot-started', timestamp: Date.now(), data: { instruction: task.instruction, mode: task.mode, runId } });
|
|
139
140
|
|
|
140
|
-
runWorkflow
|
|
141
|
+
// runWorkflow registers itself in runRegistry via runner.ts.
|
|
142
|
+
// The .catch ensures EventLog is finalized for errors before runner's try/catch.
|
|
143
|
+
const runPromise = runWorkflow(workflowPath, {
|
|
141
144
|
params: { taskJson: JSON.stringify(task), projectDir },
|
|
142
145
|
dryRun: args.dryRun as boolean | undefined,
|
|
143
146
|
eventLog,
|
|
144
147
|
}).catch((err) => {
|
|
145
|
-
//
|
|
146
|
-
|
|
147
|
-
eventLog.done();
|
|
148
|
+
// Finalize EventLog for early errors that throw before runner.ts's try/finally
|
|
149
|
+
try { eventLog.done(); } catch { /* already done */ }
|
|
148
150
|
console.error('[weaver-bot] Background execution failed:', err);
|
|
149
151
|
});
|
|
150
152
|
|
|
153
|
+
// Keep a reference so we don't lose track of it
|
|
154
|
+
void runPromise;
|
|
155
|
+
|
|
151
156
|
return JSON.stringify({ runId, instruction: task.instruction, status: 'started' });
|
|
152
157
|
},
|
|
153
158
|
|
|
@@ -195,9 +200,41 @@ const toolHandlers: Record<
|
|
|
195
200
|
|
|
196
201
|
async fw_weaver_status() {
|
|
197
202
|
const { SessionStore } = await import('./bot/session-state.js');
|
|
203
|
+
const { EventLog } = await import('./bot/event-log.js');
|
|
198
204
|
const store = new SessionStore();
|
|
199
205
|
const state = store.load();
|
|
200
|
-
|
|
206
|
+
|
|
207
|
+
if (!state) {
|
|
208
|
+
return JSON.stringify({ status: 'no active session' }, null, 2);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Startup cleanup: on first call after process restart, the registry is empty.
|
|
212
|
+
// If session.json claims 'executing' but nothing is in the registry, heal it.
|
|
213
|
+
if (!runRegistry.startupCleanupDone) {
|
|
214
|
+
runRegistry.markStartupCleanupDone();
|
|
215
|
+
if (state.status !== 'idle' && runRegistry.size === 0) {
|
|
216
|
+
// Orphaned session from a previous process — heal it
|
|
217
|
+
const orphanRunId = state.currentRunId;
|
|
218
|
+
if (orphanRunId) {
|
|
219
|
+
try { new EventLog(orphanRunId).done(); } catch { /* non-fatal */ }
|
|
220
|
+
}
|
|
221
|
+
await store.update({ status: 'idle', currentTask: null, currentRunId: null });
|
|
222
|
+
return JSON.stringify({ ...state, status: 'idle', currentTask: null, currentRunId: null, alive: false }, null, 2);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Check if the current run is actually alive in the registry
|
|
227
|
+
const currentRunId = state.currentRunId ?? null;
|
|
228
|
+
const alive = currentRunId ? runRegistry.isAlive(currentRunId) : state.status === 'idle' ? null : runRegistry.size > 0;
|
|
229
|
+
|
|
230
|
+
// Self-heal: if status says executing but run is dead, clean up
|
|
231
|
+
if (currentRunId && alive === false && state.status !== 'idle') {
|
|
232
|
+
try { new EventLog(currentRunId).done(); } catch { /* non-fatal */ }
|
|
233
|
+
await store.update({ status: 'idle', currentTask: null, currentRunId: null });
|
|
234
|
+
return JSON.stringify({ ...state, status: 'idle', currentTask: null, currentRunId: null, alive: false }, null, 2);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return JSON.stringify({ ...state, alive }, null, 2);
|
|
201
238
|
},
|
|
202
239
|
|
|
203
240
|
async fw_weaver_events(args) {
|
|
@@ -208,7 +245,14 @@ const toolHandlers: Record<
|
|
|
208
245
|
const offset = (args.offset as number) ?? 0;
|
|
209
246
|
const log = new EventLog(runId);
|
|
210
247
|
const events = log.tail(offset);
|
|
211
|
-
|
|
248
|
+
let done = log.isDone();
|
|
249
|
+
|
|
250
|
+
// Self-healing: if the run is not in the registry and has no .done marker,
|
|
251
|
+
// it's a zombie. Write the .done marker so this is the last poll.
|
|
252
|
+
if (!done && !runRegistry.isAlive(runId)) {
|
|
253
|
+
try { log.done(); } catch { /* non-fatal */ }
|
|
254
|
+
done = true;
|
|
255
|
+
}
|
|
212
256
|
|
|
213
257
|
return JSON.stringify({ events, done });
|
|
214
258
|
},
|
|
@@ -251,9 +295,10 @@ const toolHandlers: Record<
|
|
|
251
295
|
|
|
252
296
|
if (action === 'status') {
|
|
253
297
|
const state = store.load();
|
|
254
|
-
return
|
|
255
|
-
|
|
256
|
-
|
|
298
|
+
if (!state) return JSON.stringify({ status: 'no active session' });
|
|
299
|
+
const crid = state.currentRunId ?? null;
|
|
300
|
+
const alive = crid ? runRegistry.isAlive(crid) : null;
|
|
301
|
+
return JSON.stringify({ ...state, alive }, null, 2);
|
|
257
302
|
}
|
|
258
303
|
|
|
259
304
|
if (action === 'stop') {
|
|
@@ -310,92 +355,95 @@ const toolHandlers: Record<
|
|
|
310
355
|
let completed = 0;
|
|
311
356
|
let failed = 0;
|
|
312
357
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
// Check for cancel signal
|
|
317
|
-
try {
|
|
318
|
-
const { SteeringController } = await import('./bot/steering.js');
|
|
319
|
-
const ctrl = new SteeringController();
|
|
320
|
-
const signal = await ctrl.check();
|
|
321
|
-
if (signal?.command === 'cancel') {
|
|
322
|
-
sessionLog.emit({ type: 'session:cancelled', timestamp: Date.now() });
|
|
323
|
-
break;
|
|
324
|
-
}
|
|
325
|
-
} catch { /* non-fatal */ }
|
|
326
|
-
|
|
327
|
-
const runId = RunStore.newId();
|
|
328
|
-
const taskLog = new EventLog(runId);
|
|
329
|
-
|
|
330
|
-
// Mark task as running in the queue
|
|
331
|
-
try { await queue.markRunning(task.id); } catch { /* non-fatal */ }
|
|
358
|
+
try {
|
|
359
|
+
for (let i = 0; i < Math.min(maxTasks, pending.length); i++) {
|
|
360
|
+
const task = pending[i]!;
|
|
332
361
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
362
|
+
// Check for cancel signal
|
|
363
|
+
try {
|
|
364
|
+
const { SteeringController } = await import('./bot/steering.js');
|
|
365
|
+
const ctrl = new SteeringController();
|
|
366
|
+
const signal = await ctrl.check();
|
|
367
|
+
if (signal?.command === 'cancel') {
|
|
368
|
+
sessionLog.emit({ type: 'session:cancelled', timestamp: Date.now() });
|
|
369
|
+
break;
|
|
370
|
+
}
|
|
371
|
+
} catch { /* non-fatal */ }
|
|
339
372
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
}});
|
|
373
|
+
const runId = RunStore.newId();
|
|
374
|
+
const taskLog = new EventLog(runId);
|
|
343
375
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}});
|
|
376
|
+
// Mark task as running in the queue
|
|
377
|
+
try { await queue.markRunning(task.id); } catch { /* non-fatal */ }
|
|
347
378
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
},
|
|
354
|
-
eventLog: taskLog,
|
|
379
|
+
await store.update({
|
|
380
|
+
status: 'executing',
|
|
381
|
+
currentTask: task.instruction,
|
|
382
|
+
currentRunId: runId,
|
|
383
|
+
lastActivity: Date.now(),
|
|
355
384
|
});
|
|
356
385
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
386
|
+
sessionLog.emit({ type: 'session:task-start', timestamp: Date.now(), data: {
|
|
387
|
+
taskId: runId, instruction: task.instruction, index: i, total: Math.min(maxTasks, pending.length),
|
|
388
|
+
}});
|
|
360
389
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
cost: result.cost?.totalCost,
|
|
390
|
+
taskLog.emit({ type: 'bot-started', timestamp: Date.now(), data: {
|
|
391
|
+
instruction: task.instruction, mode: 'create', runId,
|
|
364
392
|
}});
|
|
365
393
|
|
|
366
|
-
// Mark task in queue
|
|
367
394
|
try {
|
|
368
|
-
await
|
|
369
|
-
|
|
395
|
+
const result = await runWorkflow(workflowPath, {
|
|
396
|
+
params: {
|
|
397
|
+
taskJson: JSON.stringify({ instruction: task.instruction, mode: 'create' }),
|
|
398
|
+
projectDir,
|
|
399
|
+
},
|
|
400
|
+
eventLog: taskLog,
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
const success = result.success;
|
|
404
|
+
completed += success ? 1 : 0;
|
|
405
|
+
failed += success ? 0 : 1;
|
|
406
|
+
|
|
407
|
+
sessionLog.emit({ type: 'session:task-complete', timestamp: Date.now(), data: {
|
|
408
|
+
taskId: runId, outcome: result.outcome, duration: result.executionTime,
|
|
409
|
+
cost: result.cost?.totalCost,
|
|
410
|
+
}});
|
|
411
|
+
|
|
412
|
+
// Mark task in queue
|
|
413
|
+
try {
|
|
414
|
+
await queue.remove(task.id);
|
|
415
|
+
} catch { /* non-fatal */ }
|
|
416
|
+
|
|
417
|
+
} catch (err) {
|
|
418
|
+
failed++;
|
|
419
|
+
sessionLog.emit({ type: 'session:task-complete', timestamp: Date.now(), data: {
|
|
420
|
+
taskId: runId, outcome: 'error', error: String(err),
|
|
421
|
+
}});
|
|
422
|
+
}
|
|
370
423
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
}
|
|
424
|
+
await store.update({
|
|
425
|
+
completedTasks: completed + failed,
|
|
426
|
+
currentRunId: null,
|
|
427
|
+
lastActivity: Date.now(),
|
|
428
|
+
});
|
|
376
429
|
}
|
|
377
430
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
431
|
+
sessionLog.emit({ type: 'session:ended', timestamp: Date.now(), data: {
|
|
432
|
+
reason: 'complete', completed, failed, total: completed + failed,
|
|
433
|
+
}});
|
|
434
|
+
} catch (err) {
|
|
435
|
+
console.error('[weaver-session] Session failed:', err);
|
|
436
|
+
sessionLog.emit({ type: 'session:ended', timestamp: Date.now(), data: {
|
|
437
|
+
reason: 'error', error: String(err),
|
|
438
|
+
}});
|
|
439
|
+
} finally {
|
|
440
|
+
// GUARANTEED: session log finalized and state reset, even on crash
|
|
441
|
+
try { sessionLog.done(); } catch { /* already done */ }
|
|
442
|
+
try {
|
|
443
|
+
await store.update({ status: 'idle', currentTask: null, currentRunId: null });
|
|
444
|
+
} catch { /* non-fatal */ }
|
|
383
445
|
}
|
|
384
|
-
|
|
385
|
-
sessionLog.emit({ type: 'session:ended', timestamp: Date.now(), data: {
|
|
386
|
-
reason: 'complete', completed, failed, total: completed + failed,
|
|
387
|
-
}});
|
|
388
|
-
sessionLog.done();
|
|
389
|
-
|
|
390
|
-
await store.update({ status: 'idle', currentTask: null, currentRunId: null });
|
|
391
|
-
})().catch(async (err) => {
|
|
392
|
-
console.error('[weaver-session] Session failed:', err);
|
|
393
|
-
sessionLog.emit({ type: 'session:ended', timestamp: Date.now(), data: {
|
|
394
|
-
reason: 'error', error: String(err),
|
|
395
|
-
}});
|
|
396
|
-
sessionLog.done();
|
|
397
|
-
await store.update({ status: 'idle', currentTask: null, currentRunId: null });
|
|
398
|
-
});
|
|
446
|
+
})();
|
|
399
447
|
|
|
400
448
|
return JSON.stringify({ started: true, sessionId, taskCount: Math.min(maxTasks, pending.length) });
|
|
401
449
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RunRegistry — in-memory registry of active workflow runs.
|
|
3
|
+
*
|
|
4
|
+
* Module-level singleton. The ai-chat-provider module is cached per workspace
|
|
5
|
+
* by the platform, so this Map persists across all tool calls within a session.
|
|
6
|
+
*
|
|
7
|
+
* Purpose:
|
|
8
|
+
* - Definitive alive/dead check: if a runId is in the registry, it's alive.
|
|
9
|
+
* If not, it's dead (completed, failed, or orphaned).
|
|
10
|
+
* - Self-healing: any read path (fw_weaver_status, fw_weaver_events) can check
|
|
11
|
+
* the registry and clean up stale state as a side effect.
|
|
12
|
+
* - Startup cleanup: on first access after process restart, the registry is empty,
|
|
13
|
+
* so any session.json claiming 'executing' is immediately detectable as orphaned.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export interface ActiveRun {
|
|
17
|
+
/** The workflow file being executed. */
|
|
18
|
+
file: string;
|
|
19
|
+
/** ISO timestamp when the run started. */
|
|
20
|
+
startedAt: string;
|
|
21
|
+
/** The Promise that resolves when the run completes (for awaiting if needed). */
|
|
22
|
+
promise: Promise<unknown>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Module-level singleton — survives across tool calls, cleared on process restart. */
|
|
26
|
+
const registry = new Map<string, ActiveRun>();
|
|
27
|
+
|
|
28
|
+
/** Whether startup cleanup has been performed in this process lifetime. */
|
|
29
|
+
let startupCleanupDone = false;
|
|
30
|
+
|
|
31
|
+
export const runRegistry = {
|
|
32
|
+
/** Register a new active run. */
|
|
33
|
+
register(runId: string, entry: ActiveRun): void {
|
|
34
|
+
registry.set(runId, entry);
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
/** Remove a completed/failed run from the registry. */
|
|
38
|
+
remove(runId: string): void {
|
|
39
|
+
registry.delete(runId);
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
/** Check if a run is currently active (alive). */
|
|
43
|
+
isAlive(runId: string): boolean {
|
|
44
|
+
return registry.has(runId);
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
/** Get the active run entry, or undefined if not alive. */
|
|
48
|
+
get(runId: string): ActiveRun | undefined {
|
|
49
|
+
return registry.get(runId);
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
/** Get all active run IDs. */
|
|
53
|
+
activeRunIds(): string[] {
|
|
54
|
+
return Array.from(registry.keys());
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
/** Number of currently active runs. */
|
|
58
|
+
get size(): number {
|
|
59
|
+
return registry.size;
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
/** Whether startup cleanup has already run in this process lifetime. */
|
|
63
|
+
get startupCleanupDone(): boolean {
|
|
64
|
+
return startupCleanupDone;
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
/** Mark startup cleanup as completed. */
|
|
68
|
+
markStartupCleanupDone(): void {
|
|
69
|
+
startupCleanupDone = true;
|
|
70
|
+
},
|
|
71
|
+
};
|
package/src/bot/runner.ts
CHANGED
|
@@ -26,6 +26,7 @@ import type { NotificationErrorHandler } from './notifications.js';
|
|
|
26
26
|
import { RunStore } from './run-store.js';
|
|
27
27
|
import { CostTracker } from './cost-tracker.js';
|
|
28
28
|
import { CostStore } from './cost-store.js';
|
|
29
|
+
import { runRegistry } from './run-registry.js';
|
|
29
30
|
|
|
30
31
|
function resolveApproval(
|
|
31
32
|
approval: BotConfig['approval'],
|
|
@@ -122,6 +123,11 @@ export async function runWorkflow(
|
|
|
122
123
|
const runId = RunStore.newId();
|
|
123
124
|
const startedAt = new Date().toISOString();
|
|
124
125
|
|
|
126
|
+
// Track this run in the in-memory registry for alive/dead checks
|
|
127
|
+
let registryPromiseResolve: (() => void) | undefined;
|
|
128
|
+
const registryPromise = new Promise<void>((resolve) => { registryPromiseResolve = resolve; });
|
|
129
|
+
runRegistry.register(runId, { file: absPath, startedAt, promise: registryPromise });
|
|
130
|
+
|
|
125
131
|
// Collect execution trace and audit events for persistence
|
|
126
132
|
const collectedTrace: ExecutionEvent[] = [];
|
|
127
133
|
const collectedAudit: AuditEvent[] = [];
|
|
@@ -362,7 +368,6 @@ export async function runWorkflow(
|
|
|
362
368
|
}, verbose);
|
|
363
369
|
|
|
364
370
|
logEvent?.({ type: 'bot-completed', timestamp: Date.now(), data: { success, outcome, summary } });
|
|
365
|
-
options?.eventLog?.done();
|
|
366
371
|
|
|
367
372
|
auditEmit('run-complete', { success, outcome, summary });
|
|
368
373
|
|
|
@@ -395,12 +400,15 @@ export async function runWorkflow(
|
|
|
395
400
|
}, verbose);
|
|
396
401
|
|
|
397
402
|
logEvent?.({ type: 'bot-failed', timestamp: Date.now(), data: { error: msg } });
|
|
398
|
-
options?.eventLog?.done();
|
|
399
403
|
|
|
400
404
|
auditEmit('run-complete', { success: false, error: msg });
|
|
401
405
|
|
|
402
406
|
return { success: false, summary: msg, outcome: 'error', cost: costSummary };
|
|
403
407
|
} finally {
|
|
408
|
+
// GUARANTEED cleanup — runs on success, error, dry-run early return, and crash
|
|
409
|
+
runRegistry.remove(runId);
|
|
410
|
+
registryPromiseResolve?.();
|
|
411
|
+
try { options?.eventLog?.done(); } catch { /* already done or no log */ }
|
|
404
412
|
teardownAuditLogger();
|
|
405
413
|
}
|
|
406
414
|
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
## Weaver Bot
|
|
2
|
-
|
|
3
|
-
The Weaver bot is an autonomous AI agent that creates and modifies Flow Weaver workflows from natural language instructions.
|
|
4
|
-
|
|
5
|
-
### Running the bot
|
|
6
|
-
|
|
7
|
-
Use `fw_weaver_bot` with a task description:
|
|
8
|
-
- `task`: Natural language instruction (required)
|
|
9
|
-
- `mode`: `create` (new workflow), `modify` (edit existing), `read` (analyze), `batch` (multiple tasks)
|
|
10
|
-
- `targets`: File paths for modify/read mode
|
|
11
|
-
- `autoApprove`: Skip the approval gate (default: true in studio)
|
|
12
|
-
|
|
13
|
-
### Execution flow
|
|
14
|
-
|
|
15
|
-
1. **Receive task** — parses instruction and determines mode
|
|
16
|
-
2. **Build context** — gathers project state, templates, and relevant files
|
|
17
|
-
3. **Plan** — AI generates a step-by-step execution plan
|
|
18
|
-
4. **Approval gate** — plan shown for review (skipped if autoApprove)
|
|
19
|
-
5. **Execute + validate + retry** — runs steps, validates output, retries on errors (up to 3 attempts)
|
|
20
|
-
6. **Git ops** — commits changes if successful
|
|
21
|
-
7. **Report** — returns summary with outcome
|
|
22
|
-
|
|
23
|
-
### Steering a running bot
|
|
24
|
-
|
|
25
|
-
Use `fw_weaver_steer` to control execution:
|
|
26
|
-
- `pause` — pause at next safe point
|
|
27
|
-
- `resume` — continue after pause
|
|
28
|
-
- `cancel` — abort execution
|
|
29
|
-
- `redirect` — change task mid-execution
|
|
30
|
-
- `queue` — add a follow-up task
|
|
31
|
-
|
|
32
|
-
### Checking status
|
|
33
|
-
|
|
34
|
-
Use `fw_weaver_status` to see the current bot session state, active task, and completion count.
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
## Genesis Self-Evolution
|
|
2
|
-
|
|
3
|
-
Genesis is Weaver's self-evolution protocol. It autonomously observes, proposes, applies, and validates changes to workflows.
|
|
4
|
-
|
|
5
|
-
### Running a Genesis cycle
|
|
6
|
-
|
|
7
|
-
Use `fw_weaver_genesis` to trigger a single cycle:
|
|
8
|
-
- `projectDir`: Project directory (defaults to workspace)
|
|
9
|
-
- `dryRun`: Preview changes without applying
|
|
10
|
-
|
|
11
|
-
### How it works
|
|
12
|
-
|
|
13
|
-
1. **Observe** — scans project for workflows and their health
|
|
14
|
-
2. **Fingerprint** — detects if workflows changed since last cycle (avoids duplicate proposals)
|
|
15
|
-
3. **Stabilize check** — prevents rapid churn (cooldown between cycles)
|
|
16
|
-
4. **Propose** — AI analyzes project state and proposes modifications within a budget
|
|
17
|
-
5. **Validate proposal** — checks proposed changes are safe
|
|
18
|
-
6. **Snapshot** — creates backup before applying
|
|
19
|
-
7. **Apply** — executes proposed operations via `flow-weaver modify`
|
|
20
|
-
8. **Validate result** — compiles and validates the modified workflow
|
|
21
|
-
9. **Threshold check** — decides if changes meet the quality bar
|
|
22
|
-
10. **Approve** — human approval gate (configurable)
|
|
23
|
-
11. **Commit** — git commit with rollback capability
|
|
24
|
-
12. **Escrow** — data safety pipeline for recovery
|
|
25
|
-
|
|
26
|
-
### Safety features
|
|
27
|
-
|
|
28
|
-
- Pre-apply snapshots for rollback
|
|
29
|
-
- Escrow recovery from previous cycles
|
|
30
|
-
- Fingerprinting prevents duplicate proposals
|
|
31
|
-
- Threshold checking before committing
|
|
32
|
-
- Budget limits on proposed changes
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
## Task Queue & Steering
|
|
2
|
-
|
|
3
|
-
Weaver supports queuing tasks for background processing and steering running bots in real-time.
|
|
4
|
-
|
|
5
|
-
### Task queue
|
|
6
|
-
|
|
7
|
-
Use `fw_weaver_queue` to manage tasks:
|
|
8
|
-
- `add` — add a task to the queue (requires `task` instruction)
|
|
9
|
-
- `list` — show all queued tasks
|
|
10
|
-
- `clear` — remove all pending tasks
|
|
11
|
-
- `remove` — remove a specific task by ID
|
|
12
|
-
|
|
13
|
-
Tasks are stored in NDJSON format and processed sequentially by the bot session.
|
|
14
|
-
|
|
15
|
-
### Steering commands
|
|
16
|
-
|
|
17
|
-
Use `fw_weaver_steer` to control a running bot:
|
|
18
|
-
- `pause` — pause execution at the next safe point
|
|
19
|
-
- `resume` — continue after a pause
|
|
20
|
-
- `cancel` — abort the current task
|
|
21
|
-
- `redirect` — change the task instruction mid-execution (requires `payload` with new instruction)
|
|
22
|
-
- `queue` — add a follow-up task without interrupting the current one
|
|
23
|
-
|
|
24
|
-
### Batch mode
|
|
25
|
-
|
|
26
|
-
For multiple related tasks, use `fw_weaver_bot` with `mode: "batch"` to process them in sequence through the same bot session, sharing context between tasks.
|
|
27
|
-
|
|
28
|
-
### Monitoring
|
|
29
|
-
|
|
30
|
-
Use `fw_weaver_status` to check the current session state:
|
|
31
|
-
- Session phase (idle, planning, executing, validating)
|
|
32
|
-
- Current task instruction
|
|
33
|
-
- Completed task count
|
|
34
|
-
- Error state if any
|