agentdb 1.5.9 → 1.6.0
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/README.md +11 -11
- package/dist/agentdb.min.js +4 -4
- package/dist/cli/agentdb-cli.d.ts +29 -0
- package/dist/cli/agentdb-cli.d.ts.map +1 -1
- package/dist/cli/agentdb-cli.js +1009 -34
- package/dist/cli/agentdb-cli.js.map +1 -1
- package/dist/controllers/ContextSynthesizer.d.ts +65 -0
- package/dist/controllers/ContextSynthesizer.d.ts.map +1 -0
- package/dist/controllers/ContextSynthesizer.js +208 -0
- package/dist/controllers/ContextSynthesizer.js.map +1 -0
- package/dist/controllers/MMRDiversityRanker.d.ts +50 -0
- package/dist/controllers/MMRDiversityRanker.d.ts.map +1 -0
- package/dist/controllers/MMRDiversityRanker.js +130 -0
- package/dist/controllers/MMRDiversityRanker.js.map +1 -0
- package/dist/controllers/MetadataFilter.d.ts +70 -0
- package/dist/controllers/MetadataFilter.d.ts.map +1 -0
- package/dist/controllers/MetadataFilter.js +243 -0
- package/dist/controllers/MetadataFilter.js.map +1 -0
- package/dist/controllers/QUICClient.d.ts +109 -0
- package/dist/controllers/QUICClient.d.ts.map +1 -0
- package/dist/controllers/QUICClient.js +299 -0
- package/dist/controllers/QUICClient.js.map +1 -0
- package/dist/controllers/QUICServer.d.ts +121 -0
- package/dist/controllers/QUICServer.d.ts.map +1 -0
- package/dist/controllers/QUICServer.js +383 -0
- package/dist/controllers/QUICServer.js.map +1 -0
- package/dist/controllers/SyncCoordinator.d.ts +120 -0
- package/dist/controllers/SyncCoordinator.d.ts.map +1 -0
- package/dist/controllers/SyncCoordinator.js +441 -0
- package/dist/controllers/SyncCoordinator.js.map +1 -0
- package/dist/controllers/WASMVectorSearch.d.ts.map +1 -1
- package/dist/controllers/WASMVectorSearch.js +10 -2
- package/dist/controllers/WASMVectorSearch.js.map +1 -1
- package/dist/controllers/index.d.ts +12 -0
- package/dist/controllers/index.d.ts.map +1 -1
- package/dist/controllers/index.js +6 -0
- package/dist/controllers/index.js.map +1 -1
- package/dist/examples/quic-sync-example.d.ts +9 -0
- package/dist/examples/quic-sync-example.d.ts.map +1 -0
- package/dist/examples/quic-sync-example.js +169 -0
- package/dist/examples/quic-sync-example.js.map +1 -0
- package/dist/types/quic.d.ts +518 -0
- package/dist/types/quic.d.ts.map +1 -0
- package/dist/types/quic.js +272 -0
- package/dist/types/quic.js.map +1 -0
- package/package.json +9 -3
- package/src/browser-entry.js +41 -6
- package/src/cli/agentdb-cli.ts +1114 -33
- package/src/controllers/ContextSynthesizer.ts +285 -0
- package/src/controllers/MMRDiversityRanker.ts +187 -0
- package/src/controllers/MetadataFilter.ts +280 -0
- package/src/controllers/QUICClient.ts +413 -0
- package/src/controllers/QUICServer.ts +498 -0
- package/src/controllers/SyncCoordinator.ts +597 -0
- package/src/controllers/WASMVectorSearch.ts +11 -2
- package/src/controllers/index.ts +12 -0
- package/src/examples/quic-sync-example.ts +198 -0
- package/src/types/quic.ts +772 -0
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SyncCoordinator - Orchestrate AgentDB Synchronization
|
|
3
|
+
*
|
|
4
|
+
* Coordinates bidirectional synchronization between local and remote AgentDB instances.
|
|
5
|
+
* Handles change detection, conflict resolution, batching, and progress tracking.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Detect changes since last sync
|
|
9
|
+
* - Bidirectional sync (push and pull)
|
|
10
|
+
* - Conflict resolution strategies
|
|
11
|
+
* - Batch operations for efficiency
|
|
12
|
+
* - Progress tracking and reporting
|
|
13
|
+
* - Comprehensive error handling
|
|
14
|
+
* - Sync state persistence
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import chalk from 'chalk';
|
|
18
|
+
import { QUICClient, SyncOptions, SyncResult } from './QUICClient.js';
|
|
19
|
+
import { QUICServer, SyncRequest } from './QUICServer.js';
|
|
20
|
+
|
|
21
|
+
// Database type from db-fallback
|
|
22
|
+
type Database = any;
|
|
23
|
+
|
|
24
|
+
export interface SyncCoordinatorConfig {
|
|
25
|
+
db: Database;
|
|
26
|
+
client?: QUICClient;
|
|
27
|
+
server?: QUICServer;
|
|
28
|
+
conflictStrategy?: 'local-wins' | 'remote-wins' | 'latest-wins' | 'merge';
|
|
29
|
+
batchSize?: number;
|
|
30
|
+
autoSync?: boolean;
|
|
31
|
+
syncIntervalMs?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface SyncState {
|
|
35
|
+
lastSyncAt: number;
|
|
36
|
+
lastEpisodeSync: number;
|
|
37
|
+
lastSkillSync: number;
|
|
38
|
+
lastEdgeSync: number;
|
|
39
|
+
totalItemsSynced: number;
|
|
40
|
+
totalBytesSynced: number;
|
|
41
|
+
syncCount: number;
|
|
42
|
+
lastError?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface SyncProgress {
|
|
46
|
+
phase: 'detecting' | 'pushing' | 'pulling' | 'resolving' | 'applying' | 'completed' | 'error';
|
|
47
|
+
current: number;
|
|
48
|
+
total: number;
|
|
49
|
+
itemType?: string;
|
|
50
|
+
message?: string;
|
|
51
|
+
error?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface SyncReport {
|
|
55
|
+
success: boolean;
|
|
56
|
+
startTime: number;
|
|
57
|
+
endTime: number;
|
|
58
|
+
durationMs: number;
|
|
59
|
+
itemsPushed: number;
|
|
60
|
+
itemsPulled: number;
|
|
61
|
+
conflictsResolved: number;
|
|
62
|
+
errors: string[];
|
|
63
|
+
bytesTransferred: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export class SyncCoordinator {
|
|
67
|
+
private db: Database;
|
|
68
|
+
private client?: QUICClient;
|
|
69
|
+
private server?: QUICServer;
|
|
70
|
+
private config: Required<Omit<SyncCoordinatorConfig, 'db' | 'client' | 'server'>>;
|
|
71
|
+
private syncState: SyncState;
|
|
72
|
+
private isSyncing: boolean = false;
|
|
73
|
+
private autoSyncInterval: NodeJS.Timeout | null = null;
|
|
74
|
+
|
|
75
|
+
constructor(config: SyncCoordinatorConfig) {
|
|
76
|
+
this.db = config.db;
|
|
77
|
+
this.client = config.client;
|
|
78
|
+
this.server = config.server;
|
|
79
|
+
this.config = {
|
|
80
|
+
conflictStrategy: config.conflictStrategy || 'latest-wins',
|
|
81
|
+
batchSize: config.batchSize || 100,
|
|
82
|
+
autoSync: config.autoSync || false,
|
|
83
|
+
syncIntervalMs: config.syncIntervalMs || 60000, // 1 minute
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// Load sync state
|
|
87
|
+
this.syncState = this.loadSyncState();
|
|
88
|
+
|
|
89
|
+
// Start auto-sync if enabled
|
|
90
|
+
if (this.config.autoSync) {
|
|
91
|
+
this.startAutoSync();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Perform bidirectional synchronization
|
|
97
|
+
*/
|
|
98
|
+
async sync(onProgress?: (progress: SyncProgress) => void): Promise<SyncReport> {
|
|
99
|
+
if (this.isSyncing) {
|
|
100
|
+
throw new Error('Sync already in progress');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!this.client) {
|
|
104
|
+
throw new Error('QUICClient not configured');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this.isSyncing = true;
|
|
108
|
+
const startTime = Date.now();
|
|
109
|
+
const errors: string[] = [];
|
|
110
|
+
let itemsPushed = 0;
|
|
111
|
+
let itemsPulled = 0;
|
|
112
|
+
let conflictsResolved = 0;
|
|
113
|
+
let bytesTransferred = 0;
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
console.log(chalk.blue('🔄 Starting synchronization...'));
|
|
117
|
+
|
|
118
|
+
// Phase 1: Detect changes
|
|
119
|
+
onProgress?.({ phase: 'detecting', current: 0, total: 100, message: 'Detecting changes...' });
|
|
120
|
+
const changes = await this.detectChanges();
|
|
121
|
+
console.log(chalk.gray(` Changes detected: ${changes.episodes.length + changes.skills.length + changes.edges.length} items`));
|
|
122
|
+
|
|
123
|
+
// Phase 2: Push changes to remote
|
|
124
|
+
if (changes.episodes.length > 0 || changes.skills.length > 0 || changes.edges.length > 0) {
|
|
125
|
+
onProgress?.({ phase: 'pushing', current: 0, total: changes.episodes.length + changes.skills.length + changes.edges.length });
|
|
126
|
+
const pushResult = await this.pushChanges(changes, onProgress);
|
|
127
|
+
itemsPushed = pushResult.itemsPushed;
|
|
128
|
+
bytesTransferred += pushResult.bytesTransferred;
|
|
129
|
+
errors.push(...pushResult.errors);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Phase 3: Pull changes from remote
|
|
133
|
+
onProgress?.({ phase: 'pulling', current: 0, total: 100, message: 'Pulling remote changes...' });
|
|
134
|
+
const pullResult = await this.pullChanges(onProgress);
|
|
135
|
+
itemsPulled = pullResult.itemsPulled;
|
|
136
|
+
bytesTransferred += pullResult.bytesTransferred;
|
|
137
|
+
errors.push(...pullResult.errors);
|
|
138
|
+
|
|
139
|
+
// Phase 4: Resolve conflicts
|
|
140
|
+
if (pullResult.conflicts && pullResult.conflicts.length > 0) {
|
|
141
|
+
onProgress?.({ phase: 'resolving', current: 0, total: pullResult.conflicts.length, message: 'Resolving conflicts...' });
|
|
142
|
+
conflictsResolved = await this.resolveConflicts(pullResult.conflicts);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Phase 5: Apply changes
|
|
146
|
+
onProgress?.({ phase: 'applying', current: 0, total: itemsPulled, message: 'Applying changes...' });
|
|
147
|
+
await this.applyChanges(pullResult.data);
|
|
148
|
+
|
|
149
|
+
// Update sync state
|
|
150
|
+
this.syncState.lastSyncAt = Date.now();
|
|
151
|
+
this.syncState.totalItemsSynced += itemsPushed + itemsPulled;
|
|
152
|
+
this.syncState.totalBytesSynced += bytesTransferred;
|
|
153
|
+
this.syncState.syncCount++;
|
|
154
|
+
this.syncState.lastError = errors.length > 0 ? errors[0] : undefined;
|
|
155
|
+
this.saveSyncState();
|
|
156
|
+
|
|
157
|
+
const endTime = Date.now();
|
|
158
|
+
const durationMs = endTime - startTime;
|
|
159
|
+
|
|
160
|
+
console.log(chalk.green('✓ Synchronization completed'));
|
|
161
|
+
console.log(chalk.gray(` Items pushed: ${itemsPushed}`));
|
|
162
|
+
console.log(chalk.gray(` Items pulled: ${itemsPulled}`));
|
|
163
|
+
console.log(chalk.gray(` Conflicts resolved: ${conflictsResolved}`));
|
|
164
|
+
console.log(chalk.gray(` Duration: ${durationMs}ms`));
|
|
165
|
+
|
|
166
|
+
onProgress?.({ phase: 'completed', current: 100, total: 100, message: 'Sync completed' });
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
success: errors.length === 0,
|
|
170
|
+
startTime,
|
|
171
|
+
endTime,
|
|
172
|
+
durationMs,
|
|
173
|
+
itemsPushed,
|
|
174
|
+
itemsPulled,
|
|
175
|
+
conflictsResolved,
|
|
176
|
+
errors,
|
|
177
|
+
bytesTransferred,
|
|
178
|
+
};
|
|
179
|
+
} catch (error) {
|
|
180
|
+
const err = error as Error;
|
|
181
|
+
const endTime = Date.now();
|
|
182
|
+
|
|
183
|
+
console.error(chalk.red('✗ Synchronization failed:'), err.message);
|
|
184
|
+
errors.push(err.message);
|
|
185
|
+
|
|
186
|
+
onProgress?.({ phase: 'error', current: 0, total: 0, error: err.message });
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
success: false,
|
|
190
|
+
startTime,
|
|
191
|
+
endTime,
|
|
192
|
+
durationMs: endTime - startTime,
|
|
193
|
+
itemsPushed,
|
|
194
|
+
itemsPulled,
|
|
195
|
+
conflictsResolved,
|
|
196
|
+
errors,
|
|
197
|
+
bytesTransferred,
|
|
198
|
+
};
|
|
199
|
+
} finally {
|
|
200
|
+
this.isSyncing = false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Detect changes since last sync
|
|
206
|
+
*/
|
|
207
|
+
private async detectChanges(): Promise<{
|
|
208
|
+
episodes: any[];
|
|
209
|
+
skills: any[];
|
|
210
|
+
edges: any[];
|
|
211
|
+
}> {
|
|
212
|
+
const { lastEpisodeSync, lastSkillSync, lastEdgeSync } = this.syncState;
|
|
213
|
+
|
|
214
|
+
// Detect new/modified episodes
|
|
215
|
+
const episodes = this.db
|
|
216
|
+
.prepare('SELECT * FROM episodes WHERE ts > ?')
|
|
217
|
+
.all(lastEpisodeSync);
|
|
218
|
+
|
|
219
|
+
// Detect new/modified skills
|
|
220
|
+
const skills = this.db
|
|
221
|
+
.prepare('SELECT * FROM skills WHERE ts > ?')
|
|
222
|
+
.all(lastSkillSync);
|
|
223
|
+
|
|
224
|
+
// Detect new/modified edges
|
|
225
|
+
const edges = this.db
|
|
226
|
+
.prepare('SELECT * FROM skill_edges WHERE ts > ?')
|
|
227
|
+
.all(lastEdgeSync);
|
|
228
|
+
|
|
229
|
+
return { episodes, skills, edges };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Push local changes to remote
|
|
234
|
+
*/
|
|
235
|
+
private async pushChanges(
|
|
236
|
+
changes: { episodes: any[]; skills: any[]; edges: any[] },
|
|
237
|
+
onProgress?: (progress: SyncProgress) => void
|
|
238
|
+
): Promise<{ itemsPushed: number; bytesTransferred: number; errors: string[] }> {
|
|
239
|
+
const errors: string[] = [];
|
|
240
|
+
let itemsPushed = 0;
|
|
241
|
+
let bytesTransferred = 0;
|
|
242
|
+
|
|
243
|
+
// Note: Push functionality would require server API to accept writes
|
|
244
|
+
// This is a placeholder for the push logic
|
|
245
|
+
console.log(chalk.yellow('⚠️ Push to remote not yet implemented (read-only sync)'));
|
|
246
|
+
|
|
247
|
+
return { itemsPushed, bytesTransferred, errors };
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Pull changes from remote
|
|
252
|
+
*/
|
|
253
|
+
private async pullChanges(
|
|
254
|
+
onProgress?: (progress: SyncProgress) => void
|
|
255
|
+
): Promise<{
|
|
256
|
+
itemsPulled: number;
|
|
257
|
+
bytesTransferred: number;
|
|
258
|
+
data: any;
|
|
259
|
+
conflicts?: any[];
|
|
260
|
+
errors: string[];
|
|
261
|
+
}> {
|
|
262
|
+
if (!this.client) {
|
|
263
|
+
throw new Error('QUICClient not configured');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const errors: string[] = [];
|
|
267
|
+
let itemsPulled = 0;
|
|
268
|
+
let bytesTransferred = 0;
|
|
269
|
+
const allData: any = { episodes: [], skills: [], edges: [] };
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
// Pull episodes
|
|
273
|
+
const episodesResult = await this.client.sync({
|
|
274
|
+
type: 'episodes',
|
|
275
|
+
since: this.syncState.lastEpisodeSync,
|
|
276
|
+
batchSize: this.config.batchSize,
|
|
277
|
+
onProgress: (progress) => {
|
|
278
|
+
onProgress?.({
|
|
279
|
+
phase: 'pulling',
|
|
280
|
+
current: progress.itemsSynced || 0,
|
|
281
|
+
total: 100,
|
|
282
|
+
itemType: 'episodes',
|
|
283
|
+
});
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
if (episodesResult.success && episodesResult.data) {
|
|
288
|
+
allData.episodes = episodesResult.data;
|
|
289
|
+
itemsPulled += episodesResult.itemsReceived;
|
|
290
|
+
bytesTransferred += episodesResult.bytesTransferred;
|
|
291
|
+
this.syncState.lastEpisodeSync = Date.now();
|
|
292
|
+
} else {
|
|
293
|
+
errors.push(episodesResult.error || 'Failed to sync episodes');
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Pull skills
|
|
297
|
+
const skillsResult = await this.client.sync({
|
|
298
|
+
type: 'skills',
|
|
299
|
+
since: this.syncState.lastSkillSync,
|
|
300
|
+
batchSize: this.config.batchSize,
|
|
301
|
+
onProgress: (progress) => {
|
|
302
|
+
onProgress?.({
|
|
303
|
+
phase: 'pulling',
|
|
304
|
+
current: progress.itemsSynced || 0,
|
|
305
|
+
total: 100,
|
|
306
|
+
itemType: 'skills',
|
|
307
|
+
});
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
if (skillsResult.success && skillsResult.data) {
|
|
312
|
+
allData.skills = skillsResult.data;
|
|
313
|
+
itemsPulled += skillsResult.itemsReceived;
|
|
314
|
+
bytesTransferred += skillsResult.bytesTransferred;
|
|
315
|
+
this.syncState.lastSkillSync = Date.now();
|
|
316
|
+
} else {
|
|
317
|
+
errors.push(skillsResult.error || 'Failed to sync skills');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Pull edges
|
|
321
|
+
const edgesResult = await this.client.sync({
|
|
322
|
+
type: 'edges',
|
|
323
|
+
since: this.syncState.lastEdgeSync,
|
|
324
|
+
batchSize: this.config.batchSize,
|
|
325
|
+
onProgress: (progress) => {
|
|
326
|
+
onProgress?.({
|
|
327
|
+
phase: 'pulling',
|
|
328
|
+
current: progress.itemsSynced || 0,
|
|
329
|
+
total: 100,
|
|
330
|
+
itemType: 'edges',
|
|
331
|
+
});
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
if (edgesResult.success && edgesResult.data) {
|
|
336
|
+
allData.edges = edgesResult.data;
|
|
337
|
+
itemsPulled += edgesResult.itemsReceived;
|
|
338
|
+
bytesTransferred += edgesResult.bytesTransferred;
|
|
339
|
+
this.syncState.lastEdgeSync = Date.now();
|
|
340
|
+
} else {
|
|
341
|
+
errors.push(edgesResult.error || 'Failed to sync edges');
|
|
342
|
+
}
|
|
343
|
+
} catch (error) {
|
|
344
|
+
const err = error as Error;
|
|
345
|
+
errors.push(err.message);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
itemsPulled,
|
|
350
|
+
bytesTransferred,
|
|
351
|
+
data: allData,
|
|
352
|
+
errors,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Resolve conflicts between local and remote data
|
|
358
|
+
*/
|
|
359
|
+
private async resolveConflicts(conflicts: any[]): Promise<number> {
|
|
360
|
+
let resolved = 0;
|
|
361
|
+
|
|
362
|
+
for (const conflict of conflicts) {
|
|
363
|
+
switch (this.config.conflictStrategy) {
|
|
364
|
+
case 'local-wins':
|
|
365
|
+
// Keep local version
|
|
366
|
+
break;
|
|
367
|
+
case 'remote-wins':
|
|
368
|
+
// Keep remote version
|
|
369
|
+
resolved++;
|
|
370
|
+
break;
|
|
371
|
+
case 'latest-wins':
|
|
372
|
+
// Keep version with latest timestamp
|
|
373
|
+
if (conflict.remote.ts > conflict.local.ts) {
|
|
374
|
+
resolved++;
|
|
375
|
+
}
|
|
376
|
+
break;
|
|
377
|
+
case 'merge':
|
|
378
|
+
// Attempt to merge (simplified)
|
|
379
|
+
resolved++;
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return resolved;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Apply pulled changes to local database
|
|
389
|
+
*/
|
|
390
|
+
private async applyChanges(data: any): Promise<void> {
|
|
391
|
+
// Apply episodes
|
|
392
|
+
if (data.episodes && data.episodes.length > 0) {
|
|
393
|
+
const stmt = this.db.prepare(`
|
|
394
|
+
INSERT OR REPLACE INTO episodes (
|
|
395
|
+
id, ts, session_id, task, input, output, critique, reward, success,
|
|
396
|
+
latency_ms, tokens_used, tags, metadata
|
|
397
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
398
|
+
`);
|
|
399
|
+
|
|
400
|
+
for (const episode of data.episodes) {
|
|
401
|
+
stmt.run(
|
|
402
|
+
episode.id,
|
|
403
|
+
episode.ts,
|
|
404
|
+
episode.sessionId,
|
|
405
|
+
episode.task,
|
|
406
|
+
episode.input,
|
|
407
|
+
episode.output,
|
|
408
|
+
episode.critique,
|
|
409
|
+
episode.reward,
|
|
410
|
+
episode.success ? 1 : 0,
|
|
411
|
+
episode.latencyMs,
|
|
412
|
+
episode.tokensUsed,
|
|
413
|
+
JSON.stringify(episode.tags),
|
|
414
|
+
JSON.stringify(episode.metadata)
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Apply skills
|
|
420
|
+
if (data.skills && data.skills.length > 0) {
|
|
421
|
+
const stmt = this.db.prepare(`
|
|
422
|
+
INSERT OR REPLACE INTO skills (
|
|
423
|
+
id, ts, name, description, code, success_rate, usage_count,
|
|
424
|
+
avg_reward, tags, metadata
|
|
425
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
426
|
+
`);
|
|
427
|
+
|
|
428
|
+
for (const skill of data.skills) {
|
|
429
|
+
stmt.run(
|
|
430
|
+
skill.id,
|
|
431
|
+
skill.ts,
|
|
432
|
+
skill.name,
|
|
433
|
+
skill.description,
|
|
434
|
+
skill.code,
|
|
435
|
+
skill.successRate,
|
|
436
|
+
skill.usageCount,
|
|
437
|
+
skill.avgReward,
|
|
438
|
+
JSON.stringify(skill.tags),
|
|
439
|
+
JSON.stringify(skill.metadata)
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Apply edges
|
|
445
|
+
if (data.edges && data.edges.length > 0) {
|
|
446
|
+
const stmt = this.db.prepare(`
|
|
447
|
+
INSERT OR REPLACE INTO skill_edges (
|
|
448
|
+
id, ts, from_skill_id, to_skill_id, weight, co_occurrences
|
|
449
|
+
) VALUES (?, ?, ?, ?, ?, ?)
|
|
450
|
+
`);
|
|
451
|
+
|
|
452
|
+
for (const edge of data.edges) {
|
|
453
|
+
stmt.run(
|
|
454
|
+
edge.id,
|
|
455
|
+
edge.ts,
|
|
456
|
+
edge.fromSkillId,
|
|
457
|
+
edge.toSkillId,
|
|
458
|
+
edge.weight,
|
|
459
|
+
edge.coOccurrences
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Load sync state from database
|
|
467
|
+
*/
|
|
468
|
+
private loadSyncState(): SyncState {
|
|
469
|
+
try {
|
|
470
|
+
const row = this.db
|
|
471
|
+
.prepare('SELECT * FROM sync_state WHERE id = 1')
|
|
472
|
+
.get();
|
|
473
|
+
|
|
474
|
+
if (row) {
|
|
475
|
+
return {
|
|
476
|
+
lastSyncAt: row.last_sync_at,
|
|
477
|
+
lastEpisodeSync: row.last_episode_sync,
|
|
478
|
+
lastSkillSync: row.last_skill_sync,
|
|
479
|
+
lastEdgeSync: row.last_edge_sync,
|
|
480
|
+
totalItemsSynced: row.total_items_synced,
|
|
481
|
+
totalBytesSynced: row.total_bytes_synced,
|
|
482
|
+
syncCount: row.sync_count,
|
|
483
|
+
lastError: row.last_error,
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
} catch (error) {
|
|
487
|
+
// Table might not exist yet
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return {
|
|
491
|
+
lastSyncAt: 0,
|
|
492
|
+
lastEpisodeSync: 0,
|
|
493
|
+
lastSkillSync: 0,
|
|
494
|
+
lastEdgeSync: 0,
|
|
495
|
+
totalItemsSynced: 0,
|
|
496
|
+
totalBytesSynced: 0,
|
|
497
|
+
syncCount: 0,
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Save sync state to database
|
|
503
|
+
*/
|
|
504
|
+
private saveSyncState(): void {
|
|
505
|
+
try {
|
|
506
|
+
// Create table if not exists
|
|
507
|
+
this.db.exec(`
|
|
508
|
+
CREATE TABLE IF NOT EXISTS sync_state (
|
|
509
|
+
id INTEGER PRIMARY KEY,
|
|
510
|
+
last_sync_at INTEGER,
|
|
511
|
+
last_episode_sync INTEGER,
|
|
512
|
+
last_skill_sync INTEGER,
|
|
513
|
+
last_edge_sync INTEGER,
|
|
514
|
+
total_items_synced INTEGER,
|
|
515
|
+
total_bytes_synced INTEGER,
|
|
516
|
+
sync_count INTEGER,
|
|
517
|
+
last_error TEXT
|
|
518
|
+
)
|
|
519
|
+
`);
|
|
520
|
+
|
|
521
|
+
// Upsert state
|
|
522
|
+
this.db
|
|
523
|
+
.prepare(`
|
|
524
|
+
INSERT OR REPLACE INTO sync_state (
|
|
525
|
+
id, last_sync_at, last_episode_sync, last_skill_sync, last_edge_sync,
|
|
526
|
+
total_items_synced, total_bytes_synced, sync_count, last_error
|
|
527
|
+
) VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
528
|
+
`)
|
|
529
|
+
.run(
|
|
530
|
+
this.syncState.lastSyncAt,
|
|
531
|
+
this.syncState.lastEpisodeSync,
|
|
532
|
+
this.syncState.lastSkillSync,
|
|
533
|
+
this.syncState.lastEdgeSync,
|
|
534
|
+
this.syncState.totalItemsSynced,
|
|
535
|
+
this.syncState.totalBytesSynced,
|
|
536
|
+
this.syncState.syncCount,
|
|
537
|
+
this.syncState.lastError || null
|
|
538
|
+
);
|
|
539
|
+
} catch (error) {
|
|
540
|
+
const err = error as Error;
|
|
541
|
+
console.error(chalk.red('✗ Failed to save sync state:'), err.message);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Start automatic synchronization
|
|
547
|
+
*/
|
|
548
|
+
private startAutoSync(): void {
|
|
549
|
+
if (this.autoSyncInterval) {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
console.log(chalk.blue(`🔄 Auto-sync enabled (interval: ${this.config.syncIntervalMs}ms)`));
|
|
554
|
+
|
|
555
|
+
this.autoSyncInterval = setInterval(async () => {
|
|
556
|
+
try {
|
|
557
|
+
await this.sync();
|
|
558
|
+
} catch (error) {
|
|
559
|
+
const err = error as Error;
|
|
560
|
+
console.error(chalk.red('✗ Auto-sync failed:'), err.message);
|
|
561
|
+
}
|
|
562
|
+
}, this.config.syncIntervalMs);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Stop automatic synchronization
|
|
567
|
+
*/
|
|
568
|
+
stopAutoSync(): void {
|
|
569
|
+
if (this.autoSyncInterval) {
|
|
570
|
+
clearInterval(this.autoSyncInterval);
|
|
571
|
+
this.autoSyncInterval = null;
|
|
572
|
+
console.log(chalk.blue('🔄 Auto-sync disabled'));
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Get sync state
|
|
578
|
+
*/
|
|
579
|
+
getSyncState(): SyncState {
|
|
580
|
+
return { ...this.syncState };
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Get sync status
|
|
585
|
+
*/
|
|
586
|
+
getStatus(): {
|
|
587
|
+
isSyncing: boolean;
|
|
588
|
+
autoSyncEnabled: boolean;
|
|
589
|
+
state: SyncState;
|
|
590
|
+
} {
|
|
591
|
+
return {
|
|
592
|
+
isSyncing: this.isSyncing,
|
|
593
|
+
autoSyncEnabled: this.autoSyncInterval !== null,
|
|
594
|
+
state: this.getSyncState(),
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
}
|
|
@@ -67,6 +67,8 @@ export class WASMVectorSearch {
|
|
|
67
67
|
|
|
68
68
|
try {
|
|
69
69
|
// Try to load ReasoningBank WASM module
|
|
70
|
+
// Note: This requires the ReasoningBank WASM module to be built and available
|
|
71
|
+
// If not available, the system gracefully falls back to optimized JavaScript
|
|
70
72
|
const wasmPath = '../../../agentic-flow/wasm/reasoningbank/reasoningbank_wasm.js';
|
|
71
73
|
const { ReasoningBankWasm } = await import(wasmPath);
|
|
72
74
|
|
|
@@ -76,10 +78,17 @@ export class WASMVectorSearch {
|
|
|
76
78
|
|
|
77
79
|
this.wasmModule = ReasoningBankWasm;
|
|
78
80
|
this.wasmAvailable = true;
|
|
79
|
-
console.log('[WASMVectorSearch] WASM acceleration enabled');
|
|
81
|
+
console.log('[WASMVectorSearch] ReasoningBank WASM acceleration enabled');
|
|
80
82
|
} catch (error) {
|
|
81
|
-
|
|
83
|
+
// Graceful fallback - the JavaScript implementation is still highly optimized
|
|
84
|
+
// with loop unrolling and batch processing
|
|
82
85
|
this.wasmAvailable = false;
|
|
86
|
+
|
|
87
|
+
// Only show detailed error in development
|
|
88
|
+
if (process.env.NODE_ENV === 'development') {
|
|
89
|
+
console.debug('[WASMVectorSearch] ReasoningBank WASM not available, using optimized JavaScript fallback');
|
|
90
|
+
console.debug('[WASMVectorSearch] Error:', (error as Error).message);
|
|
91
|
+
}
|
|
83
92
|
}
|
|
84
93
|
}
|
|
85
94
|
|
package/src/controllers/index.ts
CHANGED
|
@@ -9,9 +9,21 @@ export { SkillLibrary } from './SkillLibrary.js';
|
|
|
9
9
|
export { EmbeddingService } from './EmbeddingService.js';
|
|
10
10
|
export { WASMVectorSearch } from './WASMVectorSearch.js';
|
|
11
11
|
export { EnhancedEmbeddingService } from './EnhancedEmbeddingService.js';
|
|
12
|
+
export { MMRDiversityRanker } from './MMRDiversityRanker.js';
|
|
13
|
+
export { ContextSynthesizer } from './ContextSynthesizer.js';
|
|
14
|
+
export { MetadataFilter } from './MetadataFilter.js';
|
|
15
|
+
export { QUICServer } from './QUICServer.js';
|
|
16
|
+
export { QUICClient } from './QUICClient.js';
|
|
17
|
+
export { SyncCoordinator } from './SyncCoordinator.js';
|
|
12
18
|
|
|
13
19
|
export type { Episode, EpisodeWithEmbedding, ReflexionQuery } from './ReflexionMemory.js';
|
|
14
20
|
export type { Skill, SkillLink, SkillQuery } from './SkillLibrary.js';
|
|
15
21
|
export type { EmbeddingConfig } from './EmbeddingService.js';
|
|
16
22
|
export type { VectorSearchConfig, VectorSearchResult, VectorIndex } from './WASMVectorSearch.js';
|
|
17
23
|
export type { EnhancedEmbeddingConfig } from './EnhancedEmbeddingService.js';
|
|
24
|
+
export type { MMROptions, MMRCandidate } from './MMRDiversityRanker.js';
|
|
25
|
+
export type { MemoryPattern, SynthesizedContext } from './ContextSynthesizer.js';
|
|
26
|
+
export type { MetadataFilters, FilterableItem, FilterOperator, FilterValue } from './MetadataFilter.js';
|
|
27
|
+
export type { QUICServerConfig, SyncRequest, SyncResponse } from './QUICServer.js';
|
|
28
|
+
export type { QUICClientConfig, SyncOptions, SyncResult, SyncProgress } from './QUICClient.js';
|
|
29
|
+
export type { SyncCoordinatorConfig, SyncState, SyncReport } from './SyncCoordinator.js';
|