agentic-flow 1.4.6 → 1.4.7
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/CHANGELOG.md +96 -0
- package/dist/reasoningbank/core/database.js +250 -0
- package/dist/reasoningbank/core/memory-engine.js +335 -0
- package/dist/reasoningbank/types/index.js +5 -0
- package/dist/utils/reasoningbankCommands.js +167 -100
- package/docs/releases/GITHUB-ISSUE-ADDENDUM-v1.4.6.md +1529 -0
- package/docs/releases/GITHUB-ISSUE-v1.4.6.md +1453 -0
- package/docs/releases/v1.4.6-reasoningbank-release.md +541 -0
- package/docs/releases/v1.4.7-bugfix.md +212 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,102 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.4.7] - 2025-10-11
|
|
9
|
+
|
|
10
|
+
### 🐛 Critical Bug Fix: ReasoningBank CLI Now Accessible
|
|
11
|
+
|
|
12
|
+
This release fixes the ReasoningBank CLI commands not being accessible in v1.4.6.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- **Critical:** ReasoningBank CLI commands now work after npm install
|
|
16
|
+
- Fixed incomplete dist/ build in published v1.4.6 package
|
|
17
|
+
- All 5 CLI commands now accessible: demo, test, init, benchmark, status
|
|
18
|
+
- Command handler properly integrated into main CLI
|
|
19
|
+
- Complete rebuild ensures all 25 ReasoningBank modules included
|
|
20
|
+
|
|
21
|
+
### Verified
|
|
22
|
+
- ✅ `npx agentic-flow reasoningbank help` - Shows full help menu
|
|
23
|
+
- ✅ `npx agentic-flow reasoningbank demo` - Interactive demo works
|
|
24
|
+
- ✅ `npx agentic-flow reasoningbank test` - 27 tests passing
|
|
25
|
+
- ✅ `npx agentic-flow reasoningbank init` - Database initialization works
|
|
26
|
+
- ✅ `npx agentic-flow reasoningbank benchmark` - Performance tests work
|
|
27
|
+
- ✅ `npx agentic-flow reasoningbank status` - Memory statistics work
|
|
28
|
+
- ✅ 502 files in package (up from incomplete v1.4.6)
|
|
29
|
+
- ✅ dist/reasoningbank/ directory fully compiled (25 modules)
|
|
30
|
+
- ✅ dist/utils/reasoningbankCommands.js properly linked
|
|
31
|
+
|
|
32
|
+
### Technical Details
|
|
33
|
+
- **Root Cause:** v1.4.6 was published before TypeScript build completed
|
|
34
|
+
- **Fix:** Clean rebuild with `rm -rf dist/ && npm run build`
|
|
35
|
+
- **Prevention:** `prepublishOnly` hook ensures build before publish
|
|
36
|
+
|
|
37
|
+
### Package Contents
|
|
38
|
+
**ReasoningBank Core (dist/reasoningbank/):**
|
|
39
|
+
- core/ - retrieve.js, judge.js, distill.js, consolidate.js, matts.js
|
|
40
|
+
- db/ - schema.js, queries.js
|
|
41
|
+
- utils/ - config.js, embeddings.js, mmr.js, pii-scrubber.js
|
|
42
|
+
- hooks/ - pre-task.js, post-task.js
|
|
43
|
+
- Tests - demo-comparison.js, test-*.js, benchmark.js
|
|
44
|
+
|
|
45
|
+
### Documentation
|
|
46
|
+
- Added `docs/releases/v1.4.7-bugfix.md` - Complete bug fix details
|
|
47
|
+
- Updated `CHANGELOG.md` with fix verification
|
|
48
|
+
|
|
49
|
+
### Breaking Changes
|
|
50
|
+
None - fully backward compatible with v1.4.6
|
|
51
|
+
|
|
52
|
+
### Migration from v1.4.6
|
|
53
|
+
Simply upgrade:
|
|
54
|
+
```bash
|
|
55
|
+
npm install -g agentic-flow@latest
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## [1.4.6] - 2025-10-10
|
|
59
|
+
|
|
60
|
+
### ✨ Major Feature: ReasoningBank - Memory System that Learns from Experience
|
|
61
|
+
|
|
62
|
+
**⚠️ Known Issue:** CLI commands not accessible in published package. Fixed in v1.4.7.
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
- **ReasoningBank** - Full closed-loop memory system implementation
|
|
66
|
+
- 4-phase learning loop (RETRIEVE → JUDGE → DISTILL → CONSOLIDATE)
|
|
67
|
+
- 4-factor scoring formula (similarity, recency, reliability, diversity)
|
|
68
|
+
- MaTTS (Memory-aware Test-Time Scaling)
|
|
69
|
+
- 27/27 tests passing
|
|
70
|
+
- Performance 2-200x faster than targets
|
|
71
|
+
|
|
72
|
+
- **Database Schema** - 6 new tables for memory persistence
|
|
73
|
+
- reasoning_memory, pattern_embeddings, task_trajectory
|
|
74
|
+
- matts_runs, consolidation_runs, pattern_links
|
|
75
|
+
|
|
76
|
+
- **CLI Commands** (5 new commands - broken in v1.4.6, fixed in v1.4.7)
|
|
77
|
+
- `reasoningbank demo` - Interactive demo comparison
|
|
78
|
+
- `reasoningbank test` - Validation test suite
|
|
79
|
+
- `reasoningbank init` - Database initialization
|
|
80
|
+
- `reasoningbank benchmark` - Performance benchmarks
|
|
81
|
+
- `reasoningbank status` - Memory statistics
|
|
82
|
+
|
|
83
|
+
- **Documentation** (3 comprehensive guides, 1,400+ lines)
|
|
84
|
+
- src/reasoningbank/README.md (528 lines)
|
|
85
|
+
- docs/REASONINGBANK-DEMO.md (420 lines)
|
|
86
|
+
- docs/REASONINGBANK-CLI-INTEGRATION.md (456 lines)
|
|
87
|
+
|
|
88
|
+
- **Security**
|
|
89
|
+
- PII scrubbing with 9 pattern types
|
|
90
|
+
- Multi-tenant support with tenant isolation
|
|
91
|
+
- Full audit trail
|
|
92
|
+
|
|
93
|
+
### Performance
|
|
94
|
+
- Insert memory: 1.175ms (851 ops/sec)
|
|
95
|
+
- Retrieve (filtered): 0.924ms (1,083 ops/sec)
|
|
96
|
+
- MMR diversity: 0.005ms (208K ops/sec)
|
|
97
|
+
- Scales to 10,000+ memories with linear performance
|
|
98
|
+
|
|
99
|
+
### Changed
|
|
100
|
+
- Version: 1.4.5 → 1.4.6
|
|
101
|
+
- README: Added ReasoningBank as primary feature
|
|
102
|
+
- Keywords: Added reasoning, memory, and learning tags
|
|
103
|
+
|
|
8
104
|
## [1.1.14] - 2025-10-05
|
|
9
105
|
|
|
10
106
|
### 🎉 Major Fix: OpenRouter Proxy Now Working!
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite Database Layer for ReasoningBank
|
|
3
|
+
* Handles memory storage, retrieval, and consolidation
|
|
4
|
+
*/
|
|
5
|
+
import Database from 'better-sqlite3';
|
|
6
|
+
import { ulid } from 'ulid';
|
|
7
|
+
export class ReasoningBankDB {
|
|
8
|
+
db;
|
|
9
|
+
constructor(dbPath) {
|
|
10
|
+
this.db = new Database(dbPath);
|
|
11
|
+
this.db.pragma('journal_mode = WAL'); // Enable Write-Ahead Logging for concurrency
|
|
12
|
+
this.initSchema();
|
|
13
|
+
}
|
|
14
|
+
initSchema() {
|
|
15
|
+
// Main memory table
|
|
16
|
+
this.db.exec(`
|
|
17
|
+
CREATE TABLE IF NOT EXISTS reasoning_memory (
|
|
18
|
+
id TEXT PRIMARY KEY,
|
|
19
|
+
title TEXT NOT NULL,
|
|
20
|
+
description TEXT NOT NULL,
|
|
21
|
+
content TEXT NOT NULL,
|
|
22
|
+
confidence REAL DEFAULT 0.5,
|
|
23
|
+
usage_count INTEGER DEFAULT 0,
|
|
24
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
25
|
+
pattern_data JSON NOT NULL
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
CREATE INDEX IF NOT EXISTS idx_memory_confidence ON reasoning_memory(confidence);
|
|
29
|
+
CREATE INDEX IF NOT EXISTS idx_memory_created_at ON reasoning_memory(created_at);
|
|
30
|
+
CREATE INDEX IF NOT EXISTS idx_memory_domain ON reasoning_memory(json_extract(pattern_data, '$.domain'));
|
|
31
|
+
`);
|
|
32
|
+
// Pattern embeddings table
|
|
33
|
+
this.db.exec(`
|
|
34
|
+
CREATE TABLE IF NOT EXISTS pattern_embeddings (
|
|
35
|
+
pattern_id TEXT PRIMARY KEY,
|
|
36
|
+
embedding BLOB NOT NULL,
|
|
37
|
+
FOREIGN KEY (pattern_id) REFERENCES reasoning_memory(id) ON DELETE CASCADE
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
CREATE INDEX IF NOT EXISTS idx_embeddings_pattern ON pattern_embeddings(pattern_id);
|
|
41
|
+
`);
|
|
42
|
+
// Task trajectory table
|
|
43
|
+
this.db.exec(`
|
|
44
|
+
CREATE TABLE IF NOT EXISTS task_trajectory (
|
|
45
|
+
id TEXT PRIMARY KEY,
|
|
46
|
+
task_id TEXT NOT NULL,
|
|
47
|
+
trajectory TEXT NOT NULL,
|
|
48
|
+
verdict TEXT NOT NULL CHECK(verdict IN ('Success', 'Failure')),
|
|
49
|
+
confidence REAL NOT NULL,
|
|
50
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_trajectory_task ON task_trajectory(task_id);
|
|
54
|
+
CREATE INDEX IF NOT EXISTS idx_trajectory_verdict ON task_trajectory(verdict);
|
|
55
|
+
`);
|
|
56
|
+
// MaTTS runs table
|
|
57
|
+
this.db.exec(`
|
|
58
|
+
CREATE TABLE IF NOT EXISTS matts_runs (
|
|
59
|
+
id TEXT PRIMARY KEY,
|
|
60
|
+
task_id TEXT NOT NULL,
|
|
61
|
+
run_index INTEGER NOT NULL,
|
|
62
|
+
result TEXT NOT NULL,
|
|
63
|
+
verdict TEXT NOT NULL CHECK(verdict IN ('Success', 'Failure')),
|
|
64
|
+
confidence REAL NOT NULL,
|
|
65
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
CREATE INDEX IF NOT EXISTS idx_matts_task ON matts_runs(task_id);
|
|
69
|
+
`);
|
|
70
|
+
}
|
|
71
|
+
// Memory operations
|
|
72
|
+
insertMemory(memory) {
|
|
73
|
+
const id = ulid();
|
|
74
|
+
const stmt = this.db.prepare(`
|
|
75
|
+
INSERT INTO reasoning_memory (id, title, description, content, confidence, usage_count, pattern_data)
|
|
76
|
+
VALUES (?, ?, ?, ?, ?, ?, json(?))
|
|
77
|
+
`);
|
|
78
|
+
stmt.run(id, memory.title, memory.description, memory.content, memory.confidence, memory.usage_count, JSON.stringify(memory.pattern_data));
|
|
79
|
+
return id;
|
|
80
|
+
}
|
|
81
|
+
getMemory(id) {
|
|
82
|
+
const stmt = this.db.prepare(`
|
|
83
|
+
SELECT id, title, description, content, confidence, usage_count,
|
|
84
|
+
datetime(created_at) as created_at, pattern_data
|
|
85
|
+
FROM reasoning_memory
|
|
86
|
+
WHERE id = ?
|
|
87
|
+
`);
|
|
88
|
+
const row = stmt.get(id);
|
|
89
|
+
if (!row)
|
|
90
|
+
return null;
|
|
91
|
+
return {
|
|
92
|
+
...row,
|
|
93
|
+
pattern_data: JSON.parse(row.pattern_data)
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
getAllMemories() {
|
|
97
|
+
const stmt = this.db.prepare(`
|
|
98
|
+
SELECT id, title, description, content, confidence, usage_count,
|
|
99
|
+
datetime(created_at) as created_at, pattern_data
|
|
100
|
+
FROM reasoning_memory
|
|
101
|
+
ORDER BY created_at DESC
|
|
102
|
+
`);
|
|
103
|
+
const rows = stmt.all();
|
|
104
|
+
return rows.map(row => ({
|
|
105
|
+
...row,
|
|
106
|
+
pattern_data: JSON.parse(row.pattern_data)
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
updateMemoryUsage(id) {
|
|
110
|
+
const stmt = this.db.prepare(`
|
|
111
|
+
UPDATE reasoning_memory
|
|
112
|
+
SET usage_count = usage_count + 1
|
|
113
|
+
WHERE id = ?
|
|
114
|
+
`);
|
|
115
|
+
stmt.run(id);
|
|
116
|
+
}
|
|
117
|
+
updateMemoryConfidence(id, confidence) {
|
|
118
|
+
const stmt = this.db.prepare(`
|
|
119
|
+
UPDATE reasoning_memory
|
|
120
|
+
SET confidence = ?
|
|
121
|
+
WHERE id = ?
|
|
122
|
+
`);
|
|
123
|
+
stmt.run(confidence, id);
|
|
124
|
+
}
|
|
125
|
+
deleteMemory(id) {
|
|
126
|
+
const stmt = this.db.prepare('DELETE FROM reasoning_memory WHERE id = ?');
|
|
127
|
+
stmt.run(id);
|
|
128
|
+
}
|
|
129
|
+
// Embedding operations
|
|
130
|
+
insertEmbedding(patternId, embedding) {
|
|
131
|
+
const buffer = Buffer.from(new Float64Array(embedding).buffer);
|
|
132
|
+
const stmt = this.db.prepare(`
|
|
133
|
+
INSERT OR REPLACE INTO pattern_embeddings (pattern_id, embedding)
|
|
134
|
+
VALUES (?, ?)
|
|
135
|
+
`);
|
|
136
|
+
stmt.run(patternId, buffer);
|
|
137
|
+
}
|
|
138
|
+
getEmbedding(patternId) {
|
|
139
|
+
const stmt = this.db.prepare('SELECT embedding FROM pattern_embeddings WHERE pattern_id = ?');
|
|
140
|
+
const row = stmt.get(patternId);
|
|
141
|
+
if (!row)
|
|
142
|
+
return null;
|
|
143
|
+
const buffer = row.embedding;
|
|
144
|
+
return Array.from(new Float64Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 8));
|
|
145
|
+
}
|
|
146
|
+
getAllEmbeddings() {
|
|
147
|
+
const stmt = this.db.prepare('SELECT pattern_id, embedding FROM pattern_embeddings');
|
|
148
|
+
const rows = stmt.all();
|
|
149
|
+
const embeddings = new Map();
|
|
150
|
+
for (const row of rows) {
|
|
151
|
+
const buffer = row.embedding;
|
|
152
|
+
const embedding = Array.from(new Float64Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 8));
|
|
153
|
+
embeddings.set(row.pattern_id, embedding);
|
|
154
|
+
}
|
|
155
|
+
return embeddings;
|
|
156
|
+
}
|
|
157
|
+
// Trajectory operations
|
|
158
|
+
insertTrajectory(trajectory) {
|
|
159
|
+
const id = ulid();
|
|
160
|
+
const stmt = this.db.prepare(`
|
|
161
|
+
INSERT INTO task_trajectory (id, task_id, trajectory, verdict, confidence)
|
|
162
|
+
VALUES (?, ?, ?, ?, ?)
|
|
163
|
+
`);
|
|
164
|
+
stmt.run(id, trajectory.task_id, trajectory.trajectory, trajectory.verdict, trajectory.confidence);
|
|
165
|
+
return id;
|
|
166
|
+
}
|
|
167
|
+
getTrajectories(taskId) {
|
|
168
|
+
const stmt = this.db.prepare(`
|
|
169
|
+
SELECT id, task_id, trajectory, verdict, confidence, datetime(created_at) as created_at
|
|
170
|
+
FROM task_trajectory
|
|
171
|
+
WHERE task_id = ?
|
|
172
|
+
ORDER BY created_at DESC
|
|
173
|
+
`);
|
|
174
|
+
return stmt.all(taskId);
|
|
175
|
+
}
|
|
176
|
+
// MaTTS operations
|
|
177
|
+
insertMattsRun(run) {
|
|
178
|
+
const id = ulid();
|
|
179
|
+
const stmt = this.db.prepare(`
|
|
180
|
+
INSERT INTO matts_runs (id, task_id, run_index, result, verdict, confidence)
|
|
181
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
182
|
+
`);
|
|
183
|
+
stmt.run(id, run.task_id, run.run_index, run.result, run.verdict, run.confidence);
|
|
184
|
+
return id;
|
|
185
|
+
}
|
|
186
|
+
getMattsRuns(taskId) {
|
|
187
|
+
const stmt = this.db.prepare(`
|
|
188
|
+
SELECT id, task_id, run_index, result, verdict, confidence, datetime(created_at) as created_at
|
|
189
|
+
FROM matts_runs
|
|
190
|
+
WHERE task_id = ?
|
|
191
|
+
ORDER BY run_index ASC
|
|
192
|
+
`);
|
|
193
|
+
return stmt.all(taskId);
|
|
194
|
+
}
|
|
195
|
+
// Statistics
|
|
196
|
+
getStats() {
|
|
197
|
+
const memoryStats = this.db.prepare(`
|
|
198
|
+
SELECT COUNT(*) as total, AVG(confidence) as avg_conf, SUM(usage_count) as total_usage
|
|
199
|
+
FROM reasoning_memory
|
|
200
|
+
`).get();
|
|
201
|
+
const trajectoryStats = this.db.prepare(`
|
|
202
|
+
SELECT
|
|
203
|
+
SUM(CASE WHEN verdict = 'Success' THEN 1 ELSE 0 END) as successes,
|
|
204
|
+
COUNT(*) as total
|
|
205
|
+
FROM task_trajectory
|
|
206
|
+
`).get();
|
|
207
|
+
return {
|
|
208
|
+
totalMemories: memoryStats.total || 0,
|
|
209
|
+
avgConfidence: memoryStats.avg_conf || 0,
|
|
210
|
+
totalUsage: memoryStats.total_usage || 0,
|
|
211
|
+
successRate: trajectoryStats.total > 0
|
|
212
|
+
? trajectoryStats.successes / trajectoryStats.total
|
|
213
|
+
: 0
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
// Consolidation helpers
|
|
217
|
+
findDuplicates(threshold = 0.95) {
|
|
218
|
+
// Returns pairs of memory IDs that are likely duplicates
|
|
219
|
+
const memories = this.getAllMemories();
|
|
220
|
+
const embeddings = this.getAllEmbeddings();
|
|
221
|
+
const duplicates = [];
|
|
222
|
+
for (let i = 0; i < memories.length; i++) {
|
|
223
|
+
for (let j = i + 1; j < memories.length; j++) {
|
|
224
|
+
const emb1 = embeddings.get(memories[i].id);
|
|
225
|
+
const emb2 = embeddings.get(memories[j].id);
|
|
226
|
+
if (emb1 && emb2) {
|
|
227
|
+
const similarity = this.cosineSimilarity(emb1, emb2);
|
|
228
|
+
if (similarity >= threshold) {
|
|
229
|
+
duplicates.push([memories[i].id, memories[j].id]);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return duplicates;
|
|
235
|
+
}
|
|
236
|
+
cosineSimilarity(a, b) {
|
|
237
|
+
let dotProduct = 0;
|
|
238
|
+
let normA = 0;
|
|
239
|
+
let normB = 0;
|
|
240
|
+
for (let i = 0; i < a.length; i++) {
|
|
241
|
+
dotProduct += a[i] * b[i];
|
|
242
|
+
normA += a[i] * a[i];
|
|
243
|
+
normB += b[i] * b[i];
|
|
244
|
+
}
|
|
245
|
+
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
246
|
+
}
|
|
247
|
+
close() {
|
|
248
|
+
this.db.close();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ReasoningBank Memory Engine
|
|
3
|
+
* Implements the 4-phase learning loop: RETRIEVE → JUDGE → DISTILL → CONSOLIDATE
|
|
4
|
+
*/
|
|
5
|
+
import { ReasoningBankDB } from './database.js';
|
|
6
|
+
import { createEmbeddingProvider, cosineSimilarity } from '../utils/embeddings.js';
|
|
7
|
+
import { piiScrubber } from '../utils/pii-scrubber.js';
|
|
8
|
+
export class ReasoningBankEngine {
|
|
9
|
+
db;
|
|
10
|
+
embeddings;
|
|
11
|
+
piiEnabled;
|
|
12
|
+
weights;
|
|
13
|
+
defaultK;
|
|
14
|
+
minConfidence;
|
|
15
|
+
consolidationThreshold;
|
|
16
|
+
memoriesSinceConsolidation = 0;
|
|
17
|
+
constructor(config) {
|
|
18
|
+
this.db = new ReasoningBankDB(config.dbPath);
|
|
19
|
+
this.embeddings = createEmbeddingProvider(config.embeddings?.provider || 'hash', {
|
|
20
|
+
apiKey: process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY,
|
|
21
|
+
model: config.embeddings?.model
|
|
22
|
+
});
|
|
23
|
+
this.piiEnabled = config.piiScrub?.enabled !== false;
|
|
24
|
+
this.weights = {
|
|
25
|
+
alpha: config.retrieval?.weights?.alpha || 0.65, // Similarity
|
|
26
|
+
beta: config.retrieval?.weights?.beta || 0.15, // Recency
|
|
27
|
+
gamma: config.retrieval?.weights?.gamma || 0.20, // Reliability
|
|
28
|
+
delta: config.retrieval?.weights?.delta || 0.10 // Diversity penalty
|
|
29
|
+
};
|
|
30
|
+
this.defaultK = config.retrieval?.k || 3;
|
|
31
|
+
this.minConfidence = config.retrieval?.minConfidence || 0.3;
|
|
32
|
+
this.consolidationThreshold = config.consolidation?.scheduleEvery || 20;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Phase 1: RETRIEVE - Get relevant memories using 4-factor scoring
|
|
36
|
+
*/
|
|
37
|
+
async retrieve(options) {
|
|
38
|
+
const k = options.k || this.defaultK;
|
|
39
|
+
const lambda = options.lambda || 0.9;
|
|
40
|
+
// Generate query embedding
|
|
41
|
+
const queryEmbedding = await this.embeddings.generate(options.query);
|
|
42
|
+
// Get all memories
|
|
43
|
+
const allMemories = this.db.getAllMemories();
|
|
44
|
+
const embeddings = this.db.getAllEmbeddings();
|
|
45
|
+
// Filter by domain if specified
|
|
46
|
+
let candidates = allMemories;
|
|
47
|
+
if (options.domain) {
|
|
48
|
+
candidates = candidates.filter(m => m.pattern_data.domain === options.domain ||
|
|
49
|
+
m.pattern_data.domain?.startsWith(options.domain + '.'));
|
|
50
|
+
}
|
|
51
|
+
// Calculate scores for each candidate
|
|
52
|
+
const scoredCandidates = [];
|
|
53
|
+
for (const memory of candidates) {
|
|
54
|
+
const embedding = embeddings.get(memory.id);
|
|
55
|
+
if (!embedding)
|
|
56
|
+
continue;
|
|
57
|
+
// 1. Similarity score (cosine similarity)
|
|
58
|
+
const similarity = cosineSimilarity(queryEmbedding, embedding);
|
|
59
|
+
// 2. Recency score (exponential decay, 30-day half-life)
|
|
60
|
+
const ageDays = (Date.now() - new Date(memory.created_at).getTime()) / (1000 * 60 * 60 * 24);
|
|
61
|
+
const recency = Math.exp(-ageDays / 30);
|
|
62
|
+
// 3. Reliability score (confidence × sqrt(usage/10))
|
|
63
|
+
const reliability = Math.min(memory.confidence * Math.sqrt(memory.usage_count / 10), 1.0);
|
|
64
|
+
// Combined score (before diversity penalty)
|
|
65
|
+
const score = this.weights.alpha * similarity +
|
|
66
|
+
this.weights.beta * recency +
|
|
67
|
+
this.weights.gamma * reliability;
|
|
68
|
+
scoredCandidates.push({
|
|
69
|
+
...memory,
|
|
70
|
+
score,
|
|
71
|
+
similarity,
|
|
72
|
+
recency,
|
|
73
|
+
reliability,
|
|
74
|
+
diversityPenalty: 0 // Will be calculated in MMR
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// Sort by score
|
|
78
|
+
scoredCandidates.sort((a, b) => b.score - a.score);
|
|
79
|
+
// Apply MMR for diversity
|
|
80
|
+
const selected = this.selectWithMMR(scoredCandidates, queryEmbedding, k, lambda);
|
|
81
|
+
// Update usage counts
|
|
82
|
+
for (const memory of selected) {
|
|
83
|
+
this.db.updateMemoryUsage(memory.id);
|
|
84
|
+
}
|
|
85
|
+
// Filter by minimum confidence
|
|
86
|
+
return selected.filter(m => m.confidence >= this.minConfidence);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* MMR (Maximal Marginal Relevance) Selection
|
|
90
|
+
* Balances relevance and diversity
|
|
91
|
+
*/
|
|
92
|
+
selectWithMMR(candidates, queryEmbedding, k, lambda) {
|
|
93
|
+
const selected = [];
|
|
94
|
+
const remaining = [...candidates];
|
|
95
|
+
const embeddings = this.db.getAllEmbeddings();
|
|
96
|
+
while (selected.length < k && remaining.length > 0) {
|
|
97
|
+
let bestScore = -Infinity;
|
|
98
|
+
let bestIndex = -1;
|
|
99
|
+
for (let i = 0; i < remaining.length; i++) {
|
|
100
|
+
const candidate = remaining[i];
|
|
101
|
+
const candidateEmbedding = embeddings.get(candidate.id);
|
|
102
|
+
if (!candidateEmbedding)
|
|
103
|
+
continue;
|
|
104
|
+
// Relevance to query
|
|
105
|
+
const relevance = candidate.score;
|
|
106
|
+
// Maximum similarity to already selected
|
|
107
|
+
let maxSimilarity = 0;
|
|
108
|
+
if (selected.length > 0) {
|
|
109
|
+
for (const selectedMemory of selected) {
|
|
110
|
+
const selectedEmbedding = embeddings.get(selectedMemory.id);
|
|
111
|
+
if (selectedEmbedding) {
|
|
112
|
+
const sim = cosineSimilarity(candidateEmbedding, selectedEmbedding);
|
|
113
|
+
maxSimilarity = Math.max(maxSimilarity, sim);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// MMR score
|
|
118
|
+
const mmrScore = lambda * relevance - (1 - lambda) * maxSimilarity;
|
|
119
|
+
if (mmrScore > bestScore) {
|
|
120
|
+
bestScore = mmrScore;
|
|
121
|
+
bestIndex = i;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (bestIndex >= 0) {
|
|
125
|
+
selected.push(remaining[bestIndex]);
|
|
126
|
+
remaining.splice(bestIndex, 1);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return selected;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Phase 2: JUDGE - Evaluate task outcome
|
|
136
|
+
*/
|
|
137
|
+
async judge(trajectory) {
|
|
138
|
+
// Simple heuristic judge (can be upgraded to LLM)
|
|
139
|
+
const scrubbed = this.piiEnabled ? piiScrubber.scrub(trajectory) : trajectory;
|
|
140
|
+
// Heuristics for success/failure
|
|
141
|
+
const errorKeywords = ['error', 'failed', 'exception', 'timeout', 'unauthorized', 'forbidden'];
|
|
142
|
+
const successKeywords = ['success', 'completed', 'ok', '200', 'done'];
|
|
143
|
+
const lowerTrajectory = scrubbed.toLowerCase();
|
|
144
|
+
const hasError = errorKeywords.some(kw => lowerTrajectory.includes(kw));
|
|
145
|
+
const hasSuccess = successKeywords.some(kw => lowerTrajectory.includes(kw));
|
|
146
|
+
if (hasSuccess && !hasError) {
|
|
147
|
+
return { label: 'Success', confidence: 0.8, rationale: 'Success keywords found' };
|
|
148
|
+
}
|
|
149
|
+
else if (hasError && !hasSuccess) {
|
|
150
|
+
return { label: 'Failure', confidence: 0.8, rationale: 'Error keywords found' };
|
|
151
|
+
}
|
|
152
|
+
else if (hasSuccess && hasError) {
|
|
153
|
+
return { label: 'Success', confidence: 0.5, rationale: 'Mixed signals' };
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
return { label: 'Failure', confidence: 0.5, rationale: 'No clear indicators' };
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Phase 3: DISTILL - Extract patterns from trajectory
|
|
161
|
+
*/
|
|
162
|
+
async distill(taskId, trajectory, verdict, domain) {
|
|
163
|
+
const scrubbed = this.piiEnabled ? piiScrubber.scrub(trajectory) : trajectory;
|
|
164
|
+
// Store trajectory
|
|
165
|
+
this.db.insertTrajectory({
|
|
166
|
+
task_id: taskId,
|
|
167
|
+
trajectory: scrubbed,
|
|
168
|
+
verdict: verdict.label,
|
|
169
|
+
confidence: verdict.confidence
|
|
170
|
+
});
|
|
171
|
+
// Extract pattern based on verdict
|
|
172
|
+
const pattern = verdict.label === 'Success'
|
|
173
|
+
? this.extractSuccessPattern(scrubbed, domain)
|
|
174
|
+
: this.extractFailureGuardrail(scrubbed, domain);
|
|
175
|
+
// Store as memory
|
|
176
|
+
const memoryId = this.db.insertMemory({
|
|
177
|
+
title: pattern.title,
|
|
178
|
+
description: pattern.description,
|
|
179
|
+
content: pattern.content,
|
|
180
|
+
confidence: verdict.confidence,
|
|
181
|
+
usage_count: 0,
|
|
182
|
+
pattern_data: {
|
|
183
|
+
domain,
|
|
184
|
+
success_pattern: verdict.label === 'Success',
|
|
185
|
+
failure_guardrail: verdict.label === 'Failure'
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
// Generate and store embedding
|
|
189
|
+
const embedding = await this.embeddings.generate(pattern.content);
|
|
190
|
+
this.db.insertEmbedding(memoryId, embedding);
|
|
191
|
+
this.memoriesSinceConsolidation++;
|
|
192
|
+
return memoryId;
|
|
193
|
+
}
|
|
194
|
+
extractSuccessPattern(trajectory, domain) {
|
|
195
|
+
// Extract key steps from successful execution
|
|
196
|
+
const lines = trajectory.split('\n').filter(l => l.trim());
|
|
197
|
+
const keySteps = lines.slice(0, 5).join('\n');
|
|
198
|
+
return {
|
|
199
|
+
title: `Success pattern for ${domain}`,
|
|
200
|
+
description: `Successful execution strategy`,
|
|
201
|
+
content: `Successful approach:\n${keySteps}`
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
extractFailureGuardrail(trajectory, domain) {
|
|
205
|
+
// Extract error information
|
|
206
|
+
const lines = trajectory.split('\n').filter(l => l.trim());
|
|
207
|
+
const errorInfo = lines.find(l => l.toLowerCase().includes('error') ||
|
|
208
|
+
l.toLowerCase().includes('failed')) || 'Unknown error';
|
|
209
|
+
return {
|
|
210
|
+
title: `Failure guardrail for ${domain}`,
|
|
211
|
+
description: `Prevention strategy for common failures`,
|
|
212
|
+
content: `Avoid: ${errorInfo}\nRecommend: Check prerequisites and retry with backoff`
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Phase 4: CONSOLIDATE - Deduplicate and prune
|
|
217
|
+
*/
|
|
218
|
+
async consolidate(options) {
|
|
219
|
+
const startTime = Date.now();
|
|
220
|
+
const dedupeThreshold = options?.dedupeThreshold || 0.95;
|
|
221
|
+
const maxAgeDays = options?.prune?.maxAgeDays || 90;
|
|
222
|
+
const minConfidence = options?.prune?.minConfidence || 0.3;
|
|
223
|
+
const unusedDays = options?.prune?.unusedDays || 30;
|
|
224
|
+
// Find and merge duplicates
|
|
225
|
+
const duplicates = this.db.findDuplicates(dedupeThreshold);
|
|
226
|
+
for (const [id1, id2] of duplicates) {
|
|
227
|
+
const mem1 = this.db.getMemory(id1);
|
|
228
|
+
const mem2 = this.db.getMemory(id2);
|
|
229
|
+
if (mem1 && mem2) {
|
|
230
|
+
// Keep the one with higher confidence and usage
|
|
231
|
+
const keepId = mem1.confidence > mem2.confidence ||
|
|
232
|
+
(mem1.confidence === mem2.confidence && mem1.usage_count > mem2.usage_count)
|
|
233
|
+
? id1 : id2;
|
|
234
|
+
const deleteId = keepId === id1 ? id2 : id1;
|
|
235
|
+
this.db.deleteMemory(deleteId);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// Prune old or low-quality memories
|
|
239
|
+
const allMemories = this.db.getAllMemories();
|
|
240
|
+
let pruned = 0;
|
|
241
|
+
for (const memory of allMemories) {
|
|
242
|
+
const ageDays = (Date.now() - new Date(memory.created_at).getTime()) / (1000 * 60 * 60 * 24);
|
|
243
|
+
const lastUsedDays = ageDays; // Simplified: assume last used = created
|
|
244
|
+
const shouldPrune = ageDays > maxAgeDays ||
|
|
245
|
+
memory.confidence < minConfidence ||
|
|
246
|
+
(memory.usage_count === 0 && lastUsedDays > unusedDays);
|
|
247
|
+
if (shouldPrune) {
|
|
248
|
+
this.db.deleteMemory(memory.id);
|
|
249
|
+
pruned++;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// Detect contradictions (simplified)
|
|
253
|
+
const contradictions = 0; // TODO: Implement semantic contradiction detection
|
|
254
|
+
this.memoriesSinceConsolidation = 0;
|
|
255
|
+
return {
|
|
256
|
+
processed: allMemories.length,
|
|
257
|
+
duplicates: duplicates.length,
|
|
258
|
+
contradictions,
|
|
259
|
+
pruned,
|
|
260
|
+
durationMs: Date.now() - startTime
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* High-level task execution with full learning loop
|
|
265
|
+
*/
|
|
266
|
+
async runTask(options) {
|
|
267
|
+
// Phase 1: RETRIEVE
|
|
268
|
+
const memories = await this.retrieve({
|
|
269
|
+
query: options.query,
|
|
270
|
+
domain: options.domain
|
|
271
|
+
});
|
|
272
|
+
// EXECUTE
|
|
273
|
+
const result = await options.executeFn(memories);
|
|
274
|
+
// Phase 2: JUDGE
|
|
275
|
+
const verdict = await this.judge(result.log);
|
|
276
|
+
// Phase 3: DISTILL
|
|
277
|
+
await this.distill(options.taskId, result.log, verdict, options.domain);
|
|
278
|
+
// Phase 4: CONSOLIDATE (if threshold reached)
|
|
279
|
+
if (this.memoriesSinceConsolidation >= this.consolidationThreshold) {
|
|
280
|
+
await this.consolidate();
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
success: result.success,
|
|
284
|
+
summary: `Task ${options.taskId}: ${verdict.label} (confidence: ${verdict.confidence})`,
|
|
285
|
+
memories,
|
|
286
|
+
verdict
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* MaTTS: Memory-aware Test-Time Scaling (Parallel)
|
|
291
|
+
*/
|
|
292
|
+
async mattsParallel(options) {
|
|
293
|
+
const runs = await Promise.all(Array.from({ length: options.k }, async (_, i) => {
|
|
294
|
+
const memories = await this.retrieve({
|
|
295
|
+
query: options.query,
|
|
296
|
+
domain: options.domain
|
|
297
|
+
});
|
|
298
|
+
const result = await options.executeFn(memories);
|
|
299
|
+
const verdict = await this.judge(result.log);
|
|
300
|
+
this.db.insertMattsRun({
|
|
301
|
+
task_id: options.taskId,
|
|
302
|
+
run_index: i,
|
|
303
|
+
result: result.log,
|
|
304
|
+
verdict: verdict.label,
|
|
305
|
+
confidence: verdict.confidence
|
|
306
|
+
});
|
|
307
|
+
return { result, verdict };
|
|
308
|
+
}));
|
|
309
|
+
// Calculate consensus
|
|
310
|
+
const successes = runs.filter(r => r.verdict.label === 'Success').length;
|
|
311
|
+
const avgConfidence = runs.reduce((sum, r) => sum + r.verdict.confidence, 0) / runs.length;
|
|
312
|
+
const consensusVerdict = {
|
|
313
|
+
label: successes > runs.length / 2 ? 'Success' : 'Failure',
|
|
314
|
+
confidence: avgConfidence
|
|
315
|
+
};
|
|
316
|
+
return {
|
|
317
|
+
success: consensusVerdict.label === 'Success',
|
|
318
|
+
summary: `MaTTS Parallel: ${successes}/${runs.length} successes, consensus: ${consensusVerdict.label}`,
|
|
319
|
+
memories: [],
|
|
320
|
+
verdict: consensusVerdict
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Get statistics
|
|
325
|
+
*/
|
|
326
|
+
getStats() {
|
|
327
|
+
return this.db.getStats();
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Close database connection
|
|
331
|
+
*/
|
|
332
|
+
close() {
|
|
333
|
+
this.db.close();
|
|
334
|
+
}
|
|
335
|
+
}
|