@kernel.chat/kbot 3.14.0 → 3.14.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/consultation.d.ts +117 -0
- package/dist/consultation.d.ts.map +1 -0
- package/dist/consultation.js +388 -0
- package/dist/consultation.js.map +1 -0
- package/dist/packages/kbot/src/tools/mcp-client.d.ts +2 -0
- package/dist/packages/kbot/src/tools/mcp-client.d.ts.map +1 -0
- package/dist/packages/kbot/src/tools/mcp-client.js +132 -0
- package/dist/packages/kbot/src/tools/mcp-client.js.map +1 -0
- package/dist/packages/kbot/src/tools/mcp-discovery.d.ts +2 -0
- package/dist/packages/kbot/src/tools/mcp-discovery.d.ts.map +1 -0
- package/dist/packages/kbot/src/tools/mcp-discovery.js +433 -0
- package/dist/packages/kbot/src/tools/mcp-discovery.js.map +1 -0
- package/dist/packages/kbot/src/tools/memory-harness.d.ts +2 -0
- package/dist/packages/kbot/src/tools/memory-harness.d.ts.map +1 -0
- package/dist/packages/kbot/src/tools/memory-harness.js +542 -0
- package/dist/packages/kbot/src/tools/memory-harness.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
`` `typescript
|
|
2
|
+
import { Tool, ToolResult, z } from 'zod';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
|
|
6
|
+
export interface MemoryEntry {
|
|
7
|
+
id: string;
|
|
8
|
+
timestamp: number;
|
|
9
|
+
type: 'user_message' | 'agent_response' | 'tool_call' | 'tool_result' | 'thought';
|
|
10
|
+
content: string;
|
|
11
|
+
context: {
|
|
12
|
+
sessionId: string;
|
|
13
|
+
userId: string;
|
|
14
|
+
userIntent?: string;
|
|
15
|
+
reasoning?: string;
|
|
16
|
+
};
|
|
17
|
+
tags: string[];
|
|
18
|
+
embeddingVector?: number[];
|
|
19
|
+
relevanceScore?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface MemoryGraph {
|
|
23
|
+
nodes: Map<string, MemoryEntry>;
|
|
24
|
+
edges: Map<string, Set<string>>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface MemoryQuery {
|
|
28
|
+
query: string;
|
|
29
|
+
filters?: {
|
|
30
|
+
type?: string | { $in: string[] };
|
|
31
|
+
tags?: string[];
|
|
32
|
+
timeframe?: {
|
|
33
|
+
from?: Date;
|
|
34
|
+
to?: Date;
|
|
35
|
+
};
|
|
36
|
+
relatedToId?: string;
|
|
37
|
+
};
|
|
38
|
+
limit?: number;
|
|
39
|
+
maxDepth?: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class MemoryHarness {
|
|
43
|
+
private memoryPath: string;
|
|
44
|
+
private memoryGraph: MemoryGraph;
|
|
45
|
+
private sessionId: string;
|
|
46
|
+
private userId: string;
|
|
47
|
+
private embeddingModel: (text: string) => Promise<number[]>;
|
|
48
|
+
private vectorStore: Map<string, number[]>;
|
|
49
|
+
|
|
50
|
+
constructor(
|
|
51
|
+
memoryPath: string = './.kbot/memory',
|
|
52
|
+
embeddingModel: (text: string) => Promise<number[]>
|
|
53
|
+
) {
|
|
54
|
+
this.memoryPath = memoryPath;
|
|
55
|
+
this.embeddingModel = embeddingModel;
|
|
56
|
+
this.vectorStore = new Map();
|
|
57
|
+
this.memoryGraph = { nodes: new Map(), edges: new Map() };
|
|
58
|
+
this.sessionId = Date.now().toString();
|
|
59
|
+
this.ensureDirectory();
|
|
60
|
+
this.loadMemory();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private ensureDirectory(): void {
|
|
64
|
+
if (!fs.existsSync(this.memoryPath)) {
|
|
65
|
+
fs.mkdirSync(this.memoryPath, { recursive: true });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private async generateEmbedding(text: string): Promise<number[]> {
|
|
70
|
+
const encoded = encodeURIComponent(text);
|
|
71
|
+
// Simple placeholder - in production, use a real embedding model
|
|
72
|
+
// This simulates vector generation for demonstration
|
|
73
|
+
const hash = await this.hashString(encoded);
|
|
74
|
+
return Array.from({ length: 128 }, () =>
|
|
75
|
+
Math.abs(hash.charCodeAt(Math.floor(Math.random() * hash.length)) % 256)
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private async hashString(str: string): Promise<string> {
|
|
80
|
+
const buffer = Buffer.from(str, 'utf-8');
|
|
81
|
+
const hash = await crypto.subtle.digest('SHA-256', buffer);
|
|
82
|
+
return Array.from(new Uint8Array(hash)).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private generateId(): string {
|
|
86
|
+
return `;
|
|
87
|
+
$;
|
|
88
|
+
{
|
|
89
|
+
this.sessionId;
|
|
90
|
+
}
|
|
91
|
+
_$;
|
|
92
|
+
{
|
|
93
|
+
Date.now();
|
|
94
|
+
}
|
|
95
|
+
_$;
|
|
96
|
+
{
|
|
97
|
+
Math.random().toString(36).substring(7);
|
|
98
|
+
}
|
|
99
|
+
`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private extractTags(content: string, context: MemoryEntry['context']): string[] {
|
|
103
|
+
const tags = ['agent', 'memory'];
|
|
104
|
+
|
|
105
|
+
if (context.userIntent) {
|
|
106
|
+
tags.push('intent');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (context.reasoning) {
|
|
110
|
+
tags.push('reasoning');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (content.match(/tool_call|tool_result/i)) {
|
|
114
|
+
tags.push('execution');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (content.match(/thought/i)) {
|
|
118
|
+
tags.push('thought');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Extract keywords
|
|
122
|
+
const keywords = this.extractKeywords(content, context);
|
|
123
|
+
tags.push(...keywords.slice(0, 3));
|
|
124
|
+
|
|
125
|
+
return tags;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private extractKeywords(content: string, context: MemoryEntry['context']): string[] {
|
|
129
|
+
const words = content.match(/\b[a-z]{4,}\b/gi)?.map(w => w.toLowerCase()) || [];
|
|
130
|
+
const stopWords = new Set([
|
|
131
|
+
'the', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
|
|
132
|
+
'and', 'or', 'but', 'if', 'then', 'else', 'when', 'while',
|
|
133
|
+
'of', 'at', 'by', 'for', 'with', 'about', 'against',
|
|
134
|
+
'after', 'before', 'between', 'through', 'during',
|
|
135
|
+
'into', 'onto', 'upon', 'over', 'under', 'above',
|
|
136
|
+
'from', 'up', 'down', 'in', 'on', 'off', 'out',
|
|
137
|
+
'to', 'via', 'per', 'as', 'by',
|
|
138
|
+
'i', 'you', 'we', 'they', 'he', 'she', 'it',
|
|
139
|
+
'a', 'an', 'the',
|
|
140
|
+
'this', 'that', 'these', 'those',
|
|
141
|
+
'what', 'which', 'who', 'whom', 'whose',
|
|
142
|
+
'where', 'when', 'why', 'how',
|
|
143
|
+
'yes', 'no', 'maybe', 'perhaps',
|
|
144
|
+
'ok', 'okay', 'right', 'sure', 'certainly',
|
|
145
|
+
'please', 'thank', 'thanks', 'sorry',
|
|
146
|
+
'i', 'me', 'my', 'mine',
|
|
147
|
+
'you', 'your', 'yours',
|
|
148
|
+
'he', 'him', 'his', 'herself', 'himself', 'itself',
|
|
149
|
+
'she', 'her', 'herself',
|
|
150
|
+
'they', 'them', 'their', 'theirs', 'themselves'
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
return words
|
|
154
|
+
.filter(w => !stopWords.has(w) && w.length > 3)
|
|
155
|
+
.slice(0, 5);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async saveEntry(entry: MemoryEntry): Promise<void> {
|
|
159
|
+
entry.id = this.generateId();
|
|
160
|
+
entry.timestamp = Date.now();
|
|
161
|
+
entry.context.sessionId = this.sessionId;
|
|
162
|
+
entry.context.userId = this.userId;
|
|
163
|
+
|
|
164
|
+
// Extract tags from content and context
|
|
165
|
+
entry.tags = this.extractTags(entry.content, entry.context);
|
|
166
|
+
|
|
167
|
+
// Generate embedding if not provided
|
|
168
|
+
if (!entry.embeddingVector) {
|
|
169
|
+
entry.embeddingVector = await this.generateEmbedding(entry.content);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
this.memoryGraph.nodes.set(entry.id, entry);
|
|
173
|
+
this.vectorStore.set(entry.id, entry.embeddingVector!);
|
|
174
|
+
|
|
175
|
+
// Save to persistent storage
|
|
176
|
+
this.saveToDisk();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private async saveToDisk(): Promise<void> {
|
|
180
|
+
const data = JSON.stringify({
|
|
181
|
+
sessionId: this.sessionId,
|
|
182
|
+
userId: this.userId,
|
|
183
|
+
timestamp: Date.now(),
|
|
184
|
+
graph: {
|
|
185
|
+
nodes: Array.from(this.memoryGraph.nodes.entries()),
|
|
186
|
+
edges: Array.from(this.memoryGraph.edges.entries()).map(([from, to]) => ({ from, to }))
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const filePath = path.join(this.memoryPath, `;
|
|
191
|
+
session_$;
|
|
192
|
+
{
|
|
193
|
+
this.sessionId;
|
|
194
|
+
}
|
|
195
|
+
json `);
|
|
196
|
+
fs.writeFileSync(filePath, data);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async loadMemory(): Promise<void> {
|
|
200
|
+
const files = fs.readdirSync(this.memoryPath)
|
|
201
|
+
.filter(f => f.endsWith('.json'));
|
|
202
|
+
|
|
203
|
+
for (const file of files) {
|
|
204
|
+
const filePath = path.join(this.memoryPath, file);
|
|
205
|
+
const data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
206
|
+
|
|
207
|
+
this.sessionId = data.sessionId;
|
|
208
|
+
this.userId = data.userId;
|
|
209
|
+
|
|
210
|
+
for (const [id, entry] of data.graph.nodes) {
|
|
211
|
+
this.memoryGraph.nodes.set(id, entry as MemoryEntry);
|
|
212
|
+
this.vectorStore.set(id, entry.embeddingVector || []);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
for (const edge of data.graph.edges) {
|
|
216
|
+
this.memoryGraph.edges.get(edge.from) = this.memoryGraph.edges.get(edge.from) || new Set();
|
|
217
|
+
this.memoryGraph.edges.get(edge.from)!.add(edge.to);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async addEntry(content: string, type: MemoryEntry['type'], context: MemoryEntry['context']): Promise<string> {
|
|
223
|
+
const entry: MemoryEntry = {
|
|
224
|
+
id: '',
|
|
225
|
+
timestamp: Date.now(),
|
|
226
|
+
type,
|
|
227
|
+
content,
|
|
228
|
+
context,
|
|
229
|
+
tags: [],
|
|
230
|
+
embeddingVector: [],
|
|
231
|
+
relevanceScore: 0
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
await this.saveEntry(entry);
|
|
235
|
+
return entry.id;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async relatedToId(targetId: string, limit: number = 5): Promise<MemoryEntry[]> {
|
|
239
|
+
const targetEntry = this.memoryGraph.nodes.get(targetId);
|
|
240
|
+
if (!targetEntry) return [];
|
|
241
|
+
|
|
242
|
+
const relatedIds = new Set<string>();
|
|
243
|
+
const visited = new Set<string>();
|
|
244
|
+
const queue: string[] = [targetId];
|
|
245
|
+
const depth = 2; // Limit to 2 hops
|
|
246
|
+
|
|
247
|
+
let currentDepth = 0;
|
|
248
|
+
while (currentDepth < depth && queue.length > 0) {
|
|
249
|
+
const currentQueue = [...queue];
|
|
250
|
+
queue.length = 0;
|
|
251
|
+
|
|
252
|
+
for (const nodeId of currentQueue) {
|
|
253
|
+
if (visited.has(nodeId)) continue;
|
|
254
|
+
visited.add(nodeId);
|
|
255
|
+
relatedIds.add(nodeId);
|
|
256
|
+
|
|
257
|
+
const neighbors = this.memoryGraph.edges.get(nodeId);
|
|
258
|
+
if (neighbors) {
|
|
259
|
+
for (const neighborId of neighbors) {
|
|
260
|
+
if (!visited.has(neighborId)) {
|
|
261
|
+
queue.push(neighborId);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
currentDepth++;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const relatedEntries = Array.from(relatedIds)
|
|
271
|
+
.map(id => this.memoryGraph.nodes.get(id))
|
|
272
|
+
.filter((entry): entry is MemoryEntry => entry !== undefined)
|
|
273
|
+
.slice(0, limit);
|
|
274
|
+
|
|
275
|
+
return relatedEntries;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async queryMemory(query: MemoryQuery): Promise<MemoryEntry[]> {
|
|
279
|
+
const { query: queryText, filters, limit = 10, maxDepth = 3 } = query;
|
|
280
|
+
|
|
281
|
+
// Semantic similarity search
|
|
282
|
+
const queryEmbedding = await this.generateEmbedding(queryText);
|
|
283
|
+
|
|
284
|
+
// Calculate relevance scores for all entries
|
|
285
|
+
const scoredEntries = Array.from(this.memoryGraph.nodes.entries())
|
|
286
|
+
.map(([id, entry]) => {
|
|
287
|
+
let similarity = 0;
|
|
288
|
+
|
|
289
|
+
// Text similarity (simple keyword overlap)
|
|
290
|
+
if (entry.content) {
|
|
291
|
+
const queryWords = new Set(queryText.toLowerCase().match(/\b[a-z]{3,}\b/g)?.map(w => w.toLowerCase()) || []);
|
|
292
|
+
const entryWords = new Set(entry.content.toLowerCase().match(/\b[a-z]{3,}\b/g) || []);
|
|
293
|
+
const intersection = Array.from(queryWords).filter(w => entryWords.has(w));
|
|
294
|
+
similarity += Math.min(intersection.length / queryWords.size, 1) * 0.4;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Tag matching
|
|
298
|
+
if (filters?.tags && filters.tags.length > 0) {
|
|
299
|
+
const tagMatch = filters.tags.filter(t => entry.tags.includes(t)).length;
|
|
300
|
+
similarity += Math.min(tagMatch / filters.tags.length, 1) * 0.3;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Type filtering
|
|
304
|
+
if (filters?.type) {
|
|
305
|
+
if (typeof filters.type === 'string') {
|
|
306
|
+
if (entry.type !== filters.type) return null;
|
|
307
|
+
} else if (typeof filters.type === 'object' && '$in' in filters.type) {
|
|
308
|
+
if (!filters.type.$in.includes(entry.type)) return null;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Timeframe filtering
|
|
313
|
+
if (filters?.timeframe) {
|
|
314
|
+
const now = Date.now();
|
|
315
|
+
const minTime = filters.timeframe.from ? filters.timeframe.from.getTime() : -Infinity;
|
|
316
|
+
const maxTime = filters.timeframe.to ? filters.timeframe.to.getTime() : Infinity;
|
|
317
|
+
if (entry.timestamp < minTime || entry.timestamp > maxTime) return null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Related to specific entry
|
|
321
|
+
if (filters?.relatedToId) {
|
|
322
|
+
const related = this.relatedToId(filters.relatedToId, 100);
|
|
323
|
+
if (related.length === 0) return null;
|
|
324
|
+
// Check if this entry or its ancestors are in related set
|
|
325
|
+
const isRelated = this.isAncestor(entry.id, new Set(related.map(r => r.id)));
|
|
326
|
+
if (!isRelated) return null;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return { id, entry, score: similarity };
|
|
330
|
+
})
|
|
331
|
+
.filter((result): result is { id: string; entry: MemoryEntry; score: number } => result !== null);
|
|
332
|
+
|
|
333
|
+
// Sort by score and limit results
|
|
334
|
+
scoredEntries.sort((a, b) => b.score - a.score);
|
|
335
|
+
|
|
336
|
+
// Expand results to include related entries up to maxDepth
|
|
337
|
+
const expandedResults = new Map<string, { id: string; entry: MemoryEntry; score: number }>();
|
|
338
|
+
const visited = new Set<string>();
|
|
339
|
+
|
|
340
|
+
const expand = (seedId: string, currentDepth: number) => {
|
|
341
|
+
if (currentDepth > maxDepth) return;
|
|
342
|
+
|
|
343
|
+
const seedEntry = this.memoryGraph.nodes.get(seedId);
|
|
344
|
+
if (!seedEntry) return;
|
|
345
|
+
|
|
346
|
+
const related = this.relatedToId(seedId, Infinity);
|
|
347
|
+
for (const entry of related) {
|
|
348
|
+
if (visited.has(entry.id)) continue;
|
|
349
|
+
visited.add(entry.id);
|
|
350
|
+
expandedResults.set(entry.id, { id: entry.id, entry, score: a.score * 0.9 });
|
|
351
|
+
expand(entry.id, currentDepth + 1);
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
for (const { id, entry, score } of scoredEntries) {
|
|
356
|
+
expand(id, 0);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const finalResults = Array.from(expandedResults.entries())
|
|
360
|
+
.map(([id, { entry, score }]) => ({ id, entry, score }))
|
|
361
|
+
.sort((a, b) => b.score - a.score)
|
|
362
|
+
.map(r => r.entry)
|
|
363
|
+
.slice(0, limit);
|
|
364
|
+
|
|
365
|
+
return finalResults;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
private isAncestor(candidateId: string, visited: Set<string>): boolean {
|
|
369
|
+
if (visited.has(candidateId)) return false;
|
|
370
|
+
visited.add(candidateId);
|
|
371
|
+
|
|
372
|
+
const entry = this.memoryGraph.nodes.get(candidateId);
|
|
373
|
+
if (!entry) return false;
|
|
374
|
+
|
|
375
|
+
// Check if this entry has edges to any visited nodes
|
|
376
|
+
const neighbors = this.memoryGraph.edges.get(candidateId);
|
|
377
|
+
if (neighbors) {
|
|
378
|
+
for (const neighborId of neighbors) {
|
|
379
|
+
if (visited.has(neighborId)) {
|
|
380
|
+
return true;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
async recall(
|
|
389
|
+
sessionId: string | null = null,
|
|
390
|
+
context: string = '',
|
|
391
|
+
limit: number = 5
|
|
392
|
+
): Promise<MemoryEntry[]> {
|
|
393
|
+
const currentSession = sessionId || this.sessionId;
|
|
394
|
+
|
|
395
|
+
// Get all entries from current session
|
|
396
|
+
const sessionEntries = Array.from(this.memoryGraph.nodes.entries())
|
|
397
|
+
.filter(([_, entry]) => entry.context.sessionId === currentSession);
|
|
398
|
+
|
|
399
|
+
// Recursively find related entries across sessions
|
|
400
|
+
const allEntries: MemoryEntry[] = [];
|
|
401
|
+
const visited = new Set<string>();
|
|
402
|
+
const queue: string[] = sessionEntries.map(([id, _]) => id);
|
|
403
|
+
|
|
404
|
+
while (queue.length > 0 && allEntries.length < limit * 3) {
|
|
405
|
+
const currentId = queue.shift()!;
|
|
406
|
+
if (visited.has(currentId)) continue;
|
|
407
|
+
visited.add(currentId);
|
|
408
|
+
allEntries.push(this.memoryGraph.nodes.get(currentId)!);
|
|
409
|
+
|
|
410
|
+
// Add neighbors to queue for further exploration
|
|
411
|
+
const neighbors = this.memoryGraph.edges.get(currentId);
|
|
412
|
+
if (neighbors) {
|
|
413
|
+
for (const neighborId of neighbors) {
|
|
414
|
+
if (!visited.has(neighborId)) {
|
|
415
|
+
queue.push(neighborId);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Score and filter
|
|
422
|
+
const scored = allEntries.map(entry => ({
|
|
423
|
+
entry,
|
|
424
|
+
score: context
|
|
425
|
+
? this.similarityToContext(entry, context)
|
|
426
|
+
: entry.relevanceScore || 0
|
|
427
|
+
})).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
428
|
+
|
|
429
|
+
return scored.map(r => r.entry);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
private similarityToContext(entry: MemoryEntry, context: string): number {
|
|
433
|
+
const contextEmbedding = this.generateEmbedding(context);
|
|
434
|
+
const entryEmbedding = entry.embeddingVector || [];
|
|
435
|
+
|
|
436
|
+
// Cosine similarity
|
|
437
|
+
let dotProduct = 0;
|
|
438
|
+
let magnitudeA = 0;
|
|
439
|
+
let magnitudeB = 0;
|
|
440
|
+
|
|
441
|
+
for (let i = 0; i < Math.min(contextEmbedding.length, entryEmbedding.length); i++) {
|
|
442
|
+
dotProduct += contextEmbedding[i] * entryEmbedding[i];
|
|
443
|
+
magnitudeA += contextEmbedding[i] ** 2;
|
|
444
|
+
magnitudeB += entryEmbedding[i] ** 2;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (magnitudeA === 0 || magnitudeB === 0) return 0;
|
|
448
|
+
|
|
449
|
+
return dotProduct / (Math.sqrt(magnitudeA) * Math.sqrt(magnitudeB));
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
async export(): Promise<{
|
|
453
|
+
sessionId: string;
|
|
454
|
+
userId: string;
|
|
455
|
+
entries: MemoryEntry[];
|
|
456
|
+
}> {
|
|
457
|
+
return {
|
|
458
|
+
sessionId: this.sessionId,
|
|
459
|
+
userId: this.userId,
|
|
460
|
+
entries: Array.from(this.memoryGraph.nodes.values())
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async clear(): Promise<void> {
|
|
465
|
+
this.memoryGraph = { nodes: new Map(), edges: new Map() };
|
|
466
|
+
this.vectorStore.clear();
|
|
467
|
+
await this.saveToDisk();
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
export const memoryHarnessSchema = z.object({
|
|
472
|
+
sessionId: z.string().optional(),
|
|
473
|
+
userId: z.string().optional(),
|
|
474
|
+
context: z.string().optional().default(''),
|
|
475
|
+
limit: z.number().optional().default(5),
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
export const MemoryHarnessTool: Tool<typeof memoryHarnessSchema> = {
|
|
479
|
+
name: 'memory_harness',
|
|
480
|
+
description: 'Persistent, recursive memory system for storing and retrieving agent interactions. Supports semantic search, graph-based related entry finding, and local-first storage.',
|
|
481
|
+
parameters: memoryHarnessSchema,
|
|
482
|
+
execute: async ({ sessionId, userId, context, limit = 5 }: z.infer<typeof memoryHarnessSchema>) => {
|
|
483
|
+
const harness = new MemoryHarness();
|
|
484
|
+
harness.userId = userId || 'anonymous';
|
|
485
|
+
|
|
486
|
+
if (sessionId) {
|
|
487
|
+
harness.sessionId = sessionId;
|
|
488
|
+
await harness.loadMemory();
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (context) {
|
|
492
|
+
const results = await harness.recall(sessionId, context, limit);
|
|
493
|
+
return {
|
|
494
|
+
success: true,
|
|
495
|
+
results: results.map(r => ({
|
|
496
|
+
id: r.id,
|
|
497
|
+
type: r.type,
|
|
498
|
+
content: r.content.substring(0, 500) + (r.content.length > 500 ? '...' : ''),
|
|
499
|
+
timestamp: r.timestamp,
|
|
500
|
+
tags: r.tags
|
|
501
|
+
})),
|
|
502
|
+
total: results.length,
|
|
503
|
+
hint: 'Memory query complete. Entries retrieved based on semantic similarity to your context.'
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
return {
|
|
508
|
+
success: true,
|
|
509
|
+
message: 'Memory harness initialized. Use context parameter to query related memories.',
|
|
510
|
+
hint: 'Provide a context string to find memories related to your query.'
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
export async function addMemoryEntry(
|
|
516
|
+
content: string,
|
|
517
|
+
type: MemoryEntry['type'],
|
|
518
|
+
context: MemoryEntry['context']
|
|
519
|
+
): Promise<string> {
|
|
520
|
+
const harness = new MemoryHarness();
|
|
521
|
+
harness.userId = 'anonymous';
|
|
522
|
+
|
|
523
|
+
const entryId = await harness.addEntry(content, type, context);
|
|
524
|
+
|
|
525
|
+
// Auto-save to disk
|
|
526
|
+
await harness.saveToDisk();
|
|
527
|
+
|
|
528
|
+
return entryId;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
export async function findRelatedMemories(
|
|
532
|
+
targetId: string,
|
|
533
|
+
limit: number = 5
|
|
534
|
+
): Promise<MemoryEntry[]> {
|
|
535
|
+
const harness = new MemoryHarness();
|
|
536
|
+
|
|
537
|
+
const related = await harness.relatedToId(targetId, limit);
|
|
538
|
+
return related;
|
|
539
|
+
}
|
|
540
|
+
` ``;
|
|
541
|
+
export {};
|
|
542
|
+
//# sourceMappingURL=memory-harness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-harness.js","sourceRoot":"","sources":["../../../../../src/packages/kbot/src/tools/memory-harness.ts"],"names":[],"mappings":"AAAA,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAqFU,CAAA;AAAA,CAAC,CAAA;AAAA,CAAC;IAAA,IAAI,CAAC,SAAS,CAAA;AAAA,CAAC;AAAA,EAAE,CAAA;AAAA,CAAC;IAAA,IAAI,CAAC,GAAG,EAAE,CAAA;AAAA,CAAC;AAAA,EAAE,CAAA;AAAA,CAAC;IAAA,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;AAAA,CAAC;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iDA2FrC,CAAA;AAAA,SAAS,CAAA;AAAA,CAAC;IAAA,IAAI,CAAC,SAAS,CAAA;AAAA,CAAC;AAAC,IAAI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyV9E,CAAA,EAAE,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kernel.chat/kbot",
|
|
3
|
-
"version": "3.14.
|
|
3
|
+
"version": "3.14.5",
|
|
4
4
|
"description": "The only AI agent that builds its own tools. Self-evolving terminal AI: 290 tools, 23 agents, 20 providers. Runtime tool forging via forge_tool, Forge Registry for community tools, autopoietic health monitoring, immune self-audit agent. Cost-aware model routing, fallback chains, Bayesian skill routing. Embedded llama.cpp, MCP server, programmatic SDK. MIT.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|