@sparkleideas/plugins 3.0.0-alpha.10
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 +401 -0
- package/__tests__/collection-manager.test.ts +332 -0
- package/__tests__/dependency-graph.test.ts +434 -0
- package/__tests__/enhanced-plugin-registry.test.ts +488 -0
- package/__tests__/plugin-registry.test.ts +368 -0
- package/__tests__/ruvector-bridge.test.ts +2429 -0
- package/__tests__/ruvector-integration.test.ts +1602 -0
- package/__tests__/ruvector-migrations.test.ts +1099 -0
- package/__tests__/ruvector-quantization.test.ts +846 -0
- package/__tests__/ruvector-streaming.test.ts +1088 -0
- package/__tests__/sdk.test.ts +325 -0
- package/__tests__/security.test.ts +348 -0
- package/__tests__/utils/ruvector-test-utils.ts +860 -0
- package/examples/plugin-creator/index.ts +636 -0
- package/examples/plugin-creator/plugin-creator.test.ts +312 -0
- package/examples/ruvector/README.md +288 -0
- package/examples/ruvector/attention-patterns.ts +394 -0
- package/examples/ruvector/basic-usage.ts +288 -0
- package/examples/ruvector/docker-compose.yml +75 -0
- package/examples/ruvector/gnn-analysis.ts +501 -0
- package/examples/ruvector/hyperbolic-hierarchies.ts +557 -0
- package/examples/ruvector/init-db.sql +119 -0
- package/examples/ruvector/quantization.ts +680 -0
- package/examples/ruvector/self-learning.ts +447 -0
- package/examples/ruvector/semantic-search.ts +576 -0
- package/examples/ruvector/streaming-large-data.ts +507 -0
- package/examples/ruvector/transactions.ts +594 -0
- package/examples/ruvector-plugins/hook-pattern-library.ts +486 -0
- package/examples/ruvector-plugins/index.ts +79 -0
- package/examples/ruvector-plugins/intent-router.ts +354 -0
- package/examples/ruvector-plugins/mcp-tool-optimizer.ts +424 -0
- package/examples/ruvector-plugins/reasoning-bank.ts +657 -0
- package/examples/ruvector-plugins/ruvector-plugins.test.ts +518 -0
- package/examples/ruvector-plugins/semantic-code-search.ts +498 -0
- package/examples/ruvector-plugins/shared/index.ts +20 -0
- package/examples/ruvector-plugins/shared/vector-utils.ts +257 -0
- package/examples/ruvector-plugins/sona-learning.ts +445 -0
- package/package.json +97 -0
- package/src/collections/collection-manager.ts +661 -0
- package/src/collections/index.ts +56 -0
- package/src/collections/official/index.ts +1040 -0
- package/src/core/base-plugin.ts +416 -0
- package/src/core/plugin-interface.ts +215 -0
- package/src/hooks/index.ts +685 -0
- package/src/index.ts +378 -0
- package/src/integrations/agentic-flow.ts +743 -0
- package/src/integrations/index.ts +88 -0
- package/src/integrations/ruvector/ARCHITECTURE.md +1245 -0
- package/src/integrations/ruvector/attention-advanced.ts +1040 -0
- package/src/integrations/ruvector/attention-executor.ts +782 -0
- package/src/integrations/ruvector/attention-mechanisms.ts +757 -0
- package/src/integrations/ruvector/attention.ts +1063 -0
- package/src/integrations/ruvector/gnn.ts +3050 -0
- package/src/integrations/ruvector/hyperbolic.ts +1948 -0
- package/src/integrations/ruvector/index.ts +394 -0
- package/src/integrations/ruvector/migrations/001_create_extension.sql +135 -0
- package/src/integrations/ruvector/migrations/002_create_vector_tables.sql +259 -0
- package/src/integrations/ruvector/migrations/003_create_indices.sql +328 -0
- package/src/integrations/ruvector/migrations/004_create_functions.sql +598 -0
- package/src/integrations/ruvector/migrations/005_create_attention_functions.sql +654 -0
- package/src/integrations/ruvector/migrations/006_create_gnn_functions.sql +728 -0
- package/src/integrations/ruvector/migrations/007_create_hyperbolic_functions.sql +762 -0
- package/src/integrations/ruvector/migrations/index.ts +35 -0
- package/src/integrations/ruvector/migrations/migrations.ts +647 -0
- package/src/integrations/ruvector/quantization.ts +2036 -0
- package/src/integrations/ruvector/ruvector-bridge.ts +2000 -0
- package/src/integrations/ruvector/self-learning.ts +2376 -0
- package/src/integrations/ruvector/streaming.ts +1737 -0
- package/src/integrations/ruvector/types.ts +1945 -0
- package/src/providers/index.ts +643 -0
- package/src/registry/dependency-graph.ts +568 -0
- package/src/registry/enhanced-plugin-registry.ts +994 -0
- package/src/registry/plugin-registry.ts +604 -0
- package/src/sdk/index.ts +563 -0
- package/src/security/index.ts +594 -0
- package/src/types/index.ts +446 -0
- package/src/workers/index.ts +700 -0
- package/tmp.json +0 -0
- package/tsconfig.json +25 -0
- package/vitest.config.ts +23 -0
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ReasoningBank Integration Plugin
|
|
3
|
+
*
|
|
4
|
+
* Stores successful reasoning trajectories and retrieves them for similar problems.
|
|
5
|
+
* Uses @ruvector/wasm for vector storage with HNSW indexing (<1ms search).
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Store reasoning chains with embeddings
|
|
9
|
+
* - Retrieve similar past reasoning for new problems
|
|
10
|
+
* - Learn from successful/failed outcomes
|
|
11
|
+
* - Verdict judgment for quality scoring
|
|
12
|
+
* - Memory distillation for pattern extraction
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { reasoningBankPlugin } from '@sparkleideas/plugins/examples/ruvector-plugins';
|
|
17
|
+
* await getDefaultRegistry().register(reasoningBankPlugin);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
PluginBuilder,
|
|
23
|
+
MCPToolBuilder,
|
|
24
|
+
HookBuilder,
|
|
25
|
+
HookEvent,
|
|
26
|
+
HookPriority,
|
|
27
|
+
Security,
|
|
28
|
+
} from '../../src/index.js';
|
|
29
|
+
|
|
30
|
+
// Import shared vector utilities (consolidated from all plugins)
|
|
31
|
+
import {
|
|
32
|
+
IVectorDB,
|
|
33
|
+
createVectorDB,
|
|
34
|
+
generateHashEmbedding,
|
|
35
|
+
} from './shared/vector-utils.js';
|
|
36
|
+
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// Types
|
|
39
|
+
// ============================================================================
|
|
40
|
+
|
|
41
|
+
export interface ReasoningTrajectory {
|
|
42
|
+
id: string;
|
|
43
|
+
problem: string;
|
|
44
|
+
problemEmbedding?: Float32Array;
|
|
45
|
+
steps: ReasoningStep[];
|
|
46
|
+
outcome: 'success' | 'failure' | 'partial';
|
|
47
|
+
score: number;
|
|
48
|
+
metadata: {
|
|
49
|
+
taskType: string;
|
|
50
|
+
duration: number;
|
|
51
|
+
tokensUsed: number;
|
|
52
|
+
model?: string;
|
|
53
|
+
timestamp: Date;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface ReasoningStep {
|
|
58
|
+
thought: string;
|
|
59
|
+
action: string;
|
|
60
|
+
observation: string;
|
|
61
|
+
confidence: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface RetrievalResult {
|
|
65
|
+
trajectory: ReasoningTrajectory;
|
|
66
|
+
similarity: number;
|
|
67
|
+
applicability: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface VerdictJudgment {
|
|
71
|
+
trajectoryId: string;
|
|
72
|
+
verdict: 'accept' | 'reject' | 'revise';
|
|
73
|
+
score: number;
|
|
74
|
+
feedback: string;
|
|
75
|
+
improvements?: string[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ============================================================================
|
|
79
|
+
// ReasoningBank Core
|
|
80
|
+
// ============================================================================
|
|
81
|
+
|
|
82
|
+
export class ReasoningBank {
|
|
83
|
+
private vectorDb: IVectorDB | null = null;
|
|
84
|
+
private trajectories = new Map<string, ReasoningTrajectory>();
|
|
85
|
+
private dimensions: number;
|
|
86
|
+
private nextId = 1;
|
|
87
|
+
private initPromise: Promise<void> | null = null;
|
|
88
|
+
|
|
89
|
+
constructor(dimensions: number = 1536) {
|
|
90
|
+
this.dimensions = dimensions;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Initialize the vector database.
|
|
95
|
+
*/
|
|
96
|
+
async initialize(): Promise<void> {
|
|
97
|
+
if (this.vectorDb) return;
|
|
98
|
+
if (this.initPromise) return this.initPromise;
|
|
99
|
+
|
|
100
|
+
this.initPromise = (async () => {
|
|
101
|
+
this.vectorDb = await createVectorDB(this.dimensions);
|
|
102
|
+
})();
|
|
103
|
+
|
|
104
|
+
return this.initPromise;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private async ensureInitialized(): Promise<IVectorDB> {
|
|
108
|
+
await this.initialize();
|
|
109
|
+
return this.vectorDb!;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Store a reasoning trajectory.
|
|
114
|
+
*/
|
|
115
|
+
async store(trajectory: Omit<ReasoningTrajectory, 'id'>): Promise<string> {
|
|
116
|
+
const db = await this.ensureInitialized();
|
|
117
|
+
const id = `reasoning-${this.nextId++}`;
|
|
118
|
+
|
|
119
|
+
// Validate inputs
|
|
120
|
+
const safeProblem = Security.validateString(trajectory.problem, { maxLength: 10000 });
|
|
121
|
+
|
|
122
|
+
// Generate embedding from problem + steps
|
|
123
|
+
const embedding = trajectory.problemEmbedding ?? this.generateEmbedding(safeProblem);
|
|
124
|
+
|
|
125
|
+
const fullTrajectory: ReasoningTrajectory = {
|
|
126
|
+
...trajectory,
|
|
127
|
+
id,
|
|
128
|
+
problem: safeProblem,
|
|
129
|
+
problemEmbedding: embedding,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// Store in vector DB with HNSW indexing
|
|
133
|
+
db.insert(embedding, id, {
|
|
134
|
+
problem: safeProblem,
|
|
135
|
+
outcome: trajectory.outcome,
|
|
136
|
+
score: trajectory.score,
|
|
137
|
+
taskType: trajectory.metadata.taskType,
|
|
138
|
+
stepsCount: trajectory.steps.length,
|
|
139
|
+
timestamp: trajectory.metadata.timestamp.toISOString(),
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Store full trajectory
|
|
143
|
+
this.trajectories.set(id, fullTrajectory);
|
|
144
|
+
|
|
145
|
+
return id;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Retrieve similar reasoning trajectories (<1ms with HNSW).
|
|
150
|
+
*/
|
|
151
|
+
async retrieve(
|
|
152
|
+
problem: string,
|
|
153
|
+
options?: {
|
|
154
|
+
k?: number;
|
|
155
|
+
minScore?: number;
|
|
156
|
+
taskType?: string;
|
|
157
|
+
outcomeFilter?: 'success' | 'failure' | 'partial';
|
|
158
|
+
}
|
|
159
|
+
): Promise<RetrievalResult[]> {
|
|
160
|
+
const db = await this.ensureInitialized();
|
|
161
|
+
const k = options?.k ?? 5;
|
|
162
|
+
const minScore = options?.minScore ?? 0.5;
|
|
163
|
+
|
|
164
|
+
const safeProblem = Security.validateString(problem, { maxLength: 10000 });
|
|
165
|
+
const queryEmbedding = this.generateEmbedding(safeProblem);
|
|
166
|
+
|
|
167
|
+
// HNSW search - sub-millisecond for 10K+ vectors
|
|
168
|
+
const searchResults = db.search(queryEmbedding, k * 2);
|
|
169
|
+
|
|
170
|
+
const results: RetrievalResult[] = [];
|
|
171
|
+
|
|
172
|
+
for (const result of searchResults) {
|
|
173
|
+
if (result.score < minScore) continue;
|
|
174
|
+
|
|
175
|
+
const trajectory = this.trajectories.get(result.id);
|
|
176
|
+
if (!trajectory) continue;
|
|
177
|
+
|
|
178
|
+
// Apply filters
|
|
179
|
+
if (options?.taskType && trajectory.metadata.taskType !== options.taskType) continue;
|
|
180
|
+
if (options?.outcomeFilter && trajectory.outcome !== options.outcomeFilter) continue;
|
|
181
|
+
|
|
182
|
+
// Calculate applicability based on task type match and recency
|
|
183
|
+
const applicability = this.calculateApplicability(trajectory, safeProblem, options?.taskType);
|
|
184
|
+
|
|
185
|
+
results.push({
|
|
186
|
+
trajectory,
|
|
187
|
+
similarity: result.score,
|
|
188
|
+
applicability,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
if (results.length >= k) break;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return results.sort((a, b) => (b.similarity * b.applicability) - (a.similarity * a.applicability));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Judge a trajectory and update its score.
|
|
199
|
+
*/
|
|
200
|
+
async judge(judgment: VerdictJudgment): Promise<void> {
|
|
201
|
+
const trajectory = this.trajectories.get(judgment.trajectoryId);
|
|
202
|
+
if (!trajectory) {
|
|
203
|
+
throw new Error(`Trajectory ${judgment.trajectoryId} not found`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const db = await this.ensureInitialized();
|
|
207
|
+
|
|
208
|
+
// Update score based on verdict
|
|
209
|
+
const scoreAdjustment = {
|
|
210
|
+
accept: 0.1,
|
|
211
|
+
reject: -0.2,
|
|
212
|
+
revise: -0.05,
|
|
213
|
+
}[judgment.verdict];
|
|
214
|
+
|
|
215
|
+
trajectory.score = Math.max(0, Math.min(1, trajectory.score + scoreAdjustment));
|
|
216
|
+
|
|
217
|
+
// If rejected with low score, remove from index
|
|
218
|
+
if (judgment.verdict === 'reject' && trajectory.score < 0.2) {
|
|
219
|
+
db.delete(trajectory.id);
|
|
220
|
+
this.trajectories.delete(trajectory.id);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Distill patterns from successful trajectories.
|
|
226
|
+
*/
|
|
227
|
+
async distill(taskType?: string): Promise<{
|
|
228
|
+
patterns: string[];
|
|
229
|
+
commonSteps: string[];
|
|
230
|
+
avgSteps: number;
|
|
231
|
+
successRate: number;
|
|
232
|
+
}> {
|
|
233
|
+
const trajectories = Array.from(this.trajectories.values())
|
|
234
|
+
.filter(t => (!taskType || t.metadata.taskType === taskType) && t.score > 0.6);
|
|
235
|
+
|
|
236
|
+
if (trajectories.length === 0) {
|
|
237
|
+
return { patterns: [], commonSteps: [], avgSteps: 0, successRate: 0 };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const actionCounts = new Map<string, number>();
|
|
241
|
+
let totalSteps = 0;
|
|
242
|
+
let successCount = 0;
|
|
243
|
+
|
|
244
|
+
for (const t of trajectories) {
|
|
245
|
+
totalSteps += t.steps.length;
|
|
246
|
+
if (t.outcome === 'success') successCount++;
|
|
247
|
+
|
|
248
|
+
for (const step of t.steps) {
|
|
249
|
+
const count = actionCounts.get(step.action) ?? 0;
|
|
250
|
+
actionCounts.set(step.action, count + 1);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const commonSteps = Array.from(actionCounts.entries())
|
|
255
|
+
.sort((a, b) => b[1] - a[1])
|
|
256
|
+
.slice(0, 10)
|
|
257
|
+
.map(([action]) => action);
|
|
258
|
+
|
|
259
|
+
const patterns = this.extractPatterns(trajectories);
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
patterns,
|
|
263
|
+
commonSteps,
|
|
264
|
+
avgSteps: totalSteps / trajectories.length,
|
|
265
|
+
successRate: successCount / trajectories.length,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Get statistics about stored trajectories.
|
|
271
|
+
*/
|
|
272
|
+
getStats(): {
|
|
273
|
+
total: number;
|
|
274
|
+
byOutcome: Record<string, number>;
|
|
275
|
+
byTaskType: Record<string, number>;
|
|
276
|
+
avgScore: number;
|
|
277
|
+
} {
|
|
278
|
+
const trajectories = Array.from(this.trajectories.values());
|
|
279
|
+
|
|
280
|
+
const byOutcome: Record<string, number> = { success: 0, failure: 0, partial: 0 };
|
|
281
|
+
const byTaskType: Record<string, number> = {};
|
|
282
|
+
let totalScore = 0;
|
|
283
|
+
|
|
284
|
+
for (const t of trajectories) {
|
|
285
|
+
byOutcome[t.outcome]++;
|
|
286
|
+
byTaskType[t.metadata.taskType] = (byTaskType[t.metadata.taskType] ?? 0) + 1;
|
|
287
|
+
totalScore += t.score;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
total: trajectories.length,
|
|
292
|
+
byOutcome,
|
|
293
|
+
byTaskType,
|
|
294
|
+
avgScore: trajectories.length > 0 ? totalScore / trajectories.length : 0,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// =========================================================================
|
|
299
|
+
// Private Helpers
|
|
300
|
+
// =========================================================================
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* External embedding provider (optional - set via setEmbeddingProvider)
|
|
304
|
+
* When set, uses @sparkleideas/embeddings for high-quality embeddings
|
|
305
|
+
*/
|
|
306
|
+
private embeddingProvider: ((text: string) => Promise<Float32Array>) | null = null;
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Set external embedding provider from @sparkleideas/embeddings
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* ```typescript
|
|
313
|
+
* import { createEmbeddingService } from '@sparkleideas/embeddings';
|
|
314
|
+
* const embeddings = createEmbeddingService({ provider: 'transformers' });
|
|
315
|
+
* await embeddings.initialize();
|
|
316
|
+
* bank.setEmbeddingProvider(async (text) => {
|
|
317
|
+
* const result = await embeddings.embed(text);
|
|
318
|
+
* return result.embedding;
|
|
319
|
+
* });
|
|
320
|
+
* ```
|
|
321
|
+
*/
|
|
322
|
+
setEmbeddingProvider(provider: (text: string) => Promise<Float32Array>): void {
|
|
323
|
+
this.embeddingProvider = provider;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Generate embedding using external provider or fallback to hash-based
|
|
328
|
+
* Performance: <100ms with external provider, <1ms with hash fallback
|
|
329
|
+
*/
|
|
330
|
+
private generateEmbedding(text: string): Float32Array {
|
|
331
|
+
// Use synchronous hash-based fallback for immediate returns
|
|
332
|
+
// Async embeddings are handled by generateEmbeddingAsync
|
|
333
|
+
return this.generateHashEmbedding(text);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Generate embedding asynchronously using external provider if available
|
|
338
|
+
*/
|
|
339
|
+
async generateEmbeddingAsync(text: string): Promise<Float32Array> {
|
|
340
|
+
if (this.embeddingProvider) {
|
|
341
|
+
try {
|
|
342
|
+
return await this.embeddingProvider(text);
|
|
343
|
+
} catch (error) {
|
|
344
|
+
// Fallback to hash-based if provider fails
|
|
345
|
+
console.warn('[ReasoningBank] Embedding provider failed, using fallback:', error);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return this.generateHashEmbedding(text);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Hash-based embedding fallback (fast but low quality)
|
|
353
|
+
* Used when @sparkleideas/embeddings is not configured
|
|
354
|
+
*/
|
|
355
|
+
private generateHashEmbedding(text: string): Float32Array {
|
|
356
|
+
const embedding = new Float32Array(this.dimensions);
|
|
357
|
+
let hash = 0;
|
|
358
|
+
|
|
359
|
+
for (let i = 0; i < text.length; i++) {
|
|
360
|
+
hash = ((hash << 5) - hash) + text.charCodeAt(i);
|
|
361
|
+
hash = hash & hash;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
for (let i = 0; i < this.dimensions; i++) {
|
|
365
|
+
embedding[i] = Math.sin(hash * (i + 1) * 0.001) * 0.5 + 0.5;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// L2 Normalize
|
|
369
|
+
let norm = 0;
|
|
370
|
+
for (let i = 0; i < this.dimensions; i++) {
|
|
371
|
+
norm += embedding[i] * embedding[i];
|
|
372
|
+
}
|
|
373
|
+
norm = Math.sqrt(norm);
|
|
374
|
+
for (let i = 0; i < this.dimensions; i++) {
|
|
375
|
+
embedding[i] /= norm;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return embedding;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
private calculateApplicability(
|
|
382
|
+
trajectory: ReasoningTrajectory,
|
|
383
|
+
_problem: string,
|
|
384
|
+
taskType?: string
|
|
385
|
+
): number {
|
|
386
|
+
let score = trajectory.score;
|
|
387
|
+
|
|
388
|
+
if (taskType && trajectory.metadata.taskType === taskType) {
|
|
389
|
+
score *= 1.2;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (trajectory.outcome === 'success') {
|
|
393
|
+
score *= 1.1;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const age = Date.now() - trajectory.metadata.timestamp.getTime();
|
|
397
|
+
const daysSinceCreation = age / (1000 * 60 * 60 * 24);
|
|
398
|
+
if (daysSinceCreation > 7) {
|
|
399
|
+
score *= Math.exp(-0.05 * (daysSinceCreation - 7));
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return Math.min(1, score);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
private extractPatterns(trajectories: ReasoningTrajectory[]): string[] {
|
|
406
|
+
const patterns: string[] = [];
|
|
407
|
+
const sequences = new Map<string, number>();
|
|
408
|
+
|
|
409
|
+
for (const t of trajectories) {
|
|
410
|
+
for (let i = 0; i < t.steps.length - 1; i++) {
|
|
411
|
+
const seq = `${t.steps[i].action} → ${t.steps[i + 1].action}`;
|
|
412
|
+
sequences.set(seq, (sequences.get(seq) ?? 0) + 1);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
for (const [seq, count] of sequences) {
|
|
417
|
+
if (count >= 2) {
|
|
418
|
+
patterns.push(`Common sequence: ${seq} (${count} occurrences)`);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return patterns.slice(0, 5);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// ============================================================================
|
|
427
|
+
// Plugin Definition
|
|
428
|
+
// ============================================================================
|
|
429
|
+
|
|
430
|
+
let reasoningBankInstance: ReasoningBank | null = null;
|
|
431
|
+
|
|
432
|
+
async function getReasoningBank(): Promise<ReasoningBank> {
|
|
433
|
+
if (!reasoningBankInstance) {
|
|
434
|
+
reasoningBankInstance = new ReasoningBank(1536);
|
|
435
|
+
await reasoningBankInstance.initialize();
|
|
436
|
+
}
|
|
437
|
+
return reasoningBankInstance;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export const reasoningBankPlugin = new PluginBuilder('reasoning-bank', '1.0.0')
|
|
441
|
+
.withDescription('Store and retrieve reasoning trajectories using @ruvector/wasm HNSW indexing')
|
|
442
|
+
.withAuthor('Claude Flow Team')
|
|
443
|
+
.withTags(['reasoning', 'memory', 'learning', 'ruvector', 'hnsw'])
|
|
444
|
+
.withMCPTools([
|
|
445
|
+
new MCPToolBuilder('reasoning-store')
|
|
446
|
+
.withDescription('Store a reasoning trajectory for future retrieval')
|
|
447
|
+
.addStringParam('problem', 'The problem that was solved', { required: true })
|
|
448
|
+
.addStringParam('steps', 'JSON array of reasoning steps', { required: true })
|
|
449
|
+
.addStringParam('outcome', 'Outcome: success, failure, or partial', {
|
|
450
|
+
required: true,
|
|
451
|
+
enum: ['success', 'failure', 'partial'],
|
|
452
|
+
})
|
|
453
|
+
.addNumberParam('score', 'Quality score 0-1', { default: 0.7, minimum: 0, maximum: 1 })
|
|
454
|
+
.addStringParam('taskType', 'Type of task (coding, research, planning, etc.)', { required: true })
|
|
455
|
+
.withHandler(async (params) => {
|
|
456
|
+
try {
|
|
457
|
+
const steps = JSON.parse(params.steps as string) as ReasoningStep[];
|
|
458
|
+
const rb = await getReasoningBank();
|
|
459
|
+
|
|
460
|
+
const id = await rb.store({
|
|
461
|
+
problem: params.problem as string,
|
|
462
|
+
steps,
|
|
463
|
+
outcome: params.outcome as 'success' | 'failure' | 'partial',
|
|
464
|
+
score: params.score as number,
|
|
465
|
+
metadata: {
|
|
466
|
+
taskType: params.taskType as string,
|
|
467
|
+
duration: 0,
|
|
468
|
+
tokensUsed: 0,
|
|
469
|
+
timestamp: new Date(),
|
|
470
|
+
},
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
return {
|
|
474
|
+
content: [{
|
|
475
|
+
type: 'text',
|
|
476
|
+
text: `✅ Stored reasoning trajectory: ${id}\n` +
|
|
477
|
+
`Problem: ${(params.problem as string).substring(0, 100)}...\n` +
|
|
478
|
+
`Steps: ${steps.length}\n` +
|
|
479
|
+
`Outcome: ${params.outcome}\n` +
|
|
480
|
+
`Score: ${params.score}`,
|
|
481
|
+
}],
|
|
482
|
+
};
|
|
483
|
+
} catch (error) {
|
|
484
|
+
return {
|
|
485
|
+
content: [{ type: 'text', text: `❌ Error: ${error instanceof Error ? error.message : String(error)}` }],
|
|
486
|
+
isError: true,
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
})
|
|
490
|
+
.build(),
|
|
491
|
+
|
|
492
|
+
new MCPToolBuilder('reasoning-retrieve')
|
|
493
|
+
.withDescription('Retrieve similar reasoning trajectories (<1ms with HNSW)')
|
|
494
|
+
.addStringParam('problem', 'The problem to find similar reasoning for', { required: true })
|
|
495
|
+
.addNumberParam('k', 'Number of results', { default: 5 })
|
|
496
|
+
.addNumberParam('minScore', 'Minimum similarity score', { default: 0.5 })
|
|
497
|
+
.addStringParam('taskType', 'Filter by task type')
|
|
498
|
+
.addStringParam('outcomeFilter', 'Filter by outcome', { enum: ['success', 'failure', 'partial'] })
|
|
499
|
+
.withHandler(async (params) => {
|
|
500
|
+
try {
|
|
501
|
+
const rb = await getReasoningBank();
|
|
502
|
+
const results = await rb.retrieve(params.problem as string, {
|
|
503
|
+
k: params.k as number,
|
|
504
|
+
minScore: params.minScore as number,
|
|
505
|
+
taskType: params.taskType as string | undefined,
|
|
506
|
+
outcomeFilter: params.outcomeFilter as 'success' | 'failure' | 'partial' | undefined,
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
if (results.length === 0) {
|
|
510
|
+
return { content: [{ type: 'text', text: '📭 No similar reasoning found.' }] };
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const output = results.map((r, i) =>
|
|
514
|
+
`**${i + 1}. ${r.trajectory.id}** (similarity: ${(r.similarity * 100).toFixed(1)}%)\n` +
|
|
515
|
+
` Problem: ${r.trajectory.problem.substring(0, 80)}...\n` +
|
|
516
|
+
` Outcome: ${r.trajectory.outcome} | Steps: ${r.trajectory.steps.length}\n` +
|
|
517
|
+
` Actions: ${r.trajectory.steps.map(s => s.action).join(' → ')}`
|
|
518
|
+
).join('\n\n');
|
|
519
|
+
|
|
520
|
+
return {
|
|
521
|
+
content: [{ type: 'text', text: `📚 **Found ${results.length} similar trajectories:**\n\n${output}` }],
|
|
522
|
+
};
|
|
523
|
+
} catch (error) {
|
|
524
|
+
return {
|
|
525
|
+
content: [{ type: 'text', text: `❌ Error: ${error instanceof Error ? error.message : String(error)}` }],
|
|
526
|
+
isError: true,
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
})
|
|
530
|
+
.build(),
|
|
531
|
+
|
|
532
|
+
new MCPToolBuilder('reasoning-judge')
|
|
533
|
+
.withDescription('Judge a reasoning trajectory and update its score')
|
|
534
|
+
.addStringParam('trajectoryId', 'ID of the trajectory to judge', { required: true })
|
|
535
|
+
.addStringParam('verdict', 'Verdict: accept, reject, or revise', {
|
|
536
|
+
required: true,
|
|
537
|
+
enum: ['accept', 'reject', 'revise'],
|
|
538
|
+
})
|
|
539
|
+
.addStringParam('feedback', 'Feedback about the trajectory')
|
|
540
|
+
.withHandler(async (params) => {
|
|
541
|
+
try {
|
|
542
|
+
const rb = await getReasoningBank();
|
|
543
|
+
await rb.judge({
|
|
544
|
+
trajectoryId: params.trajectoryId as string,
|
|
545
|
+
verdict: params.verdict as 'accept' | 'reject' | 'revise',
|
|
546
|
+
score: params.verdict === 'accept' ? 0.1 : params.verdict === 'reject' ? -0.2 : -0.05,
|
|
547
|
+
feedback: (params.feedback as string) ?? '',
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
return {
|
|
551
|
+
content: [{
|
|
552
|
+
type: 'text',
|
|
553
|
+
text: `⚖️ Judged trajectory ${params.trajectoryId}: ${params.verdict}`,
|
|
554
|
+
}],
|
|
555
|
+
};
|
|
556
|
+
} catch (error) {
|
|
557
|
+
return {
|
|
558
|
+
content: [{ type: 'text', text: `❌ Error: ${error instanceof Error ? error.message : String(error)}` }],
|
|
559
|
+
isError: true,
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
})
|
|
563
|
+
.build(),
|
|
564
|
+
|
|
565
|
+
new MCPToolBuilder('reasoning-distill')
|
|
566
|
+
.withDescription('Extract common patterns from successful reasoning trajectories')
|
|
567
|
+
.addStringParam('taskType', 'Filter by task type (optional)')
|
|
568
|
+
.withHandler(async (params) => {
|
|
569
|
+
try {
|
|
570
|
+
const rb = await getReasoningBank();
|
|
571
|
+
const distilled = await rb.distill(params.taskType as string | undefined);
|
|
572
|
+
|
|
573
|
+
return {
|
|
574
|
+
content: [{
|
|
575
|
+
type: 'text',
|
|
576
|
+
text: `🧬 **Distilled Patterns${params.taskType ? ` for ${params.taskType}` : ''}:**\n\n` +
|
|
577
|
+
`**Success Rate:** ${(distilled.successRate * 100).toFixed(1)}%\n` +
|
|
578
|
+
`**Average Steps:** ${distilled.avgSteps.toFixed(1)}\n\n` +
|
|
579
|
+
`**Common Actions:**\n${distilled.commonSteps.map((s, i) => `${i + 1}. ${s}`).join('\n')}\n\n` +
|
|
580
|
+
`**Patterns:**\n${distilled.patterns.map((p, i) => `${i + 1}. ${p}`).join('\n') || 'None found yet'}`,
|
|
581
|
+
}],
|
|
582
|
+
};
|
|
583
|
+
} catch (error) {
|
|
584
|
+
return {
|
|
585
|
+
content: [{ type: 'text', text: `❌ Error: ${error instanceof Error ? error.message : String(error)}` }],
|
|
586
|
+
isError: true,
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
})
|
|
590
|
+
.build(),
|
|
591
|
+
|
|
592
|
+
new MCPToolBuilder('reasoning-stats')
|
|
593
|
+
.withDescription('Get statistics about stored reasoning trajectories')
|
|
594
|
+
.withHandler(async () => {
|
|
595
|
+
const rb = await getReasoningBank();
|
|
596
|
+
const stats = rb.getStats();
|
|
597
|
+
|
|
598
|
+
return {
|
|
599
|
+
content: [{
|
|
600
|
+
type: 'text',
|
|
601
|
+
text: `📊 **ReasoningBank Statistics:**\n\n` +
|
|
602
|
+
`**Total Trajectories:** ${stats.total}\n` +
|
|
603
|
+
`**Backend:** @ruvector/wasm HNSW\n\n` +
|
|
604
|
+
`**By Outcome:**\n` +
|
|
605
|
+
` ✅ Success: ${stats.byOutcome.success}\n` +
|
|
606
|
+
` ❌ Failure: ${stats.byOutcome.failure}\n` +
|
|
607
|
+
` ⚠️ Partial: ${stats.byOutcome.partial}\n\n` +
|
|
608
|
+
`**By Task Type:**\n${Object.entries(stats.byTaskType).map(([type, count]) => ` • ${type}: ${count}`).join('\n') || ' None'}\n\n` +
|
|
609
|
+
`**Average Score:** ${(stats.avgScore * 100).toFixed(1)}%`,
|
|
610
|
+
}],
|
|
611
|
+
};
|
|
612
|
+
})
|
|
613
|
+
.build(),
|
|
614
|
+
])
|
|
615
|
+
.withHooks([
|
|
616
|
+
new HookBuilder(HookEvent.PostTaskComplete)
|
|
617
|
+
.withName('reasoning-auto-store')
|
|
618
|
+
.withDescription('Automatically store successful task reasoning')
|
|
619
|
+
.withPriority(HookPriority.Low)
|
|
620
|
+
.when((ctx) => {
|
|
621
|
+
const data = ctx.data as { success?: boolean; reasoning?: unknown[] } | undefined;
|
|
622
|
+
return data?.success === true && Array.isArray(data?.reasoning) && data.reasoning.length > 0;
|
|
623
|
+
})
|
|
624
|
+
.handle(async (ctx) => {
|
|
625
|
+
const data = ctx.data as { problem?: string; reasoning?: ReasoningStep[]; taskType?: string };
|
|
626
|
+
if (!data.problem || !data.reasoning) return { success: true };
|
|
627
|
+
|
|
628
|
+
try {
|
|
629
|
+
const rb = await getReasoningBank();
|
|
630
|
+
await rb.store({
|
|
631
|
+
problem: data.problem,
|
|
632
|
+
steps: data.reasoning,
|
|
633
|
+
outcome: 'success',
|
|
634
|
+
score: 0.8,
|
|
635
|
+
metadata: {
|
|
636
|
+
taskType: data.taskType ?? 'general',
|
|
637
|
+
duration: 0,
|
|
638
|
+
tokensUsed: 0,
|
|
639
|
+
timestamp: new Date(),
|
|
640
|
+
},
|
|
641
|
+
});
|
|
642
|
+
} catch {
|
|
643
|
+
// Silent fail for auto-store
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
return { success: true };
|
|
647
|
+
})
|
|
648
|
+
.build(),
|
|
649
|
+
])
|
|
650
|
+
.onInitialize(async (ctx) => {
|
|
651
|
+
ctx.logger.info('ReasoningBank plugin initializing with @ruvector/wasm...');
|
|
652
|
+
await getReasoningBank();
|
|
653
|
+
ctx.logger.info('ReasoningBank ready - HNSW indexing enabled');
|
|
654
|
+
})
|
|
655
|
+
.build();
|
|
656
|
+
|
|
657
|
+
export default reasoningBankPlugin;
|