@sesamespace/hivemind 0.10.0 → 0.11.1

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 (52) hide show
  1. package/.pnpmrc.json +1 -0
  2. package/AUTO-DEBUG-DESIGN.md +267 -0
  3. package/AUTOMATIC-MEMORY-MANAGEMENT.md +109 -0
  4. package/DASHBOARD-PLAN.md +206 -0
  5. package/MEMORY-ENHANCEMENT-PLAN.md +211 -0
  6. package/TOOL-USE-DESIGN.md +173 -0
  7. package/dist/{chunk-FBQBBAPZ.js → chunk-4C6B2AMB.js} +2 -2
  8. package/dist/{chunk-FK6WYXRM.js → chunk-4YXOQGQC.js} +2 -2
  9. package/dist/{chunk-IXBIAX76.js → chunk-K6KL2VD6.js} +2 -2
  10. package/dist/{chunk-IJRAVHQC.js → chunk-LWJCKTQP.js} +51 -11
  11. package/dist/chunk-LWJCKTQP.js.map +1 -0
  12. package/dist/{chunk-BHCDOHSK.js → chunk-LYL5GG2F.js} +3 -3
  13. package/dist/{chunk-M3A2WRXM.js → chunk-OB6OXLPC.js} +430 -2
  14. package/dist/chunk-OB6OXLPC.js.map +1 -0
  15. package/dist/{chunk-DPLCEMEC.js → chunk-ZA4NWNS6.js} +2 -2
  16. package/dist/commands/fleet.js +3 -3
  17. package/dist/commands/init.js +3 -3
  18. package/dist/commands/service.js +1 -1
  19. package/dist/commands/start.js +3 -3
  20. package/dist/commands/watchdog.js +3 -3
  21. package/dist/dashboard.html +100 -60
  22. package/dist/index.js +2 -2
  23. package/dist/main.js +7 -7
  24. package/dist/start.js +1 -1
  25. package/docs/TOOL-PARITY-PLAN.md +191 -0
  26. package/package.json +23 -24
  27. package/src/memory/dashboard-integration.ts +295 -0
  28. package/src/memory/index.ts +187 -0
  29. package/src/memory/performance-test.ts +208 -0
  30. package/src/memory/processors/agent-sync.ts +312 -0
  31. package/src/memory/processors/command-learner.ts +298 -0
  32. package/src/memory/processors/memory-api-client.ts +105 -0
  33. package/src/memory/processors/message-flow-integration.ts +168 -0
  34. package/src/memory/processors/research-digester.ts +204 -0
  35. package/test-caitlin-access.md +11 -0
  36. package/dist/chunk-IJRAVHQC.js.map +0 -1
  37. package/dist/chunk-M3A2WRXM.js.map +0 -1
  38. package/install.sh +0 -162
  39. package/packages/memory/Cargo.lock +0 -6480
  40. package/packages/memory/Cargo.toml +0 -21
  41. package/packages/memory/src/src/context.rs +0 -179
  42. package/packages/memory/src/src/embeddings.rs +0 -51
  43. package/packages/memory/src/src/main.rs +0 -887
  44. package/packages/memory/src/src/promotion.rs +0 -808
  45. package/packages/memory/src/src/scoring.rs +0 -142
  46. package/packages/memory/src/src/store.rs +0 -460
  47. package/packages/memory/src/src/tasks.rs +0 -321
  48. /package/dist/{chunk-FBQBBAPZ.js.map → chunk-4C6B2AMB.js.map} +0 -0
  49. /package/dist/{chunk-FK6WYXRM.js.map → chunk-4YXOQGQC.js.map} +0 -0
  50. /package/dist/{chunk-IXBIAX76.js.map → chunk-K6KL2VD6.js.map} +0 -0
  51. /package/dist/{chunk-BHCDOHSK.js.map → chunk-LYL5GG2F.js.map} +0 -0
  52. /package/dist/{chunk-DPLCEMEC.js.map → chunk-ZA4NWNS6.js.map} +0 -0
@@ -0,0 +1,312 @@
1
+ /**
2
+ * Agent Sync - Shares knowledge between multiple Hivemind agents
3
+ */
4
+
5
+ import { BackgroundProcessor } from './background-processor';
6
+ import { EventEmitter } from 'events';
7
+ import axios from 'axios';
8
+
9
+ export interface AgentKnowledge {
10
+ agentId: string;
11
+ agentName: string;
12
+ context: string;
13
+ tasks: SharedTask[];
14
+ insights: string[];
15
+ capabilities: string[];
16
+ lastSync: Date;
17
+ }
18
+
19
+ export interface SharedTask {
20
+ id: string;
21
+ description: string;
22
+ status: 'active' | 'completed' | 'blocked';
23
+ assignedTo: string;
24
+ dependencies: string[];
25
+ progress: number;
26
+ notes: string[];
27
+ }
28
+
29
+ export interface SyncMessage {
30
+ type: 'task-update' | 'insight' | 'handoff' | 'capability';
31
+ fromAgent: string;
32
+ toAgent?: string; // Optional for broadcasts
33
+ payload: any;
34
+ timestamp: Date;
35
+ }
36
+
37
+ export class AgentSync extends BackgroundProcessor {
38
+ private agentId: string;
39
+ private agentName: string;
40
+ private otherAgents: Map<string, AgentKnowledge> = new Map();
41
+ private syncQueue: SyncMessage[] = [];
42
+ private sharedTasks: Map<string, SharedTask> = new Map();
43
+ private syncEndpoints: Map<string, string> = new Map(); // agentId -> endpoint
44
+
45
+ constructor(agentId: string, agentName: string) {
46
+ super('agent-sync', 30000); // Sync every 30 seconds
47
+ this.agentId = agentId;
48
+ this.agentName = agentName;
49
+ }
50
+
51
+ async process(): Promise<void> {
52
+ // Process outgoing sync messages
53
+ await this.processSyncQueue();
54
+
55
+ // Pull updates from other agents
56
+ await this.pullUpdates();
57
+
58
+ // Check for stale tasks that might need handoff
59
+ await this.checkHandoffs();
60
+
61
+ this.emit('sync-complete', {
62
+ agentsKnown: this.otherAgents.size,
63
+ sharedTasks: this.sharedTasks.size,
64
+ queuedMessages: this.syncQueue.length
65
+ });
66
+ }
67
+
68
+ /**
69
+ * Register another agent for syncing
70
+ */
71
+ async registerAgent(agentId: string, agentName: string, endpoint: string): Promise<void> {
72
+ this.syncEndpoints.set(agentId, endpoint);
73
+ this.otherAgents.set(agentId, {
74
+ agentId,
75
+ agentName,
76
+ context: 'unknown',
77
+ tasks: [],
78
+ insights: [],
79
+ capabilities: [],
80
+ lastSync: new Date()
81
+ });
82
+ }
83
+
84
+ /**
85
+ * Share a task with other agents
86
+ */
87
+ async shareTask(task: Omit<SharedTask, 'id'>): Promise<string> {
88
+ const taskId = `${this.agentId}-${Date.now()}`;
89
+ const sharedTask: SharedTask = {
90
+ id: taskId,
91
+ ...task
92
+ };
93
+
94
+ this.sharedTasks.set(taskId, sharedTask);
95
+
96
+ // Broadcast task to other agents
97
+ this.queueSync({
98
+ type: 'task-update',
99
+ fromAgent: this.agentId,
100
+ payload: sharedTask,
101
+ timestamp: new Date()
102
+ });
103
+
104
+ return taskId;
105
+ }
106
+
107
+ /**
108
+ * Share an insight learned during work
109
+ */
110
+ async shareInsight(insight: string, context?: string): Promise<void> {
111
+ this.queueSync({
112
+ type: 'insight',
113
+ fromAgent: this.agentId,
114
+ payload: { insight, context },
115
+ timestamp: new Date()
116
+ });
117
+ }
118
+
119
+ /**
120
+ * Announce a new capability
121
+ */
122
+ async announceCapability(capability: string): Promise<void> {
123
+ this.queueSync({
124
+ type: 'capability',
125
+ fromAgent: this.agentId,
126
+ payload: { capability },
127
+ timestamp: new Date()
128
+ });
129
+ }
130
+
131
+ /**
132
+ * Request handoff of a task to another agent
133
+ */
134
+ async requestHandoff(taskId: string, toAgent: string, reason: string): Promise<void> {
135
+ const task = this.sharedTasks.get(taskId);
136
+ if (!task) return;
137
+
138
+ this.queueSync({
139
+ type: 'handoff',
140
+ fromAgent: this.agentId,
141
+ toAgent,
142
+ payload: { taskId, task, reason },
143
+ timestamp: new Date()
144
+ });
145
+ }
146
+
147
+ private queueSync(message: SyncMessage): void {
148
+ this.syncQueue.push(message);
149
+ }
150
+
151
+ private async processSyncQueue(): Promise<void> {
152
+ while (this.syncQueue.length > 0) {
153
+ const message = this.syncQueue.shift();
154
+ if (!message) continue;
155
+
156
+ if (message.toAgent) {
157
+ // Direct message to specific agent
158
+ await this.sendToAgent(message.toAgent, message);
159
+ } else {
160
+ // Broadcast to all agents
161
+ for (const agentId of this.syncEndpoints.keys()) {
162
+ if (agentId !== this.agentId) {
163
+ await this.sendToAgent(agentId, message);
164
+ }
165
+ }
166
+ }
167
+ }
168
+ }
169
+
170
+ private async sendToAgent(agentId: string, message: SyncMessage): Promise<void> {
171
+ const endpoint = this.syncEndpoints.get(agentId);
172
+ if (!endpoint) return;
173
+
174
+ try {
175
+ await axios.post(`${endpoint}/sync`, message, {
176
+ timeout: 5000,
177
+ headers: {
178
+ 'X-Agent-ID': this.agentId,
179
+ 'X-Agent-Name': this.agentName
180
+ }
181
+ });
182
+ } catch (error) {
183
+ this.emit('sync-error', { agentId, error });
184
+ }
185
+ }
186
+
187
+ private async pullUpdates(): Promise<void> {
188
+ for (const [agentId, endpoint] of this.syncEndpoints) {
189
+ if (agentId === this.agentId) continue;
190
+
191
+ try {
192
+ const response = await axios.get(`${endpoint}/sync/updates`, {
193
+ params: {
194
+ since: this.otherAgents.get(agentId)?.lastSync.toISOString()
195
+ },
196
+ timeout: 5000,
197
+ headers: {
198
+ 'X-Agent-ID': this.agentId
199
+ }
200
+ });
201
+
202
+ if (response.data.updates) {
203
+ await this.processUpdates(agentId, response.data.updates);
204
+ }
205
+ } catch (error) {
206
+ this.emit('pull-error', { agentId, error });
207
+ }
208
+ }
209
+ }
210
+
211
+ private async processUpdates(fromAgent: string, updates: SyncMessage[]): Promise<void> {
212
+ const agentKnowledge = this.otherAgents.get(fromAgent);
213
+ if (!agentKnowledge) return;
214
+
215
+ for (const update of updates) {
216
+ switch (update.type) {
217
+ case 'task-update':
218
+ const task = update.payload as SharedTask;
219
+ this.sharedTasks.set(task.id, task);
220
+ agentKnowledge.tasks = agentKnowledge.tasks.filter(t => t.id !== task.id);
221
+ agentKnowledge.tasks.push(task);
222
+ break;
223
+
224
+ case 'insight':
225
+ const { insight } = update.payload;
226
+ if (!agentKnowledge.insights.includes(insight)) {
227
+ agentKnowledge.insights.push(insight);
228
+ }
229
+ break;
230
+
231
+ case 'capability':
232
+ const { capability } = update.payload;
233
+ if (!agentKnowledge.capabilities.includes(capability)) {
234
+ agentKnowledge.capabilities.push(capability);
235
+ }
236
+ break;
237
+
238
+ case 'handoff':
239
+ if (update.toAgent === this.agentId) {
240
+ await this.handleHandoff(update.payload);
241
+ }
242
+ break;
243
+ }
244
+ }
245
+
246
+ agentKnowledge.lastSync = new Date();
247
+ }
248
+
249
+ private async handleHandoff(payload: any): Promise<void> {
250
+ const { taskId, task, reason } = payload;
251
+
252
+ // Accept the handoff
253
+ task.assignedTo = this.agentName;
254
+ this.sharedTasks.set(taskId, task);
255
+
256
+ this.emit('handoff-received', { taskId, task, reason });
257
+
258
+ // Acknowledge the handoff
259
+ this.queueSync({
260
+ type: 'task-update',
261
+ fromAgent: this.agentId,
262
+ payload: task,
263
+ timestamp: new Date()
264
+ });
265
+ }
266
+
267
+ private async checkHandoffs(): Promise<void> {
268
+ // Check for tasks that might need handoff
269
+ for (const task of this.sharedTasks.values()) {
270
+ if (task.assignedTo === this.agentName && task.status === 'blocked') {
271
+ // Check if another agent might be better suited
272
+ const betterAgent = this.findBetterAgent(task);
273
+ if (betterAgent) {
274
+ await this.requestHandoff(
275
+ task.id,
276
+ betterAgent,
277
+ 'Task blocked, another agent may have required capabilities'
278
+ );
279
+ }
280
+ }
281
+ }
282
+ }
283
+
284
+ private findBetterAgent(task: SharedTask): string | null {
285
+ // Simple heuristic: find agent with relevant capabilities
286
+ for (const [agentId, knowledge] of this.otherAgents) {
287
+ // Check if agent has capabilities mentioned in task notes
288
+ for (const note of task.notes) {
289
+ for (const capability of knowledge.capabilities) {
290
+ if (note.toLowerCase().includes(capability.toLowerCase())) {
291
+ return agentId;
292
+ }
293
+ }
294
+ }
295
+ }
296
+ return null;
297
+ }
298
+
299
+ /**
300
+ * Get knowledge about other agents
301
+ */
302
+ async getAgentKnowledge(): Promise<AgentKnowledge[]> {
303
+ return Array.from(this.otherAgents.values());
304
+ }
305
+
306
+ /**
307
+ * Get all shared tasks
308
+ */
309
+ async getSharedTasks(): Promise<SharedTask[]> {
310
+ return Array.from(this.sharedTasks.values());
311
+ }
312
+ }
@@ -0,0 +1,298 @@
1
+ /**
2
+ * Command Learner - Tracks successful command patterns and builds a knowledge base
3
+ */
4
+
5
+ import { BackgroundProcessor } from './background-processor';
6
+ import { EventEmitter } from 'events';
7
+
8
+ export interface CommandPattern {
9
+ pattern: string;
10
+ description: string;
11
+ examples: CommandExample[];
12
+ successRate: number;
13
+ category: string;
14
+ relatedCommands: string[];
15
+ lastUsed: Date;
16
+ usageCount: number;
17
+ }
18
+
19
+ export interface CommandExample {
20
+ command: string;
21
+ context: string;
22
+ output?: string;
23
+ success: boolean;
24
+ timestamp: Date;
25
+ }
26
+
27
+ export class CommandLearner extends BackgroundProcessor {
28
+ private patterns: Map<string, CommandPattern> = new Map();
29
+ private recentCommands: CommandExample[] = [];
30
+ private categoryIndex: Map<string, Set<string>> = new Map();
31
+
32
+ constructor() {
33
+ super('command-learner', 60000); // Process every minute
34
+ }
35
+
36
+ async process(): Promise<void> {
37
+ // Analyze recent commands to extract patterns
38
+ if (this.recentCommands.length >= 5) {
39
+ await this.extractPatterns();
40
+ }
41
+
42
+ // Update success rates
43
+ this.updateSuccessRates();
44
+
45
+ // Emit current state
46
+ this.emit('patterns-updated', {
47
+ totalPatterns: this.patterns.size,
48
+ categories: Array.from(this.categoryIndex.keys()),
49
+ recentCommands: this.recentCommands.length
50
+ });
51
+ }
52
+
53
+ /**
54
+ * Track a command execution
55
+ */
56
+ async trackCommand(command: string, context: string, output?: string, success: boolean = true): Promise<void> {
57
+ const example: CommandExample = {
58
+ command,
59
+ context,
60
+ output: output?.substring(0, 1000), // Limit output size
61
+ success,
62
+ timestamp: new Date()
63
+ };
64
+
65
+ this.recentCommands.push(example);
66
+
67
+ // Keep only last 100 commands
68
+ if (this.recentCommands.length > 100) {
69
+ this.recentCommands.shift();
70
+ }
71
+
72
+ // Check if this matches an existing pattern
73
+ await this.matchToPattern(example);
74
+ }
75
+
76
+ private async extractPatterns(): Promise<void> {
77
+ // Group commands by similarity
78
+ const groups = this.groupSimilarCommands(this.recentCommands.slice(-20));
79
+
80
+ for (const group of groups) {
81
+ if (group.length >= 2) {
82
+ const pattern = this.derivePattern(group);
83
+ if (pattern) {
84
+ this.patterns.set(pattern.pattern, pattern);
85
+
86
+ // Index by category
87
+ if (!this.categoryIndex.has(pattern.category)) {
88
+ this.categoryIndex.set(pattern.category, new Set());
89
+ }
90
+ this.categoryIndex.get(pattern.category)!.add(pattern.pattern);
91
+ }
92
+ }
93
+ }
94
+ }
95
+
96
+ private groupSimilarCommands(commands: CommandExample[]): CommandExample[][] {
97
+ const groups: CommandExample[][] = [];
98
+ const used = new Set<number>();
99
+
100
+ for (let i = 0; i < commands.length; i++) {
101
+ if (used.has(i)) continue;
102
+
103
+ const group = [commands[i]];
104
+ used.add(i);
105
+
106
+ for (let j = i + 1; j < commands.length; j++) {
107
+ if (used.has(j)) continue;
108
+
109
+ if (this.areSimilar(commands[i].command, commands[j].command)) {
110
+ group.push(commands[j]);
111
+ used.add(j);
112
+ }
113
+ }
114
+
115
+ if (group.length > 1) {
116
+ groups.push(group);
117
+ }
118
+ }
119
+
120
+ return groups;
121
+ }
122
+
123
+ private areSimilar(cmd1: string, cmd2: string): boolean {
124
+ // Tokenize commands
125
+ const tokens1 = cmd1.split(/\s+/);
126
+ const tokens2 = cmd2.split(/\s+/);
127
+
128
+ // Check if base command is the same
129
+ if (tokens1[0] !== tokens2[0]) return false;
130
+
131
+ // Check structural similarity
132
+ if (Math.abs(tokens1.length - tokens2.length) > 2) return false;
133
+
134
+ // Count matching tokens (excluding file paths and specific values)
135
+ let matches = 0;
136
+ for (let i = 0; i < Math.min(tokens1.length, tokens2.length); i++) {
137
+ if (tokens1[i] === tokens2[i] ||
138
+ (this.isVariable(tokens1[i]) && this.isVariable(tokens2[i]))) {
139
+ matches++;
140
+ }
141
+ }
142
+
143
+ return matches / Math.max(tokens1.length, tokens2.length) > 0.6;
144
+ }
145
+
146
+ private isVariable(token: string): boolean {
147
+ // Detect tokens that are likely variables (paths, URLs, IDs, etc.)
148
+ return token.includes('/') ||
149
+ token.includes('.') ||
150
+ token.match(/^[a-f0-9]{8,}$/i) !== null ||
151
+ token.match(/^\d+$/) !== null;
152
+ }
153
+
154
+ private derivePattern(examples: CommandExample[]): CommandPattern | null {
155
+ if (examples.length === 0) return null;
156
+
157
+ const baseCmd = examples[0].command.split(/\s+/)[0];
158
+ const category = this.categorizeCommand(baseCmd);
159
+
160
+ // Find common structure
161
+ const tokenizedExamples = examples.map(e => e.command.split(/\s+/));
162
+ const patternTokens: string[] = [];
163
+
164
+ for (let i = 0; i < tokenizedExamples[0].length; i++) {
165
+ const tokensAtPosition = tokenizedExamples.map(t => t[i]).filter(Boolean);
166
+
167
+ if (tokensAtPosition.every(t => t === tokensAtPosition[0])) {
168
+ patternTokens.push(tokensAtPosition[0]);
169
+ } else if (tokensAtPosition.every(t => this.isVariable(t))) {
170
+ patternTokens.push('<variable>');
171
+ } else {
172
+ patternTokens.push('<option>');
173
+ }
174
+ }
175
+
176
+ const pattern = patternTokens.join(' ');
177
+ const successCount = examples.filter(e => e.success).length;
178
+
179
+ return {
180
+ pattern,
181
+ description: this.generateDescription(pattern, examples),
182
+ examples: examples.slice(0, 5), // Keep top 5 examples
183
+ successRate: successCount / examples.length,
184
+ category,
185
+ relatedCommands: this.findRelatedCommands(baseCmd),
186
+ lastUsed: new Date(Math.max(...examples.map(e => e.timestamp.getTime()))),
187
+ usageCount: examples.length
188
+ };
189
+ }
190
+
191
+ private categorizeCommand(baseCmd: string): string {
192
+ const categories: Record<string, string[]> = {
193
+ 'git': ['git', 'gh'],
194
+ 'file': ['ls', 'cat', 'cp', 'mv', 'rm', 'find', 'grep'],
195
+ 'process': ['ps', 'kill', 'top', 'htop'],
196
+ 'network': ['curl', 'wget', 'ping', 'netstat'],
197
+ 'package': ['npm', 'yarn', 'pip', 'brew'],
198
+ 'docker': ['docker', 'docker-compose'],
199
+ 'system': ['df', 'du', 'free', 'uname']
200
+ };
201
+
202
+ for (const [category, commands] of Object.entries(categories)) {
203
+ if (commands.includes(baseCmd)) {
204
+ return category;
205
+ }
206
+ }
207
+
208
+ return 'other';
209
+ }
210
+
211
+ private generateDescription(pattern: string, examples: CommandExample[]): string {
212
+ const baseCmd = pattern.split(' ')[0];
213
+ const contexts = [...new Set(examples.map(e => e.context))];
214
+
215
+ return `${baseCmd} command used for ${contexts.join(', ')}`;
216
+ }
217
+
218
+ private findRelatedCommands(baseCmd: string): string[] {
219
+ const related: Record<string, string[]> = {
220
+ 'git': ['git status', 'git log', 'git diff'],
221
+ 'npm': ['npm install', 'npm run', 'npm test'],
222
+ 'docker': ['docker ps', 'docker logs', 'docker exec'],
223
+ 'curl': ['wget', 'httpie', 'fetch']
224
+ };
225
+
226
+ return related[baseCmd] || [];
227
+ }
228
+
229
+ private async matchToPattern(example: CommandExample): Promise<void> {
230
+ for (const [patternStr, pattern] of this.patterns) {
231
+ if (this.matchesPattern(example.command, patternStr)) {
232
+ pattern.examples.push(example);
233
+ pattern.lastUsed = example.timestamp;
234
+ pattern.usageCount++;
235
+
236
+ // Keep only recent examples
237
+ if (pattern.examples.length > 10) {
238
+ pattern.examples = pattern.examples.slice(-10);
239
+ }
240
+
241
+ break;
242
+ }
243
+ }
244
+ }
245
+
246
+ private matchesPattern(command: string, pattern: string): boolean {
247
+ const cmdTokens = command.split(/\s+/);
248
+ const patternTokens = pattern.split(/\s+/);
249
+
250
+ if (cmdTokens.length !== patternTokens.length) return false;
251
+
252
+ for (let i = 0; i < cmdTokens.length; i++) {
253
+ if (patternTokens[i] === '<variable>' || patternTokens[i] === '<option>') {
254
+ continue;
255
+ }
256
+ if (cmdTokens[i] !== patternTokens[i]) {
257
+ return false;
258
+ }
259
+ }
260
+
261
+ return true;
262
+ }
263
+
264
+ private updateSuccessRates(): void {
265
+ for (const pattern of this.patterns.values()) {
266
+ const recentExamples = pattern.examples.slice(-10);
267
+ const successCount = recentExamples.filter(e => e.success).length;
268
+ pattern.successRate = successCount / recentExamples.length;
269
+ }
270
+ }
271
+
272
+ /**
273
+ * Get command suggestions for a given context
274
+ */
275
+ async getSuggestions(context: string, category?: string): Promise<CommandPattern[]> {
276
+ let patterns = Array.from(this.patterns.values());
277
+
278
+ // Filter by category if specified
279
+ if (category && this.categoryIndex.has(category)) {
280
+ const categoryPatterns = this.categoryIndex.get(category)!;
281
+ patterns = patterns.filter(p => categoryPatterns.has(p.pattern));
282
+ }
283
+
284
+ // Filter by context relevance
285
+ patterns = patterns.filter(p =>
286
+ p.examples.some(e => e.context.toLowerCase().includes(context.toLowerCase()))
287
+ );
288
+
289
+ // Sort by success rate and recency
290
+ return patterns
291
+ .sort((a, b) => {
292
+ const scoreA = a.successRate * 0.7 + (Date.now() - a.lastUsed.getTime() < 3600000 ? 0.3 : 0);
293
+ const scoreB = b.successRate * 0.7 + (Date.now() - b.lastUsed.getTime() < 3600000 ? 0.3 : 0);
294
+ return scoreB - scoreA;
295
+ })
296
+ .slice(0, 5);
297
+ }
298
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Client for interacting with the Hivemind memory daemon API
3
+ */
4
+
5
+ import axios from 'axios';
6
+ import { EventEmitter } from 'events';
7
+
8
+ export interface MemorySearchResult {
9
+ content: string;
10
+ similarity: number;
11
+ metadata?: Record<string, any>;
12
+ }
13
+
14
+ export interface L3Knowledge {
15
+ insight: string;
16
+ confidence: number;
17
+ sources: string[];
18
+ }
19
+
20
+ export interface MemoryStats {
21
+ episodes: number;
22
+ contexts: string[];
23
+ l3Entries: number;
24
+ }
25
+
26
+ export class MemoryAPIClient extends EventEmitter {
27
+ private baseURL: string;
28
+ private context: string;
29
+
30
+ constructor(baseURL: string = 'http://localhost:3434', context: string = 'global') {
31
+ super();
32
+ this.baseURL = baseURL;
33
+ this.context = context;
34
+ }
35
+
36
+ async health(): Promise<boolean> {
37
+ try {
38
+ const response = await axios.get(`${this.baseURL}/health`);
39
+ return response.status === 200;
40
+ } catch (error) {
41
+ this.emit('error', error);
42
+ return false;
43
+ }
44
+ }
45
+
46
+ async search(query: string, limit: number = 10): Promise<MemorySearchResult[]> {
47
+ try {
48
+ const response = await axios.get(`${this.baseURL}/search`, {
49
+ params: { q: query, context: this.context, limit }
50
+ });
51
+ return response.data.results || [];
52
+ } catch (error) {
53
+ this.emit('error', error);
54
+ return [];
55
+ }
56
+ }
57
+
58
+ async crossSearch(query: string, limit: number = 10): Promise<MemorySearchResult[]> {
59
+ try {
60
+ const response = await axios.get(`${this.baseURL}/cross-search`, {
61
+ params: { q: query, limit }
62
+ });
63
+ return response.data.results || [];
64
+ } catch (error) {
65
+ this.emit('error', error);
66
+ return [];
67
+ }
68
+ }
69
+
70
+ async getL3Knowledge(): Promise<L3Knowledge[]> {
71
+ try {
72
+ const response = await axios.get(`${this.baseURL}/promotion/l3`, {
73
+ params: { context: this.context }
74
+ });
75
+ return response.data.knowledge || [];
76
+ } catch (error) {
77
+ this.emit('error', error);
78
+ return [];
79
+ }
80
+ }
81
+
82
+ async getStats(): Promise<MemoryStats | null> {
83
+ try {
84
+ const response = await axios.get(`${this.baseURL}/stats`);
85
+ return response.data;
86
+ } catch (error) {
87
+ this.emit('error', error);
88
+ return null;
89
+ }
90
+ }
91
+
92
+ async store(content: string, metadata?: Record<string, any>): Promise<boolean> {
93
+ try {
94
+ const response = await axios.post(`${this.baseURL}/store`, {
95
+ context: this.context,
96
+ content,
97
+ metadata
98
+ });
99
+ return response.status === 200;
100
+ } catch (error) {
101
+ this.emit('error', error);
102
+ return false;
103
+ }
104
+ }
105
+ }