@synergenius/flow-weaver-pack-weaver 0.9.193 → 0.9.195
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/bot/assistant-core.js +2 -2
- package/dist/bot/assistant-core.js.map +1 -1
- package/dist/bot/capability-registry.js +2 -2
- package/dist/bot/capability-registry.js.map +1 -1
- package/dist/bot/context-compactor.d.ts +35 -0
- package/dist/bot/context-compactor.d.ts.map +1 -0
- package/dist/bot/context-compactor.js +130 -0
- package/dist/bot/context-compactor.js.map +1 -0
- package/dist/bot/memory-extraction-worker.d.ts +14 -0
- package/dist/bot/memory-extraction-worker.d.ts.map +1 -0
- package/dist/bot/memory-extraction-worker.js +42 -0
- package/dist/bot/memory-extraction-worker.js.map +1 -0
- package/dist/bot/memory-extractor.d.ts +27 -0
- package/dist/bot/memory-extractor.d.ts.map +1 -0
- package/dist/bot/memory-extractor.js +155 -0
- package/dist/bot/memory-extractor.js.map +1 -0
- package/dist/bot/operations.d.ts +3 -1
- package/dist/bot/operations.d.ts.map +1 -1
- package/dist/bot/operations.js +3 -1
- package/dist/bot/operations.js.map +1 -1
- package/dist/bot/swarm-controller.d.ts +2 -0
- package/dist/bot/swarm-controller.d.ts.map +1 -1
- package/dist/bot/swarm-controller.js +42 -0
- package/dist/bot/swarm-controller.js.map +1 -1
- package/dist/bot/task-prompt-builder.js +35 -21
- package/dist/bot/task-prompt-builder.js.map +1 -1
- package/dist/bot/task-types.d.ts +2 -0
- package/dist/bot/task-types.d.ts.map +1 -1
- package/dist/bot/tool-registry.d.ts +13 -0
- package/dist/bot/tool-registry.d.ts.map +1 -1
- package/dist/bot/tool-registry.js +80 -0
- package/dist/bot/tool-registry.js.map +1 -1
- package/dist/bot/types.d.ts +2 -0
- package/dist/bot/types.d.ts.map +1 -1
- package/dist/node-types/agent-execute.d.ts.map +1 -1
- package/dist/node-types/agent-execute.js +20 -15
- package/dist/node-types/agent-execute.js.map +1 -1
- package/dist/node-types/build-context.d.ts.map +1 -1
- package/dist/node-types/build-context.js +18 -3
- package/dist/node-types/build-context.js.map +1 -1
- package/dist/node-types/receive-task.d.ts +2 -1
- package/dist/node-types/receive-task.d.ts.map +1 -1
- package/dist/node-types/receive-task.js +4 -1
- package/dist/node-types/receive-task.js.map +1 -1
- package/dist/node-types/review-result.d.ts +9 -0
- package/dist/node-types/review-result.d.ts.map +1 -1
- package/dist/node-types/review-result.js +20 -5
- package/dist/node-types/review-result.js.map +1 -1
- package/dist/ui/capability-editor.js +2 -2
- package/dist/ui/profile-editor.js +2 -2
- package/dist/ui/swarm-dashboard.js +2 -2
- package/flowweaver.manifest.json +1 -1
- package/package.json +2 -2
- package/src/bot/assistant-core.ts +2 -2
- package/src/bot/capability-registry.ts +2 -2
- package/src/bot/context-compactor.ts +147 -0
- package/src/bot/memory-extraction-worker.ts +58 -0
- package/src/bot/memory-extractor.ts +213 -0
- package/src/bot/operations.ts +3 -1
- package/src/bot/swarm-controller.ts +43 -0
- package/src/bot/task-prompt-builder.ts +37 -21
- package/src/bot/task-types.ts +2 -0
- package/src/bot/tool-registry.ts +89 -0
- package/src/bot/types.ts +2 -0
- package/src/node-types/agent-execute.ts +25 -15
- package/src/node-types/build-context.ts +19 -3
- package/src/node-types/receive-task.ts +3 -0
- package/src/node-types/review-result.ts +22 -5
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { runAgentLoop, createAnthropicProvider, getOrCreateCliSession, } from '@synergenius/flow-weaver/agent';
|
|
2
2
|
import { createWeaverExecutor } from '../bot/weaver-tools.js';
|
|
3
|
+
/**
|
|
4
|
+
* Strip `<analysis>...</analysis>` scratchpad blocks from LLM response text.
|
|
5
|
+
* The analysis is a reasoning scaffold that improves verdict quality but
|
|
6
|
+
* should not leak into the parsed JSON output.
|
|
7
|
+
*/
|
|
8
|
+
export function stripAnalysis(text) {
|
|
9
|
+
const match = text.match(/<analysis>([\s\S]*?)<\/analysis>/);
|
|
10
|
+
const analysis = match?.[1]?.trim() || undefined;
|
|
11
|
+
const cleaned = text.replace(/<analysis>[\s\S]*?<\/analysis>/g, '').trim();
|
|
12
|
+
return { cleaned, analysis };
|
|
13
|
+
}
|
|
3
14
|
/**
|
|
4
15
|
* LLM-powered task completion reviewer.
|
|
5
16
|
* Makes a single judgment call: did the bot accomplish the assigned task?
|
|
@@ -77,7 +88,9 @@ Rate each criterion as PASS or FAIL:
|
|
|
77
88
|
|
|
78
89
|
If you need to verify file contents to judge the RESULT criterion, use the read_file tool. Only read files if the evidence is ambiguous.
|
|
79
90
|
|
|
80
|
-
|
|
91
|
+
First, write your reasoning inside <analysis> tags. Work through each criterion step by step, examining the evidence.
|
|
92
|
+
|
|
93
|
+
After your <analysis> block, output only the following JSON — no other text outside the tags:
|
|
81
94
|
{"pass": true/false, "intent": "PASS/FAIL", "execution": "PASS/FAIL", "result": "PASS/FAIL", "completeness": "PASS/FAIL", "reason": "one sentence summary"}`;
|
|
82
95
|
try {
|
|
83
96
|
const { env } = context;
|
|
@@ -101,11 +114,13 @@ Respond with exactly:
|
|
|
101
114
|
];
|
|
102
115
|
const executor = createWeaverExecutor(projectDir);
|
|
103
116
|
const result = await runAgentLoop(provider, reviewTools, executor, [{ role: 'user', content: prompt }], { maxIterations: 2 });
|
|
117
|
+
// Strip <analysis> scratchpad before parsing JSON verdict
|
|
118
|
+
const { cleaned: cleanedSummary } = stripAnalysis(result.summary);
|
|
104
119
|
// Parse the structured response
|
|
105
120
|
let pass = true;
|
|
106
121
|
let reason = 'Review completed';
|
|
107
122
|
let criteria = {};
|
|
108
|
-
const jsonMatch =
|
|
123
|
+
const jsonMatch = cleanedSummary.match(/\{[\s\S]*"pass"[\s\S]*\}/);
|
|
109
124
|
if (jsonMatch) {
|
|
110
125
|
try {
|
|
111
126
|
const parsed = JSON.parse(jsonMatch[0]);
|
|
@@ -120,15 +135,15 @@ Respond with exactly:
|
|
|
120
135
|
catch {
|
|
121
136
|
if (jsonMatch[0].includes('"pass": false') || jsonMatch[0].includes('"pass":false')) {
|
|
122
137
|
pass = false;
|
|
123
|
-
reason =
|
|
138
|
+
reason = cleanedSummary.slice(0, 200);
|
|
124
139
|
}
|
|
125
140
|
}
|
|
126
141
|
}
|
|
127
142
|
else {
|
|
128
|
-
const lower =
|
|
143
|
+
const lower = cleanedSummary.toLowerCase();
|
|
129
144
|
if (lower.includes('"pass": false') || lower.includes('"pass":false')) {
|
|
130
145
|
pass = false;
|
|
131
|
-
reason =
|
|
146
|
+
reason = cleanedSummary.slice(0, 200);
|
|
132
147
|
}
|
|
133
148
|
}
|
|
134
149
|
context.reviewJson = JSON.stringify({ pass, reason, ...criteria });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"review-result.js","sourceRoot":"","sources":["../../src/node-types/review-result.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"review-result.js","sourceRoot":"","sources":["../../src/node-types/review-result.ts"],"names":[],"mappings":"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"}
|
|
@@ -35,7 +35,7 @@ var OP_VALIDATE = "validate";
|
|
|
35
35
|
var OP_TSC_CHECK = "tsc_check";
|
|
36
36
|
var OP_RUN_TESTS = "run_tests";
|
|
37
37
|
var OP_TASK_CREATE = "task_create";
|
|
38
|
-
var
|
|
38
|
+
var OP_LEARN = "learn";
|
|
39
39
|
var OP_RECALL = "recall";
|
|
40
40
|
var OP_RESPOND = "respond";
|
|
41
41
|
var PLAN_OPERATIONS = [
|
|
@@ -317,7 +317,7 @@ Omit assignedProfile for auto-routing.`
|
|
|
317
317
|
var CAP_MEMORY = {
|
|
318
318
|
name: "memory",
|
|
319
319
|
description: "Project memory persistence.",
|
|
320
|
-
tools: [
|
|
320
|
+
tools: [OP_LEARN, OP_RECALL],
|
|
321
321
|
prompt: `## Project Memory
|
|
322
322
|
- remember(key, value): Persists a key-value pair to .weaver/project-memory.json.
|
|
323
323
|
- recall(): Returns all saved key-value pairs from project memory.`
|
|
@@ -217,7 +217,7 @@ var OP_VALIDATE = "validate";
|
|
|
217
217
|
var OP_TSC_CHECK = "tsc_check";
|
|
218
218
|
var OP_RUN_TESTS = "run_tests";
|
|
219
219
|
var OP_TASK_CREATE = "task_create";
|
|
220
|
-
var
|
|
220
|
+
var OP_LEARN = "learn";
|
|
221
221
|
var OP_RECALL = "recall";
|
|
222
222
|
|
|
223
223
|
// src/bot/capability-registry.ts
|
|
@@ -488,7 +488,7 @@ Omit assignedProfile for auto-routing.`
|
|
|
488
488
|
var CAP_MEMORY = {
|
|
489
489
|
name: "memory",
|
|
490
490
|
description: "Project memory persistence.",
|
|
491
|
-
tools: [
|
|
491
|
+
tools: [OP_LEARN, OP_RECALL],
|
|
492
492
|
prompt: `## Project Memory
|
|
493
493
|
- remember(key, value): Persists a key-value pair to .weaver/project-memory.json.
|
|
494
494
|
- recall(): Returns all saved key-value pairs from project memory.`
|
|
@@ -2668,7 +2668,7 @@ var OP_VALIDATE = "validate";
|
|
|
2668
2668
|
var OP_TSC_CHECK = "tsc_check";
|
|
2669
2669
|
var OP_RUN_TESTS = "run_tests";
|
|
2670
2670
|
var OP_TASK_CREATE = "task_create";
|
|
2671
|
-
var
|
|
2671
|
+
var OP_LEARN = "learn";
|
|
2672
2672
|
var OP_RECALL = "recall";
|
|
2673
2673
|
|
|
2674
2674
|
// src/bot/capability-registry.ts
|
|
@@ -2939,7 +2939,7 @@ Omit assignedProfile for auto-routing.`
|
|
|
2939
2939
|
var CAP_MEMORY = {
|
|
2940
2940
|
name: "memory",
|
|
2941
2941
|
description: "Project memory persistence.",
|
|
2942
|
-
tools: [
|
|
2942
|
+
tools: [OP_LEARN, OP_RECALL],
|
|
2943
2943
|
prompt: `## Project Memory
|
|
2944
2944
|
- remember(key, value): Persists a key-value pair to .weaver/project-memory.json.
|
|
2945
2945
|
- recall(): Returns all saved key-value pairs from project memory.`
|
package/flowweaver.manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifestVersion": 2,
|
|
3
3
|
"name": "@synergenius/flow-weaver-pack-weaver",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.195",
|
|
5
5
|
"description": "AI bot for Flow Weaver. Execute tasks, run workflows, evolve autonomously.",
|
|
6
6
|
"engineVersion": ">=0.22.10",
|
|
7
7
|
"categories": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synergenius/flow-weaver-pack-weaver",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.195",
|
|
4
4
|
"description": "AI bot for Flow Weaver. Execute tasks, run workflows, evolve autonomously.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"dev:install": "npm run dev && node -e \"const p=process.argv[1]; if(!p){console.error('Usage: npm run dev:install -- /path/to/project');process.exit(1)} require('child_process').execSync('npm install --no-save /tmp/synergenius-flow-weaver-pack-weaver-'+require('./package.json').version+'.tgz',{cwd:p,stdio:'inherit'}); require('child_process').execSync('find '+p+' -name fw-exec-\\* -type f -delete',{stdio:'pipe'}); console.log('✓ Installed in '+p+' (cache cleaned)')\" --"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
|
-
"@synergenius/flow-weaver": "^0.24.
|
|
73
|
+
"@synergenius/flow-weaver": "^0.24.1",
|
|
74
74
|
"@types/node": "^25.3.5",
|
|
75
75
|
"esbuild": "^0.27.4",
|
|
76
76
|
"tsx": "^4.0.0",
|
|
@@ -155,7 +155,7 @@ export async function runAssistant(opts: AssistantOptions): Promise<void> {
|
|
|
155
155
|
|
|
156
156
|
// Run single message, print result, exit
|
|
157
157
|
await runAgentLoop(provider, tools, executor, [{ role: 'user', content: pipeInput }], {
|
|
158
|
-
systemPrompt, maxIterations: 20,
|
|
158
|
+
systemPrompt: { prefix: systemPrompt, suffix: '' }, maxIterations: 20,
|
|
159
159
|
onStreamEvent: (e) => { if (e.type === 'text_delta') out(e.text); },
|
|
160
160
|
onToolEvent: (e) => {
|
|
161
161
|
if (e.type === 'tool_call_start') out(`\n ${c.cyan('◆')} ${e.name}\n`);
|
|
@@ -575,7 +575,7 @@ export async function runAssistant(opts: AssistantOptions): Promise<void> {
|
|
|
575
575
|
executor,
|
|
576
576
|
history, // full conversation history
|
|
577
577
|
{
|
|
578
|
-
systemPrompt,
|
|
578
|
+
systemPrompt: { prefix: systemPrompt, suffix: '' },
|
|
579
579
|
maxIterations: 20,
|
|
580
580
|
onStreamEvent,
|
|
581
581
|
onToolEvent,
|
|
@@ -10,7 +10,7 @@ import type { CapabilityDefinition } from './capability-types.js';
|
|
|
10
10
|
import {
|
|
11
11
|
OP_WRITE_FILE, OP_READ_FILE, OP_PATCH_FILE, OP_LIST_FILES,
|
|
12
12
|
OP_RUN_SHELL, OP_VALIDATE, OP_TSC_CHECK, OP_RUN_TESTS,
|
|
13
|
-
OP_TASK_CREATE,
|
|
13
|
+
OP_TASK_CREATE, OP_LEARN, OP_RECALL,
|
|
14
14
|
} from './operations.js';
|
|
15
15
|
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
@@ -313,7 +313,7 @@ Omit assignedProfile for auto-routing.`,
|
|
|
313
313
|
const CAP_MEMORY: CapabilityDefinition = {
|
|
314
314
|
name: 'memory',
|
|
315
315
|
description: 'Project memory persistence.',
|
|
316
|
-
tools: [
|
|
316
|
+
tools: [OP_LEARN, OP_RECALL],
|
|
317
317
|
prompt: `## Project Memory
|
|
318
318
|
- remember(key, value): Persists a key-value pair to .weaver/project-memory.json.
|
|
319
319
|
- recall(): Returns all saved key-value pairs from project memory.`,
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM-based context compaction for task retries.
|
|
3
|
+
*
|
|
4
|
+
* When a task has 3+ runs, calls a fast-tier model to produce a structured
|
|
5
|
+
* summary of all attempts. The summary replaces verbose run history in
|
|
6
|
+
* the prompt builder, preserving semantic signal (intent, errors, approaches)
|
|
7
|
+
* that cascading truncation would destroy.
|
|
8
|
+
*
|
|
9
|
+
* Uses the <analysis> scratchpad pattern — the model reasons in tags that
|
|
10
|
+
* are stripped before storing the summary.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { Task, RunProgress, CompactedRun } from './task-types.js';
|
|
14
|
+
import type { ProviderInfo } from './types.js';
|
|
15
|
+
import type { CostStrategy } from './profile-types.js';
|
|
16
|
+
|
|
17
|
+
/** Minimum runs before compaction triggers, indexed by cost strategy. */
|
|
18
|
+
const COMPACTION_THRESHOLDS: Record<CostStrategy, number> = {
|
|
19
|
+
frugal: 5,
|
|
20
|
+
balanced: 3,
|
|
21
|
+
performance: 2,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const COMPACTION_SYSTEM_PROMPT = `You are a context compactor for an AI task execution system. Your job is to
|
|
25
|
+
summarize the history of a task's execution runs into a concise briefing for
|
|
26
|
+
the next AI worker that will continue the task.
|
|
27
|
+
|
|
28
|
+
The next worker will read the workspace files directly. Your summary should
|
|
29
|
+
focus on WHAT WAS TRIED and WHY IT DIDN'T FULLY SUCCEED — not on describing
|
|
30
|
+
file contents (the worker will read those).
|
|
31
|
+
|
|
32
|
+
CRITICAL: Respond with TEXT ONLY. Do NOT call any tools. Tool calls will be
|
|
33
|
+
REJECTED and will waste your only turn.
|
|
34
|
+
|
|
35
|
+
Produce a structured summary with these sections. Be terse — total output
|
|
36
|
+
must be under 800 tokens.
|
|
37
|
+
|
|
38
|
+
<analysis>
|
|
39
|
+
Think about what patterns emerge across the runs. What keeps failing? What
|
|
40
|
+
approaches were tried? Is there stagnation?
|
|
41
|
+
</analysis>
|
|
42
|
+
|
|
43
|
+
## Task Intent
|
|
44
|
+
One sentence: what is the task trying to achieve?
|
|
45
|
+
|
|
46
|
+
## Approaches Tried
|
|
47
|
+
Bullet list of distinct approaches attempted across runs, with outcomes.
|
|
48
|
+
|
|
49
|
+
## Key Errors
|
|
50
|
+
Specific error messages or test failures that recurred. Include exact names.
|
|
51
|
+
|
|
52
|
+
## Files Touched
|
|
53
|
+
Files created or modified across all runs (deduplicated).
|
|
54
|
+
|
|
55
|
+
## What Works
|
|
56
|
+
Any partial progress that should be preserved (tests passing, files correct).
|
|
57
|
+
|
|
58
|
+
## Recommended Next Step
|
|
59
|
+
Based on the pattern of failures, what should the next run try differently?`;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Strip `<analysis>...</analysis>` scratchpad from LLM response.
|
|
63
|
+
* Reuses the same pattern as review-result.ts stripAnalysis.
|
|
64
|
+
*/
|
|
65
|
+
function stripAnalysis(text: string): string {
|
|
66
|
+
return text.replace(/<analysis>[\s\S]*?<\/analysis>/g, '').trim();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Build the user prompt from task + run history.
|
|
71
|
+
*/
|
|
72
|
+
export function buildCompactionPrompt(task: Task): string {
|
|
73
|
+
const lines: string[] = [];
|
|
74
|
+
lines.push(`Task: ${task.title}`);
|
|
75
|
+
lines.push(`Description: ${task.description}`);
|
|
76
|
+
lines.push('');
|
|
77
|
+
lines.push('Run history:');
|
|
78
|
+
|
|
79
|
+
for (let i = 0; i < task.context.runHistory.length; i++) {
|
|
80
|
+
const run = task.context.runHistory[i];
|
|
81
|
+
const rp = run as RunProgress;
|
|
82
|
+
lines.push(`Run ${i + 1} (${rp.outcome}):`);
|
|
83
|
+
if (rp.summary) lines.push(` Summary: ${rp.summary}`);
|
|
84
|
+
if (rp.filesCreated?.length) lines.push(` Files created: ${rp.filesCreated.join(', ')}`);
|
|
85
|
+
if (rp.filesModified?.length) lines.push(` Files modified: ${rp.filesModified.join(', ')}`);
|
|
86
|
+
if (rp.remainingWork) lines.push(` Remaining work: ${rp.remainingWork}`);
|
|
87
|
+
if (rp.blockers?.length) lines.push(` Blockers: ${rp.blockers.join(', ')}`);
|
|
88
|
+
lines.push('');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (task.lastAcceptanceCheck) {
|
|
92
|
+
const ac = task.lastAcceptanceCheck;
|
|
93
|
+
const checkLines = ac.results
|
|
94
|
+
.map(r => ` ${r.name}: ${r.pass ? 'PASS' : 'FAIL'}${r.detail ? ` (${r.detail.slice(0, 100)})` : ''}`)
|
|
95
|
+
.join('\n');
|
|
96
|
+
lines.push(`Acceptance check results:\n${checkLines}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
lines.push(`Stagnation count: ${task.context.stagnationCount}`);
|
|
100
|
+
|
|
101
|
+
return lines.join('\n');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Determine if compaction should run for this task.
|
|
106
|
+
*/
|
|
107
|
+
export function shouldCompact(
|
|
108
|
+
task: Task,
|
|
109
|
+
costStrategy: CostStrategy = 'balanced',
|
|
110
|
+
): boolean {
|
|
111
|
+
if (task.context.compactedSummary) return false;
|
|
112
|
+
const threshold = COMPACTION_THRESHOLDS[costStrategy] ?? 3;
|
|
113
|
+
return task.context.runHistory.length >= threshold;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Compact a task's run history into a structured summary via LLM.
|
|
118
|
+
*
|
|
119
|
+
* Returns the compacted summary string, or null if the LLM call fails.
|
|
120
|
+
* Failures are non-fatal — the prompt builder falls back to the existing
|
|
121
|
+
* context decay approach.
|
|
122
|
+
*
|
|
123
|
+
* @param task - The task to compact
|
|
124
|
+
* @param pInfo - Provider info for making the LLM call
|
|
125
|
+
* @param callAIFn - The callAI function (injected for testability)
|
|
126
|
+
*/
|
|
127
|
+
export async function compactRunHistory(
|
|
128
|
+
task: Task,
|
|
129
|
+
pInfo: Pick<ProviderInfo, 'type' | 'apiKey' | 'model' | 'cliBinPath'>,
|
|
130
|
+
callAIFn: (
|
|
131
|
+
pInfo: Pick<ProviderInfo, 'type' | 'apiKey' | 'model' | 'cliBinPath'>,
|
|
132
|
+
systemPrompt: string,
|
|
133
|
+
userPrompt: string,
|
|
134
|
+
maxTokens: number,
|
|
135
|
+
) => Promise<string>,
|
|
136
|
+
): Promise<string | null> {
|
|
137
|
+
try {
|
|
138
|
+
const userPrompt = buildCompactionPrompt(task);
|
|
139
|
+
const response = await callAIFn(pInfo, COMPACTION_SYSTEM_PROMPT, userPrompt, 1024);
|
|
140
|
+
const cleaned = stripAnalysis(response);
|
|
141
|
+
// Sanity check: the cleaned response should have meaningful content
|
|
142
|
+
if (cleaned.length < 20) return null;
|
|
143
|
+
return cleaned;
|
|
144
|
+
} catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fire-and-forget memory extraction worker.
|
|
3
|
+
*
|
|
4
|
+
* Schedules heuristic extraction from completed runs into the KnowledgeStore.
|
|
5
|
+
* Uses AsyncMutex to serialize concurrent writes from parallel bot slots.
|
|
6
|
+
* Errors are caught and logged — never blocks task release.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { AsyncMutex } from './async-mutex.js';
|
|
10
|
+
import { KnowledgeStore } from './knowledge-store.js';
|
|
11
|
+
import { extractMemoryFacts } from './memory-extractor.js';
|
|
12
|
+
import type { Task, RunProgress } from './task-types.js';
|
|
13
|
+
|
|
14
|
+
/** Maximum total knowledge entries before skipping pattern:* writes. */
|
|
15
|
+
const MAX_TOTAL_ENTRIES = 100;
|
|
16
|
+
|
|
17
|
+
const mutex = new AsyncMutex();
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Schedule a fire-and-forget memory extraction.
|
|
21
|
+
* Returns immediately — extraction runs async in the background.
|
|
22
|
+
*/
|
|
23
|
+
export function scheduleMemoryExtraction(
|
|
24
|
+
projectDir: string,
|
|
25
|
+
task: Task,
|
|
26
|
+
runProgress: RunProgress,
|
|
27
|
+
): void {
|
|
28
|
+
_doExtract(projectDir, task, runProgress).catch(err => {
|
|
29
|
+
if (process.env.WEAVER_VERBOSE) {
|
|
30
|
+
console.warn('[swarm] memory extraction failed:', err);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function _doExtract(
|
|
36
|
+
projectDir: string,
|
|
37
|
+
task: Task,
|
|
38
|
+
runProgress: RunProgress,
|
|
39
|
+
): Promise<void> {
|
|
40
|
+
await mutex.runExclusive(async () => {
|
|
41
|
+
const store = new KnowledgeStore(projectDir);
|
|
42
|
+
const existing = store.list();
|
|
43
|
+
const existingKeys = new Set(existing.map(e => e.key));
|
|
44
|
+
|
|
45
|
+
// If over the cap, only allow project:* overwrites (no new pattern:* entries)
|
|
46
|
+
const overCap = existing.length >= MAX_TOTAL_ENTRIES;
|
|
47
|
+
|
|
48
|
+
const facts = extractMemoryFacts(task, runProgress, existingKeys);
|
|
49
|
+
|
|
50
|
+
for (const fact of facts) {
|
|
51
|
+
// Skip pattern:* entries when over cap (project:* overwrites are always allowed)
|
|
52
|
+
if (overCap && fact.key.startsWith('pattern:') && !existingKeys.has(fact.key)) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
store.learn(fact.key, fact.value, `run:${runProgress.runId}`);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heuristic memory extraction from completed bot runs.
|
|
3
|
+
*
|
|
4
|
+
* Pure function — takes structured run data and returns candidate
|
|
5
|
+
* knowledge entries. No I/O, no LLM calls, no side effects.
|
|
6
|
+
*
|
|
7
|
+
* Key namespaces:
|
|
8
|
+
* - project:* — canonical project facts (test runner, build tool, linter, pkg manager)
|
|
9
|
+
* - pattern:blocker:* — recurring blockers
|
|
10
|
+
* - pattern:stagnation:* — stalled task patterns
|
|
11
|
+
* - pattern:success:* — first-run success patterns
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { Task, RunProgress } from './task-types.js';
|
|
15
|
+
|
|
16
|
+
export interface MemoryFact {
|
|
17
|
+
key: string;
|
|
18
|
+
value: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Maximum facts to extract per run. */
|
|
22
|
+
const MAX_FACTS_PER_RUN = 5;
|
|
23
|
+
|
|
24
|
+
/** Maximum key length. */
|
|
25
|
+
const MAX_KEY_LENGTH = 80;
|
|
26
|
+
|
|
27
|
+
/** Maximum value length. */
|
|
28
|
+
const MAX_VALUE_LENGTH = 500;
|
|
29
|
+
|
|
30
|
+
// ── Detection patterns ──────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
interface DetectionRule {
|
|
33
|
+
key: string;
|
|
34
|
+
pattern: RegExp;
|
|
35
|
+
value: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const TEST_RUNNERS: DetectionRule[] = [
|
|
39
|
+
{ key: 'project:test-runner', pattern: /\bvitest\b/i, value: 'vitest' },
|
|
40
|
+
{ key: 'project:test-runner', pattern: /\bjest\b/i, value: 'jest' },
|
|
41
|
+
{ key: 'project:test-runner', pattern: /\bmocha\b/i, value: 'mocha' },
|
|
42
|
+
{ key: 'project:test-runner', pattern: /\bplaywright\b/i, value: 'playwright' },
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
const BUILD_TOOLS: DetectionRule[] = [
|
|
46
|
+
{ key: 'project:build-tool', pattern: /\bvite\b/i, value: 'vite' },
|
|
47
|
+
{ key: 'project:build-tool', pattern: /\bwebpack\b/i, value: 'webpack' },
|
|
48
|
+
{ key: 'project:build-tool', pattern: /\besbuild\b/i, value: 'esbuild' },
|
|
49
|
+
{ key: 'project:build-tool', pattern: /\btsc\b/i, value: 'tsc' },
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
const PKG_MANAGERS: DetectionRule[] = [
|
|
53
|
+
{ key: 'project:pkg-manager', pattern: /\bpnpm\b/i, value: 'pnpm' },
|
|
54
|
+
{ key: 'project:pkg-manager', pattern: /\byarn\b/i, value: 'yarn' },
|
|
55
|
+
{ key: 'project:pkg-manager', pattern: /\bbun\b/i, value: 'bun' },
|
|
56
|
+
{ key: 'project:pkg-manager', pattern: /\bnpm\b/i, value: 'npm' },
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
const LINTERS: DetectionRule[] = [
|
|
60
|
+
{ key: 'project:linter', pattern: /\bbiome\b/i, value: 'biome' },
|
|
61
|
+
{ key: 'project:linter', pattern: /\beslint\b/i, value: 'eslint' },
|
|
62
|
+
{ key: 'project:linter', pattern: /\bprettier\b/i, value: 'prettier' },
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
// ── Helpers ──────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
function truncate(s: string, max: number): string {
|
|
68
|
+
return s.length <= max ? s : s.slice(0, max - 3) + '...';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function normalizeBlockerKey(text: string): string {
|
|
72
|
+
return text
|
|
73
|
+
.toLowerCase()
|
|
74
|
+
.replace(/[^a-z0-9\s-]/g, '')
|
|
75
|
+
.trim()
|
|
76
|
+
.replace(/\s+/g, '-')
|
|
77
|
+
.slice(0, 40);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function searchableText(rp: RunProgress): string {
|
|
81
|
+
const parts = [rp.summary ?? ''];
|
|
82
|
+
if (rp.checks) {
|
|
83
|
+
for (const v of Object.values(rp.checks)) {
|
|
84
|
+
parts.push(String(v));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return parts.join(' ');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function detectFirst(
|
|
91
|
+
text: string,
|
|
92
|
+
rules: DetectionRule[],
|
|
93
|
+
): MemoryFact | undefined {
|
|
94
|
+
for (const rule of rules) {
|
|
95
|
+
if (rule.pattern.test(text)) {
|
|
96
|
+
return { key: rule.key, value: rule.value };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function countExtensions(files: string[]): Record<string, number> {
|
|
103
|
+
const counts: Record<string, number> = {};
|
|
104
|
+
for (const f of files) {
|
|
105
|
+
const ext = f.includes('.') ? '.' + f.split('.').pop() : '(none)';
|
|
106
|
+
counts[ext] = (counts[ext] ?? 0) + 1;
|
|
107
|
+
}
|
|
108
|
+
return counts;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ── Main extraction ─────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Extract generalizable facts from a completed run.
|
|
115
|
+
*
|
|
116
|
+
* @param task - The task that was executed
|
|
117
|
+
* @param rp - The run's progress/result data
|
|
118
|
+
* @param existingKeys - Keys already in the KnowledgeStore (for dedup)
|
|
119
|
+
* @returns Array of facts to persist (max MAX_FACTS_PER_RUN)
|
|
120
|
+
*/
|
|
121
|
+
export function extractMemoryFacts(
|
|
122
|
+
task: Task,
|
|
123
|
+
rp: RunProgress,
|
|
124
|
+
existingKeys: Set<string>,
|
|
125
|
+
): MemoryFact[] {
|
|
126
|
+
const facts: MemoryFact[] = [];
|
|
127
|
+
const text = searchableText(rp);
|
|
128
|
+
const allFiles = [...rp.filesCreated, ...rp.filesModified];
|
|
129
|
+
|
|
130
|
+
// ── Project-level facts (overwrite existing) ──
|
|
131
|
+
|
|
132
|
+
const testRunner = detectFirst(text, TEST_RUNNERS);
|
|
133
|
+
if (testRunner) facts.push(testRunner);
|
|
134
|
+
|
|
135
|
+
const buildTool = detectFirst(text, BUILD_TOOLS);
|
|
136
|
+
if (buildTool) facts.push(buildTool);
|
|
137
|
+
|
|
138
|
+
const pkgManager = detectFirst(text, PKG_MANAGERS);
|
|
139
|
+
if (pkgManager) facts.push(pkgManager);
|
|
140
|
+
|
|
141
|
+
const linter = detectFirst(text, LINTERS);
|
|
142
|
+
if (linter) facts.push(linter);
|
|
143
|
+
|
|
144
|
+
// ── Source file pattern ──
|
|
145
|
+
|
|
146
|
+
if (allFiles.length >= 3 && !existingKeys.has('project:source-pattern')) {
|
|
147
|
+
const extCounts = countExtensions(allFiles);
|
|
148
|
+
const dominant = Object.entries(extCounts)
|
|
149
|
+
.sort(([, a], [, b]) => b - a)
|
|
150
|
+
.slice(0, 3)
|
|
151
|
+
.map(([ext, count]) => `${ext} (${count})`)
|
|
152
|
+
.join(', ');
|
|
153
|
+
facts.push({
|
|
154
|
+
key: 'project:source-pattern',
|
|
155
|
+
value: truncate(dominant, MAX_VALUE_LENGTH),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ── Blockers ──
|
|
160
|
+
|
|
161
|
+
if (rp.blockers && rp.blockers.length > 0) {
|
|
162
|
+
for (const blocker of rp.blockers.slice(0, 2)) {
|
|
163
|
+
const normalized = normalizeBlockerKey(blocker);
|
|
164
|
+
if (!normalized) continue;
|
|
165
|
+
const key = truncate(`pattern:blocker:${normalized}`, MAX_KEY_LENGTH);
|
|
166
|
+
if (!existingKeys.has(key)) {
|
|
167
|
+
facts.push({ key, value: truncate(blocker, MAX_VALUE_LENGTH) });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ── Stagnation pattern ──
|
|
173
|
+
|
|
174
|
+
if (
|
|
175
|
+
rp.outcome === 'stalled' &&
|
|
176
|
+
task.context.stagnationCount > 1
|
|
177
|
+
) {
|
|
178
|
+
const key = truncate(`pattern:stagnation:${task.id}`, MAX_KEY_LENGTH);
|
|
179
|
+
if (!existingKeys.has(key)) {
|
|
180
|
+
facts.push({
|
|
181
|
+
key,
|
|
182
|
+
value: truncate(
|
|
183
|
+
`Task "${task.title}" stalled ${task.context.stagnationCount}x. Last: ${rp.summary}`,
|
|
184
|
+
MAX_VALUE_LENGTH,
|
|
185
|
+
),
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ── First-run success pattern ──
|
|
191
|
+
|
|
192
|
+
if (
|
|
193
|
+
rp.outcome === 'completed' &&
|
|
194
|
+
task.context.runHistory.length <= 1
|
|
195
|
+
) {
|
|
196
|
+
const key = truncate(
|
|
197
|
+
`pattern:success:${rp.profileId}:${task.complexity ?? 'unknown'}`,
|
|
198
|
+
MAX_KEY_LENGTH,
|
|
199
|
+
);
|
|
200
|
+
if (!existingKeys.has(key)) {
|
|
201
|
+
facts.push({
|
|
202
|
+
key,
|
|
203
|
+
value: truncate(
|
|
204
|
+
`Profile ${rp.profileId} completed ${task.complexity ?? 'unknown'}-complexity task on first run`,
|
|
205
|
+
MAX_VALUE_LENGTH,
|
|
206
|
+
),
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Enforce max facts per run
|
|
212
|
+
return facts.slice(0, MAX_FACTS_PER_RUN);
|
|
213
|
+
}
|
package/src/bot/operations.ts
CHANGED
|
@@ -38,8 +38,10 @@ export const OP_TASK_CREATE = 'task_create';
|
|
|
38
38
|
// Memory
|
|
39
39
|
// ---------------------------------------------------------------------------
|
|
40
40
|
|
|
41
|
-
export const
|
|
41
|
+
export const OP_LEARN = 'learn';
|
|
42
42
|
export const OP_RECALL = 'recall';
|
|
43
|
+
/** @deprecated Use OP_LEARN. Kept for step-executor backward compat. */
|
|
44
|
+
export const OP_REMEMBER = 'remember';
|
|
43
45
|
|
|
44
46
|
// ---------------------------------------------------------------------------
|
|
45
47
|
// Passthrough (no execution needed)
|