@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.
Files changed (80) hide show
  1. package/README.md +401 -0
  2. package/__tests__/collection-manager.test.ts +332 -0
  3. package/__tests__/dependency-graph.test.ts +434 -0
  4. package/__tests__/enhanced-plugin-registry.test.ts +488 -0
  5. package/__tests__/plugin-registry.test.ts +368 -0
  6. package/__tests__/ruvector-bridge.test.ts +2429 -0
  7. package/__tests__/ruvector-integration.test.ts +1602 -0
  8. package/__tests__/ruvector-migrations.test.ts +1099 -0
  9. package/__tests__/ruvector-quantization.test.ts +846 -0
  10. package/__tests__/ruvector-streaming.test.ts +1088 -0
  11. package/__tests__/sdk.test.ts +325 -0
  12. package/__tests__/security.test.ts +348 -0
  13. package/__tests__/utils/ruvector-test-utils.ts +860 -0
  14. package/examples/plugin-creator/index.ts +636 -0
  15. package/examples/plugin-creator/plugin-creator.test.ts +312 -0
  16. package/examples/ruvector/README.md +288 -0
  17. package/examples/ruvector/attention-patterns.ts +394 -0
  18. package/examples/ruvector/basic-usage.ts +288 -0
  19. package/examples/ruvector/docker-compose.yml +75 -0
  20. package/examples/ruvector/gnn-analysis.ts +501 -0
  21. package/examples/ruvector/hyperbolic-hierarchies.ts +557 -0
  22. package/examples/ruvector/init-db.sql +119 -0
  23. package/examples/ruvector/quantization.ts +680 -0
  24. package/examples/ruvector/self-learning.ts +447 -0
  25. package/examples/ruvector/semantic-search.ts +576 -0
  26. package/examples/ruvector/streaming-large-data.ts +507 -0
  27. package/examples/ruvector/transactions.ts +594 -0
  28. package/examples/ruvector-plugins/hook-pattern-library.ts +486 -0
  29. package/examples/ruvector-plugins/index.ts +79 -0
  30. package/examples/ruvector-plugins/intent-router.ts +354 -0
  31. package/examples/ruvector-plugins/mcp-tool-optimizer.ts +424 -0
  32. package/examples/ruvector-plugins/reasoning-bank.ts +657 -0
  33. package/examples/ruvector-plugins/ruvector-plugins.test.ts +518 -0
  34. package/examples/ruvector-plugins/semantic-code-search.ts +498 -0
  35. package/examples/ruvector-plugins/shared/index.ts +20 -0
  36. package/examples/ruvector-plugins/shared/vector-utils.ts +257 -0
  37. package/examples/ruvector-plugins/sona-learning.ts +445 -0
  38. package/package.json +97 -0
  39. package/src/collections/collection-manager.ts +661 -0
  40. package/src/collections/index.ts +56 -0
  41. package/src/collections/official/index.ts +1040 -0
  42. package/src/core/base-plugin.ts +416 -0
  43. package/src/core/plugin-interface.ts +215 -0
  44. package/src/hooks/index.ts +685 -0
  45. package/src/index.ts +378 -0
  46. package/src/integrations/agentic-flow.ts +743 -0
  47. package/src/integrations/index.ts +88 -0
  48. package/src/integrations/ruvector/ARCHITECTURE.md +1245 -0
  49. package/src/integrations/ruvector/attention-advanced.ts +1040 -0
  50. package/src/integrations/ruvector/attention-executor.ts +782 -0
  51. package/src/integrations/ruvector/attention-mechanisms.ts +757 -0
  52. package/src/integrations/ruvector/attention.ts +1063 -0
  53. package/src/integrations/ruvector/gnn.ts +3050 -0
  54. package/src/integrations/ruvector/hyperbolic.ts +1948 -0
  55. package/src/integrations/ruvector/index.ts +394 -0
  56. package/src/integrations/ruvector/migrations/001_create_extension.sql +135 -0
  57. package/src/integrations/ruvector/migrations/002_create_vector_tables.sql +259 -0
  58. package/src/integrations/ruvector/migrations/003_create_indices.sql +328 -0
  59. package/src/integrations/ruvector/migrations/004_create_functions.sql +598 -0
  60. package/src/integrations/ruvector/migrations/005_create_attention_functions.sql +654 -0
  61. package/src/integrations/ruvector/migrations/006_create_gnn_functions.sql +728 -0
  62. package/src/integrations/ruvector/migrations/007_create_hyperbolic_functions.sql +762 -0
  63. package/src/integrations/ruvector/migrations/index.ts +35 -0
  64. package/src/integrations/ruvector/migrations/migrations.ts +647 -0
  65. package/src/integrations/ruvector/quantization.ts +2036 -0
  66. package/src/integrations/ruvector/ruvector-bridge.ts +2000 -0
  67. package/src/integrations/ruvector/self-learning.ts +2376 -0
  68. package/src/integrations/ruvector/streaming.ts +1737 -0
  69. package/src/integrations/ruvector/types.ts +1945 -0
  70. package/src/providers/index.ts +643 -0
  71. package/src/registry/dependency-graph.ts +568 -0
  72. package/src/registry/enhanced-plugin-registry.ts +994 -0
  73. package/src/registry/plugin-registry.ts +604 -0
  74. package/src/sdk/index.ts +563 -0
  75. package/src/security/index.ts +594 -0
  76. package/src/types/index.ts +446 -0
  77. package/src/workers/index.ts +700 -0
  78. package/tmp.json +0 -0
  79. package/tsconfig.json +25 -0
  80. 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;