@bluehawks/cli 1.0.3 → 1.0.4
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/.bluehawks/history.json +3 -12
- package/dist/config/constants.d.ts +1 -1
- package/dist/config/constants.js +1 -1
- package/dist/core/agents/orchestrator.d.ts.map +1 -1
- package/dist/core/agents/orchestrator.js +11 -1
- package/dist/core/agents/orchestrator.js.map +1 -1
- package/dist/core/api/client.d.ts +5 -1
- package/dist/core/api/client.d.ts.map +1 -1
- package/dist/core/api/client.js +10 -0
- package/dist/core/api/client.js.map +1 -1
- package/dist/core/api/types.d.ts +19 -0
- package/dist/core/api/types.d.ts.map +1 -1
- package/dist/core/api/types.js.map +1 -1
- package/dist/core/memory/index.d.ts +4 -0
- package/dist/core/memory/index.d.ts.map +1 -0
- package/dist/core/memory/index.js +4 -0
- package/dist/core/memory/index.js.map +1 -0
- package/dist/core/memory/manager.d.ts +29 -0
- package/dist/core/memory/manager.d.ts.map +1 -0
- package/dist/core/memory/manager.js +106 -0
- package/dist/core/memory/manager.js.map +1 -0
- package/dist/core/memory/storage.d.ts +15 -0
- package/dist/core/memory/storage.d.ts.map +1 -0
- package/dist/core/memory/storage.js +77 -0
- package/dist/core/memory/storage.js.map +1 -0
- package/dist/core/memory/types.d.ts +20 -0
- package/dist/core/memory/types.d.ts.map +1 -0
- package/dist/core/memory/types.js +2 -0
- package/dist/core/memory/types.js.map +1 -0
- package/dist/core/tools/definitions/index.d.ts +1 -0
- package/dist/core/tools/definitions/index.d.ts.map +1 -1
- package/dist/core/tools/definitions/index.js +3 -0
- package/dist/core/tools/definitions/index.js.map +1 -1
- package/dist/core/tools/definitions/memory.d.ts +2 -0
- package/dist/core/tools/definitions/memory.d.ts.map +1 -0
- package/dist/core/tools/definitions/memory.js +155 -0
- package/dist/core/tools/definitions/memory.js.map +1 -0
- package/package.json +3 -1
- package/src/config/constants.ts +1 -1
- package/src/core/agents/orchestrator.ts +13 -2
- package/src/core/api/client.ts +17 -0
- package/src/core/api/types.ts +23 -0
- package/src/core/memory/index.ts +3 -0
- package/src/core/memory/manager.ts +120 -0
- package/src/core/memory/storage.ts +90 -0
- package/src/core/memory/types.ts +22 -0
- package/src/core/tools/definitions/index.ts +3 -0
- package/src/core/tools/definitions/memory.ts +171 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { toolRegistry } from '../registry.js';
|
|
2
|
+
import { memoryManager } from '../../memory/index.js';
|
|
3
|
+
// Remember Tool
|
|
4
|
+
const rememberTool = {
|
|
5
|
+
name: 'remember',
|
|
6
|
+
safeToAutoRun: true,
|
|
7
|
+
definition: {
|
|
8
|
+
type: 'function',
|
|
9
|
+
function: {
|
|
10
|
+
name: 'remember',
|
|
11
|
+
description: 'Store a piece of information, preference, or concept in long-term memory. Use this to remember user choices, project guidelines, or important facts.',
|
|
12
|
+
parameters: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
content: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
description: 'The information to remember.',
|
|
18
|
+
},
|
|
19
|
+
type: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
enum: ['preference', 'mistake', 'knowledge', 'task_context'],
|
|
22
|
+
description: 'The type of memory. Default is knowledge.',
|
|
23
|
+
},
|
|
24
|
+
tags: {
|
|
25
|
+
type: 'array',
|
|
26
|
+
items: { type: 'string' },
|
|
27
|
+
description: 'Tags to categorize this memory.',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
required: ['content'],
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
async execute(args) {
|
|
35
|
+
const content = args.content;
|
|
36
|
+
const type = args.type || 'knowledge';
|
|
37
|
+
const tags = args.tags || [];
|
|
38
|
+
const memory = await memoryManager.remember(content, type, { tags });
|
|
39
|
+
return `Remembered: "${content}" (ID: ${memory.id})`;
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
// Recall Tool
|
|
43
|
+
const recallTool = {
|
|
44
|
+
name: 'recall',
|
|
45
|
+
safeToAutoRun: true,
|
|
46
|
+
definition: {
|
|
47
|
+
type: 'function',
|
|
48
|
+
function: {
|
|
49
|
+
name: 'recall',
|
|
50
|
+
description: 'Search long-term memory for relevant information. Use this to find past decisions, user preferences, or project guidelines.',
|
|
51
|
+
parameters: {
|
|
52
|
+
type: 'object',
|
|
53
|
+
properties: {
|
|
54
|
+
query: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
description: 'The search query to find relevant memories.',
|
|
57
|
+
},
|
|
58
|
+
limit: {
|
|
59
|
+
type: 'number',
|
|
60
|
+
description: 'Maximum number of results to return. Default is 5.',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
required: ['query'],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
async execute(args) {
|
|
68
|
+
const query = args.query;
|
|
69
|
+
const limit = args.limit || 5;
|
|
70
|
+
const results = await memoryManager.search(query, limit);
|
|
71
|
+
if (results.length === 0) {
|
|
72
|
+
return 'No relevant memories found.';
|
|
73
|
+
}
|
|
74
|
+
return results
|
|
75
|
+
.map((m) => `[${m.type.toUpperCase()}] ${m.content} (Similarity: ${(m.similarity * 100).toFixed(1)}%)`)
|
|
76
|
+
.join('\n');
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
// Forget Tool
|
|
80
|
+
const forgetTool = {
|
|
81
|
+
name: 'forget',
|
|
82
|
+
safeToAutoRun: false, // Deletion is unsafe
|
|
83
|
+
definition: {
|
|
84
|
+
type: 'function',
|
|
85
|
+
function: {
|
|
86
|
+
name: 'forget',
|
|
87
|
+
description: 'Delete a memory by its ID.',
|
|
88
|
+
parameters: {
|
|
89
|
+
type: 'object',
|
|
90
|
+
properties: {
|
|
91
|
+
id: {
|
|
92
|
+
type: 'string',
|
|
93
|
+
description: 'The ID of the memory to delete.',
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
required: ['id'],
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
async execute(args) {
|
|
101
|
+
const id = args.id;
|
|
102
|
+
await memoryManager.forget(id);
|
|
103
|
+
return `Forgot memory with ID: ${id}`;
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
// Learn Mistake Tool
|
|
107
|
+
const learnMistakeTool = {
|
|
108
|
+
name: 'learn_mistake',
|
|
109
|
+
safeToAutoRun: true, // Learning is safe
|
|
110
|
+
definition: {
|
|
111
|
+
type: 'function',
|
|
112
|
+
function: {
|
|
113
|
+
name: 'learn_mistake',
|
|
114
|
+
description: 'Record a mistake and its solution to avoid repeating it in the future.',
|
|
115
|
+
parameters: {
|
|
116
|
+
type: 'object',
|
|
117
|
+
properties: {
|
|
118
|
+
error: {
|
|
119
|
+
type: 'string',
|
|
120
|
+
description: 'The error or mistake that occurred.',
|
|
121
|
+
},
|
|
122
|
+
fix: {
|
|
123
|
+
type: 'string',
|
|
124
|
+
description: 'The solution or lesson learned.',
|
|
125
|
+
},
|
|
126
|
+
context: {
|
|
127
|
+
type: 'string',
|
|
128
|
+
description: 'Optional context (e.g., file path, command).',
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
required: ['error', 'fix'],
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
async execute(args) {
|
|
136
|
+
const error = args.error;
|
|
137
|
+
const fix = args.fix;
|
|
138
|
+
const context = args.context;
|
|
139
|
+
const content = `Mistake: ${error}\nSolution: ${fix}${context ? `\nContext: ${context}` : ''}`;
|
|
140
|
+
const memory = await memoryManager.remember(content, 'mistake', {
|
|
141
|
+
originalError: error,
|
|
142
|
+
fix,
|
|
143
|
+
context,
|
|
144
|
+
});
|
|
145
|
+
return `Recorded mistake and solution (ID: ${memory.id}). I will remember this to avoid it in the future.`;
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
// Register all memory tools
|
|
149
|
+
export function registerMemoryTools() {
|
|
150
|
+
toolRegistry.register(rememberTool);
|
|
151
|
+
toolRegistry.register(recallTool);
|
|
152
|
+
toolRegistry.register(forgetTool);
|
|
153
|
+
toolRegistry.register(learnMistakeTool);
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../../../src/core/tools/definitions/memory.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bluehawks/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "A production-ready multi-agent AI CLI assistant",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"node": ">=20.0.0"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
+
"better-sqlite3": "^12.6.2",
|
|
38
39
|
"chalk": "^5.3.0",
|
|
39
40
|
"commander": "^12.1.0",
|
|
40
41
|
"eventsource-parser": "^3.0.0",
|
|
@@ -50,6 +51,7 @@
|
|
|
50
51
|
"zod": "^3.24.1"
|
|
51
52
|
},
|
|
52
53
|
"devDependencies": {
|
|
54
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
53
55
|
"@types/node": "^22.10.5",
|
|
54
56
|
"@types/react": "^18.3.18",
|
|
55
57
|
"@typescript-eslint/eslint-plugin": "^8.19.1",
|
package/src/config/constants.ts
CHANGED
|
@@ -8,7 +8,7 @@ export const DEFAULT_MODEL = 'Qwen/Qwen3-0.6B';
|
|
|
8
8
|
|
|
9
9
|
// CLI Metadata
|
|
10
10
|
export const CLI_NAME = 'bluehawks';
|
|
11
|
-
export const CLI_VERSION = '1.0.
|
|
11
|
+
export const CLI_VERSION = '1.0.4';
|
|
12
12
|
export const CLI_DESCRIPTION = 'A production-ready multi-agent AI CLI assistant';
|
|
13
13
|
|
|
14
14
|
// Configuration Paths
|
|
@@ -9,6 +9,7 @@ import { Agent, type AgentResponse } from './agent.js';
|
|
|
9
9
|
import { CONTEXT_FILE } from '../../config/constants.js';
|
|
10
10
|
import * as fs from 'node:fs/promises';
|
|
11
11
|
import * as path from 'node:path';
|
|
12
|
+
import { memoryManager } from '../memory/index.js'; // Corrected path
|
|
12
13
|
|
|
13
14
|
export interface SubAgentConfig {
|
|
14
15
|
name: string;
|
|
@@ -178,17 +179,27 @@ Before making any changes, first:
|
|
|
178
179
|
onToolEnd?: (name: string, result: string) => void;
|
|
179
180
|
}
|
|
180
181
|
): Promise<AgentResponse> {
|
|
182
|
+
// Retrieve relevant memories
|
|
183
|
+
const memories = await memoryManager.search(userMessage, 5);
|
|
184
|
+
let systemPrompt = this.buildSystemPrompt();
|
|
185
|
+
|
|
186
|
+
if (memories.length > 0) {
|
|
187
|
+
const memoryContext = memories
|
|
188
|
+
.map(m => `- [${m.type.toUpperCase()}] ${m.content}`)
|
|
189
|
+
.join('\n');
|
|
190
|
+
systemPrompt += `\n\n## Long-Term Memory (Relevant Context)\n${memoryContext}\n\nUse this information to guide your decisions and avoid past mistakes.`;
|
|
191
|
+
}
|
|
192
|
+
|
|
181
193
|
const mainAgent = new Agent(
|
|
182
194
|
{
|
|
183
195
|
name: 'main',
|
|
184
|
-
systemPrompt
|
|
196
|
+
systemPrompt,
|
|
185
197
|
maxIterations: this.maxTurns,
|
|
186
198
|
},
|
|
187
199
|
this.apiClient,
|
|
188
200
|
this.toolExecutor
|
|
189
201
|
);
|
|
190
202
|
|
|
191
|
-
|
|
192
203
|
return mainAgent.run(
|
|
193
204
|
userMessage,
|
|
194
205
|
callbacks?.onChunk,
|
package/src/core/api/client.ts
CHANGED
|
@@ -22,6 +22,8 @@ import {
|
|
|
22
22
|
type ToolDefinition,
|
|
23
23
|
type ToolResult,
|
|
24
24
|
type StreamDelta,
|
|
25
|
+
type EmbeddingRequest,
|
|
26
|
+
type EmbeddingResponse,
|
|
25
27
|
APIError,
|
|
26
28
|
} from './types.js';
|
|
27
29
|
|
|
@@ -42,6 +44,21 @@ export class APIClient {
|
|
|
42
44
|
this.timeout = options.timeout || DEFAULT_TIMEOUT_MS;
|
|
43
45
|
}
|
|
44
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Create embeddings for the given input
|
|
49
|
+
*/
|
|
50
|
+
async createEmbeddings(
|
|
51
|
+
input: string | string[],
|
|
52
|
+
model?: string
|
|
53
|
+
): Promise<EmbeddingResponse> {
|
|
54
|
+
const body: EmbeddingRequest = {
|
|
55
|
+
model: model || 'text-embedding-3-small',
|
|
56
|
+
input,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return this.makeRequest<EmbeddingResponse>('/embeddings', body);
|
|
60
|
+
}
|
|
61
|
+
|
|
45
62
|
/**
|
|
46
63
|
* Create a chat completion (non-streaming)
|
|
47
64
|
*/
|
package/src/core/api/types.ts
CHANGED
|
@@ -136,6 +136,29 @@ export interface APIClientOptions {
|
|
|
136
136
|
export type StreamCallback = (chunk: string) => void;
|
|
137
137
|
export type ToolCallCallback = (toolCalls: ToolCall[]) => void;
|
|
138
138
|
|
|
139
|
+
// Embedding Types
|
|
140
|
+
export interface EmbeddingRequest {
|
|
141
|
+
model: string;
|
|
142
|
+
input: string | string[];
|
|
143
|
+
user?: string;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface EmbeddingResponse {
|
|
147
|
+
object: 'list';
|
|
148
|
+
data: EmbeddingObject[];
|
|
149
|
+
model: string;
|
|
150
|
+
usage: {
|
|
151
|
+
prompt_tokens: number;
|
|
152
|
+
total_tokens: number;
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface EmbeddingObject {
|
|
157
|
+
object: 'embedding';
|
|
158
|
+
index: number;
|
|
159
|
+
embedding: number[];
|
|
160
|
+
}
|
|
161
|
+
|
|
139
162
|
// API Error
|
|
140
163
|
export class APIError extends Error {
|
|
141
164
|
constructor(
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { apiClient } from '../api/client.js';
|
|
2
|
+
import { memoryStorage } from './storage.js';
|
|
3
|
+
import type { Memory, MemoryType, SearchResult } from './types.js';
|
|
4
|
+
import * as crypto from 'crypto';
|
|
5
|
+
|
|
6
|
+
export class MemoryManager {
|
|
7
|
+
private static instance: MemoryManager;
|
|
8
|
+
|
|
9
|
+
private constructor() { }
|
|
10
|
+
|
|
11
|
+
static getInstance(): MemoryManager {
|
|
12
|
+
if (!MemoryManager.instance) {
|
|
13
|
+
MemoryManager.instance = new MemoryManager();
|
|
14
|
+
}
|
|
15
|
+
return MemoryManager.instance;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Store a new memory
|
|
20
|
+
*/
|
|
21
|
+
async remember(content: string, type: MemoryType = 'knowledge', metadata: Record<string, any> = {}): Promise<Memory> {
|
|
22
|
+
// Generate embedding
|
|
23
|
+
let embedding: number[] = [];
|
|
24
|
+
try {
|
|
25
|
+
const response = await apiClient.createEmbeddings(content);
|
|
26
|
+
if (response.data && response.data.length > 0) {
|
|
27
|
+
embedding = response.data[0].embedding;
|
|
28
|
+
}
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error('Failed to generate embedding for memory:', error);
|
|
31
|
+
// We still save the memory without embedding, it just won't be searchable by vector
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const memory: Memory = {
|
|
35
|
+
id: crypto.randomUUID(),
|
|
36
|
+
content,
|
|
37
|
+
type,
|
|
38
|
+
metadata,
|
|
39
|
+
embedding,
|
|
40
|
+
created_at: Date.now(),
|
|
41
|
+
updated_at: Date.now(),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
await memoryStorage.save(memory);
|
|
45
|
+
return memory;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Search memories by semantic similarity
|
|
50
|
+
*/
|
|
51
|
+
async search(query: string, limit: number = 5, minSimilarity: number = 0.7): Promise<SearchResult[]> {
|
|
52
|
+
// Generate query embedding
|
|
53
|
+
let queryEmbedding: number[] = [];
|
|
54
|
+
try {
|
|
55
|
+
const response = await apiClient.createEmbeddings(query);
|
|
56
|
+
if (response.data && response.data.length > 0) {
|
|
57
|
+
queryEmbedding = response.data[0].embedding;
|
|
58
|
+
}
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error('Failed to generate embedding for search query:', error);
|
|
61
|
+
// Fallback to keyword search? For now just return empty if vector search fails
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const allMemories = await memoryStorage.getAll();
|
|
66
|
+
|
|
67
|
+
// Calculate similarity for each memory
|
|
68
|
+
const results: SearchResult[] = allMemories
|
|
69
|
+
.filter(m => m.embedding && m.embedding.length > 0)
|
|
70
|
+
.map(memory => {
|
|
71
|
+
const similarity = this.cosineSimilarity(queryEmbedding, memory.embedding!);
|
|
72
|
+
return { ...memory, similarity };
|
|
73
|
+
})
|
|
74
|
+
.filter(r => r.similarity >= minSimilarity)
|
|
75
|
+
.sort((a, b) => b.similarity - a.similarity) // Descending
|
|
76
|
+
.slice(0, limit);
|
|
77
|
+
|
|
78
|
+
return results;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Retrieve a memory by ID
|
|
83
|
+
*/
|
|
84
|
+
async get(id: string): Promise<Memory | null> {
|
|
85
|
+
return memoryStorage.get(id);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Delete a memory
|
|
90
|
+
*/
|
|
91
|
+
async forget(id: string): Promise<void> {
|
|
92
|
+
await memoryStorage.delete(id);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Clear all memories (useful for testing or reset)
|
|
97
|
+
*/
|
|
98
|
+
async clear(): Promise<void> {
|
|
99
|
+
await memoryStorage.clear();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private cosineSimilarity(vecA: number[], vecB: number[]): number {
|
|
103
|
+
if (vecA.length !== vecB.length) return 0;
|
|
104
|
+
|
|
105
|
+
let dotProduct = 0;
|
|
106
|
+
let normA = 0;
|
|
107
|
+
let normB = 0;
|
|
108
|
+
|
|
109
|
+
for (let i = 0; i < vecA.length; i++) {
|
|
110
|
+
dotProduct += vecA[i] * vecB[i];
|
|
111
|
+
normA += vecA[i] * vecA[i];
|
|
112
|
+
normB += vecB[i] * vecB[i];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (normA === 0 || normB === 0) return 0;
|
|
116
|
+
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const memoryManager = MemoryManager.getInstance();
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import * as fs from 'fs';
|
|
5
|
+
import { CONFIG_DIR_NAME } from '../../config/constants.js';
|
|
6
|
+
import type { Memory, MemoryType } from './types.js';
|
|
7
|
+
|
|
8
|
+
export class MemoryStorage {
|
|
9
|
+
private db: Database.Database;
|
|
10
|
+
private dbPath: string;
|
|
11
|
+
|
|
12
|
+
constructor() {
|
|
13
|
+
const homeDir = os.homedir();
|
|
14
|
+
const configDir = path.join(homeDir, CONFIG_DIR_NAME);
|
|
15
|
+
|
|
16
|
+
// Ensure directory exists
|
|
17
|
+
if (!fs.existsSync(configDir)) {
|
|
18
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
this.dbPath = path.join(configDir, 'memory.db');
|
|
22
|
+
this.db = new Database(this.dbPath);
|
|
23
|
+
this.initialize();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private initialize(): void {
|
|
27
|
+
this.db.exec(`
|
|
28
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
29
|
+
id TEXT PRIMARY KEY,
|
|
30
|
+
content TEXT NOT NULL,
|
|
31
|
+
type TEXT NOT NULL,
|
|
32
|
+
metadata TEXT,
|
|
33
|
+
embedding TEXT,
|
|
34
|
+
created_at INTEGER NOT NULL,
|
|
35
|
+
updated_at INTEGER NOT NULL
|
|
36
|
+
);
|
|
37
|
+
CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(type);
|
|
38
|
+
`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async save(memory: Memory): Promise<void> {
|
|
42
|
+
const stmt = this.db.prepare(`
|
|
43
|
+
INSERT OR REPLACE INTO memories (id, content, type, metadata, embedding, created_at, updated_at)
|
|
44
|
+
VALUES (@id, @content, @type, @metadata, @embedding, @created_at, @updated_at)
|
|
45
|
+
`);
|
|
46
|
+
|
|
47
|
+
stmt.run({
|
|
48
|
+
...memory,
|
|
49
|
+
metadata: JSON.stringify(memory.metadata || {}),
|
|
50
|
+
embedding: JSON.stringify(memory.embedding || []),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async get(id: string): Promise<Memory | null> {
|
|
55
|
+
const stmt = this.db.prepare('SELECT * FROM memories WHERE id = ?');
|
|
56
|
+
const row = stmt.get(id) as any;
|
|
57
|
+
|
|
58
|
+
if (!row) return null;
|
|
59
|
+
return this.mapRowToMemory(row);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async getAll(): Promise<Memory[]> {
|
|
63
|
+
const stmt = this.db.prepare('SELECT * FROM memories');
|
|
64
|
+
const rows = stmt.all() as any[];
|
|
65
|
+
return rows.map(r => this.mapRowToMemory(r));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async delete(id: string): Promise<void> {
|
|
69
|
+
const stmt = this.db.prepare('DELETE FROM memories WHERE id = ?');
|
|
70
|
+
stmt.run(id);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async clear(): Promise<void> {
|
|
74
|
+
this.db.exec('DELETE FROM memories');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private mapRowToMemory(row: any): Memory {
|
|
78
|
+
return {
|
|
79
|
+
id: row.id,
|
|
80
|
+
content: row.content,
|
|
81
|
+
type: row.type as MemoryType,
|
|
82
|
+
metadata: JSON.parse(row.metadata),
|
|
83
|
+
embedding: JSON.parse(row.embedding),
|
|
84
|
+
created_at: row.created_at,
|
|
85
|
+
updated_at: row.updated_at,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const memoryStorage = new MemoryStorage();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface Memory {
|
|
2
|
+
id: string;
|
|
3
|
+
content: string;
|
|
4
|
+
type: MemoryType;
|
|
5
|
+
metadata?: Record<string, any>;
|
|
6
|
+
embedding?: number[];
|
|
7
|
+
created_at: number;
|
|
8
|
+
updated_at: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type MemoryType = 'preference' | 'mistake' | 'knowledge' | 'task_context';
|
|
12
|
+
|
|
13
|
+
export interface MemoryMetadata {
|
|
14
|
+
source?: string;
|
|
15
|
+
confidence?: number;
|
|
16
|
+
tags?: string[];
|
|
17
|
+
[key: string]: any;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface SearchResult extends Memory {
|
|
21
|
+
similarity: number;
|
|
22
|
+
}
|
|
@@ -8,6 +8,7 @@ import { registerShellTools } from './shell.js';
|
|
|
8
8
|
import { registerSearchTools } from './search.js';
|
|
9
9
|
import { registerGitTools } from './git.js';
|
|
10
10
|
import { registerWebTools } from './web.js';
|
|
11
|
+
import { registerMemoryTools } from './memory.js';
|
|
11
12
|
|
|
12
13
|
export function registerAllTools(): void {
|
|
13
14
|
registerFileTools();
|
|
@@ -15,6 +16,7 @@ export function registerAllTools(): void {
|
|
|
15
16
|
registerSearchTools();
|
|
16
17
|
registerGitTools();
|
|
17
18
|
registerWebTools();
|
|
19
|
+
registerMemoryTools();
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export * from './file.js';
|
|
@@ -22,3 +24,4 @@ export * from './shell.js';
|
|
|
22
24
|
export * from './search.js';
|
|
23
25
|
export * from './git.js';
|
|
24
26
|
export * from './web.js';
|
|
27
|
+
export * from './memory.js';
|