@sesamespace/hivemind 0.8.10 → 0.8.11
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/MEMORY-ENHANCEMENT-PLAN.md +211 -0
- package/package.json +4 -2
- package/src/memory/dashboard-integration.ts +295 -0
- package/src/memory/index.ts +187 -0
- package/src/memory/performance-test.ts +208 -0
- package/src/memory/processors/agent-sync.ts +312 -0
- package/src/memory/processors/command-learner.ts +298 -0
- package/src/memory/processors/memory-api-client.ts +105 -0
- package/src/memory/processors/message-flow-integration.ts +168 -0
- package/src/memory/processors/research-digester.ts +204 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hivemind Memory System - Automatic context management for all agents
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { MessageFlowIntegration, MessageFlowConfig } from './processors/message-flow-integration';
|
|
6
|
+
import { ResearchDigester } from './processors/research-digester';
|
|
7
|
+
import { CommandLearner } from './processors/command-learner';
|
|
8
|
+
import { AgentSync } from './processors/agent-sync';
|
|
9
|
+
import { DashboardIntegration } from './dashboard-integration';
|
|
10
|
+
import { EventEmitter } from 'events';
|
|
11
|
+
|
|
12
|
+
export interface MemorySystemConfig {
|
|
13
|
+
agentId: string;
|
|
14
|
+
agentName: string;
|
|
15
|
+
workspaceRoot: string;
|
|
16
|
+
memoryURL?: string;
|
|
17
|
+
context?: string;
|
|
18
|
+
maxContextTokens?: number;
|
|
19
|
+
dashboardPort?: number;
|
|
20
|
+
syncEndpoints?: Map<string, string>; // Other agents to sync with
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class MemorySystem extends EventEmitter {
|
|
24
|
+
private messageFlow: MessageFlowIntegration;
|
|
25
|
+
private researchDigester: ResearchDigester;
|
|
26
|
+
private commandLearner: CommandLearner;
|
|
27
|
+
private agentSync: AgentSync;
|
|
28
|
+
private dashboard?: DashboardIntegration;
|
|
29
|
+
private config: MemorySystemConfig;
|
|
30
|
+
|
|
31
|
+
constructor(config: MemorySystemConfig) {
|
|
32
|
+
super();
|
|
33
|
+
this.config = config;
|
|
34
|
+
|
|
35
|
+
// Initialize core message flow
|
|
36
|
+
this.messageFlow = new MessageFlowIntegration({
|
|
37
|
+
workspaceRoot: config.workspaceRoot,
|
|
38
|
+
memoryURL: config.memoryURL,
|
|
39
|
+
context: config.context,
|
|
40
|
+
maxContextTokens: config.maxContextTokens
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Initialize processors
|
|
44
|
+
this.researchDigester = new ResearchDigester();
|
|
45
|
+
this.commandLearner = new CommandLearner();
|
|
46
|
+
this.agentSync = new AgentSync(config.agentId, config.agentName);
|
|
47
|
+
|
|
48
|
+
// Register processors with message flow
|
|
49
|
+
this.messageFlow['processManager'].register('research-digester', this.researchDigester);
|
|
50
|
+
this.messageFlow['processManager'].register('command-learner', this.commandLearner);
|
|
51
|
+
this.messageFlow['processManager'].register('agent-sync', this.agentSync);
|
|
52
|
+
|
|
53
|
+
// Set up event forwarding
|
|
54
|
+
this.setupEventForwarding();
|
|
55
|
+
|
|
56
|
+
// Register sync endpoints if provided
|
|
57
|
+
if (config.syncEndpoints) {
|
|
58
|
+
for (const [agentId, endpoint] of config.syncEndpoints) {
|
|
59
|
+
this.agentSync.registerAgent(agentId, agentId, endpoint);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private setupEventForwarding(): void {
|
|
65
|
+
// Forward errors
|
|
66
|
+
this.messageFlow.on('error', (error) => this.emit('error', error));
|
|
67
|
+
this.researchDigester.on('error', (error) => this.emit('error', { source: 'research', error }));
|
|
68
|
+
this.commandLearner.on('error', (error) => this.emit('error', { source: 'commands', error }));
|
|
69
|
+
this.agentSync.on('error', (error) => this.emit('error', { source: 'sync', error }));
|
|
70
|
+
|
|
71
|
+
// Forward interesting events
|
|
72
|
+
this.messageFlow.on('context-built', (data) => this.emit('context-built', data));
|
|
73
|
+
this.researchDigester.on('research-digested', (entry) => this.emit('research-added', entry));
|
|
74
|
+
this.commandLearner.on('patterns-updated', (stats) => this.emit('commands-updated', stats));
|
|
75
|
+
this.agentSync.on('handoff-received', (handoff) => this.emit('task-handoff', handoff));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Start the memory system
|
|
80
|
+
*/
|
|
81
|
+
async start(): Promise<void> {
|
|
82
|
+
// Start message flow (which starts all processors)
|
|
83
|
+
await this.messageFlow.start();
|
|
84
|
+
|
|
85
|
+
// Start dashboard if port specified
|
|
86
|
+
if (this.config.dashboardPort) {
|
|
87
|
+
this.dashboard = new DashboardIntegration({
|
|
88
|
+
port: this.config.dashboardPort,
|
|
89
|
+
messageFlow: this.messageFlow,
|
|
90
|
+
researchDigester: this.researchDigester,
|
|
91
|
+
commandLearner: this.commandLearner,
|
|
92
|
+
agentSync: this.agentSync
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
await this.dashboard.start(this.config.dashboardPort);
|
|
96
|
+
this.emit('dashboard-started', { port: this.config.dashboardPort });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this.emit('started');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Stop the memory system
|
|
104
|
+
*/
|
|
105
|
+
async stop(): Promise<void> {
|
|
106
|
+
await this.messageFlow.stop();
|
|
107
|
+
this.emit('stopped');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Process a message and get enriched context
|
|
112
|
+
*/
|
|
113
|
+
async processMessage(message: {
|
|
114
|
+
role: 'user' | 'assistant';
|
|
115
|
+
content: string;
|
|
116
|
+
metadata?: Record<string, any>;
|
|
117
|
+
}): Promise<string> {
|
|
118
|
+
return this.messageFlow.processMessage({
|
|
119
|
+
...message,
|
|
120
|
+
timestamp: new Date(),
|
|
121
|
+
context: this.config.context
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Add research content (from web pages, PDFs, etc.)
|
|
127
|
+
*/
|
|
128
|
+
async addResearch(content: string, metadata?: {
|
|
129
|
+
url?: string;
|
|
130
|
+
title?: string;
|
|
131
|
+
sourceType?: 'web' | 'pdf' | 'markdown' | 'other';
|
|
132
|
+
}): Promise<void> {
|
|
133
|
+
await this.researchDigester.addContent(content, metadata);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Track a command execution
|
|
138
|
+
*/
|
|
139
|
+
async trackCommand(command: string, context: string, output?: string, success: boolean = true): Promise<void> {
|
|
140
|
+
await this.commandLearner.trackCommand(command, context, output, success);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Share a task with other agents
|
|
145
|
+
*/
|
|
146
|
+
async shareTask(task: {
|
|
147
|
+
description: string;
|
|
148
|
+
status: 'active' | 'completed' | 'blocked';
|
|
149
|
+
dependencies?: string[];
|
|
150
|
+
progress?: number;
|
|
151
|
+
notes?: string[];
|
|
152
|
+
}): Promise<string> {
|
|
153
|
+
return this.agentSync.shareTask({
|
|
154
|
+
...task,
|
|
155
|
+
assignedTo: this.config.agentName,
|
|
156
|
+
dependencies: task.dependencies || [],
|
|
157
|
+
progress: task.progress || 0,
|
|
158
|
+
notes: task.notes || []
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Share an insight
|
|
164
|
+
*/
|
|
165
|
+
async shareInsight(insight: string, context?: string): Promise<void> {
|
|
166
|
+
await this.agentSync.shareInsight(insight, context);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Get current system state
|
|
171
|
+
*/
|
|
172
|
+
async getState(): Promise<any> {
|
|
173
|
+
return this.messageFlow.getState();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Export all components for advanced usage
|
|
178
|
+
export { MessageFlowIntegration } from './processors/message-flow-integration';
|
|
179
|
+
export { BackgroundProcessor, ProcessManager } from './processors/background-processor';
|
|
180
|
+
export { CodeIndexer } from './processors/code-indexer';
|
|
181
|
+
export { TaskTracker } from './processors/task-tracker';
|
|
182
|
+
export { ResearchDigester } from './processors/research-digester';
|
|
183
|
+
export { CommandLearner } from './processors/command-learner';
|
|
184
|
+
export { AgentSync } from './processors/agent-sync';
|
|
185
|
+
export { ContextManager } from './processors/context-manager';
|
|
186
|
+
export { MemoryAPIClient } from './processors/memory-api-client';
|
|
187
|
+
export { DashboardIntegration } from './dashboard-integration';
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance test for the memory management system
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { MemorySystem } from './index';
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
|
|
9
|
+
async function runPerformanceTest() {
|
|
10
|
+
console.log('🧪 Starting Hivemind Memory System Performance Test\n');
|
|
11
|
+
|
|
12
|
+
const startTime = Date.now();
|
|
13
|
+
const workspaceRoot = path.join(__dirname, '../../test-workspace');
|
|
14
|
+
|
|
15
|
+
// Create test workspace
|
|
16
|
+
if (!fs.existsSync(workspaceRoot)) {
|
|
17
|
+
fs.mkdirSync(workspaceRoot, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Initialize memory system
|
|
21
|
+
const memory = new MemorySystem({
|
|
22
|
+
agentId: 'test-agent-001',
|
|
23
|
+
agentName: 'TestAgent',
|
|
24
|
+
workspaceRoot,
|
|
25
|
+
memoryURL: 'http://localhost:3434',
|
|
26
|
+
context: 'performance-test',
|
|
27
|
+
maxContextTokens: 8000,
|
|
28
|
+
dashboardPort: 9487
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Track metrics
|
|
32
|
+
const metrics = {
|
|
33
|
+
messagesProcessed: 0,
|
|
34
|
+
contextBuildTime: [] as number[],
|
|
35
|
+
memoryUsage: [] as number[],
|
|
36
|
+
errors: 0
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Set up event listeners
|
|
40
|
+
memory.on('error', (error) => {
|
|
41
|
+
console.error('❌ Error:', error);
|
|
42
|
+
metrics.errors++;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
memory.on('context-built', (data) => {
|
|
46
|
+
console.log(`📝 Context built: ${data.contextLength} chars, ${data.sections} sections`);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
memory.on('started', () => {
|
|
50
|
+
console.log('✅ Memory system started\n');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
memory.on('dashboard-started', ({ port }) => {
|
|
54
|
+
console.log(`🌐 Dashboard available at http://localhost:${port}\n`);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
// Start the system
|
|
59
|
+
await memory.start();
|
|
60
|
+
|
|
61
|
+
// Test 1: Message processing
|
|
62
|
+
console.log('📊 Test 1: Message Processing');
|
|
63
|
+
const messages = [
|
|
64
|
+
{ role: 'user' as const, content: 'Can you help me debug the authentication system in src/auth/login.ts?' },
|
|
65
|
+
{ role: 'assistant' as const, content: 'I\'ll help you debug the authentication system. Let me look at src/auth/login.ts...' },
|
|
66
|
+
{ role: 'user' as const, content: 'The error happens when users try to login with OAuth' },
|
|
67
|
+
{ role: 'assistant' as const, content: 'I see. Let me check the OAuth configuration in src/auth/oauth.ts and src/config/oauth.json' }
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
for (const message of messages) {
|
|
71
|
+
const startContext = Date.now();
|
|
72
|
+
const context = await memory.processMessage(message);
|
|
73
|
+
const contextTime = Date.now() - startContext;
|
|
74
|
+
|
|
75
|
+
metrics.contextBuildTime.push(contextTime);
|
|
76
|
+
metrics.messagesProcessed++;
|
|
77
|
+
|
|
78
|
+
console.log(` - Processed ${message.role} message in ${contextTime}ms (context: ${context.length} chars)`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Test 2: Research ingestion
|
|
82
|
+
console.log('\n📊 Test 2: Research Ingestion');
|
|
83
|
+
const researchContent = `
|
|
84
|
+
# Understanding TypeScript Decorators
|
|
85
|
+
|
|
86
|
+
Decorators are a powerful feature in TypeScript that allow you to modify classes and their members.
|
|
87
|
+
|
|
88
|
+
## Key Points
|
|
89
|
+
- Decorators use the @ symbol
|
|
90
|
+
- They can be applied to classes, methods, properties, and parameters
|
|
91
|
+
- Decorators are executed at runtime
|
|
92
|
+
- Multiple decorators can be composed
|
|
93
|
+
|
|
94
|
+
## Common Use Cases
|
|
95
|
+
- Logging and monitoring
|
|
96
|
+
- Validation
|
|
97
|
+
- Dependency injection
|
|
98
|
+
- Route handling in web frameworks
|
|
99
|
+
|
|
100
|
+
## Example
|
|
101
|
+
\`\`\`typescript
|
|
102
|
+
@Injectable()
|
|
103
|
+
class UserService {
|
|
104
|
+
@Log()
|
|
105
|
+
async getUser(id: string) {
|
|
106
|
+
return this.db.findUser(id);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
\`\`\`
|
|
110
|
+
`;
|
|
111
|
+
|
|
112
|
+
await memory.addResearch(researchContent, {
|
|
113
|
+
title: 'TypeScript Decorators Guide',
|
|
114
|
+
sourceType: 'markdown'
|
|
115
|
+
});
|
|
116
|
+
console.log(' - Added research document');
|
|
117
|
+
|
|
118
|
+
// Test 3: Command tracking
|
|
119
|
+
console.log('\n📊 Test 3: Command Tracking');
|
|
120
|
+
const commands = [
|
|
121
|
+
{ cmd: 'git status', output: 'On branch main\nnothing to commit', success: true },
|
|
122
|
+
{ cmd: 'git pull origin main', output: 'Already up to date.', success: true },
|
|
123
|
+
{ cmd: 'npm test', output: 'Test suite failed', success: false },
|
|
124
|
+
{ cmd: 'npm test -- --fix', output: 'All tests passed', success: true }
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
for (const { cmd, output, success } of commands) {
|
|
128
|
+
await memory.trackCommand(cmd, 'debugging auth system', output, success);
|
|
129
|
+
console.log(` - Tracked command: ${cmd} (${success ? '✓' : '✗'})`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Test 4: Task sharing
|
|
133
|
+
console.log('\n📊 Test 4: Task Management');
|
|
134
|
+
const taskId = await memory.shareTask({
|
|
135
|
+
description: 'Fix OAuth authentication bug',
|
|
136
|
+
status: 'active',
|
|
137
|
+
progress: 45,
|
|
138
|
+
notes: ['Users report 401 errors', 'Might be related to token expiration']
|
|
139
|
+
});
|
|
140
|
+
console.log(` - Created shared task: ${taskId}`);
|
|
141
|
+
|
|
142
|
+
// Test 5: Insight sharing
|
|
143
|
+
await memory.shareInsight('OAuth tokens need refresh logic to handle expiration gracefully');
|
|
144
|
+
console.log(' - Shared insight about OAuth tokens');
|
|
145
|
+
|
|
146
|
+
// Test 6: Memory usage
|
|
147
|
+
console.log('\n📊 Test 6: Memory Usage');
|
|
148
|
+
for (let i = 0; i < 5; i++) {
|
|
149
|
+
const usage = process.memoryUsage();
|
|
150
|
+
metrics.memoryUsage.push(usage.heapUsed);
|
|
151
|
+
console.log(` - Heap used: ${(usage.heapUsed / 1024 / 1024).toFixed(2)} MB`);
|
|
152
|
+
|
|
153
|
+
// Process more messages to see memory growth
|
|
154
|
+
await memory.processMessage({
|
|
155
|
+
role: 'user',
|
|
156
|
+
content: `Test message ${i}: Working on feature ${i}`
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Test 7: Context building performance with large content
|
|
163
|
+
console.log('\n📊 Test 7: Large Context Performance');
|
|
164
|
+
const largeMessage = 'x'.repeat(1000) + ' Can you analyze this large codebase?';
|
|
165
|
+
const largeStart = Date.now();
|
|
166
|
+
const largeContext = await memory.processMessage({
|
|
167
|
+
role: 'user',
|
|
168
|
+
content: largeMessage
|
|
169
|
+
});
|
|
170
|
+
const largeTime = Date.now() - largeStart;
|
|
171
|
+
console.log(` - Large message processed in ${largeTime}ms (context: ${largeContext.length} chars)`);
|
|
172
|
+
|
|
173
|
+
// Get final state
|
|
174
|
+
const state = await memory.getState();
|
|
175
|
+
console.log('\n📊 Final System State:');
|
|
176
|
+
console.log(` - Memory episodes: ${state.memory?.episodes || 0}`);
|
|
177
|
+
console.log(` - Active tasks: ${state.tasks?.length || 0}`);
|
|
178
|
+
console.log(` - Working set files: ${state.workingSet?.length || 0}`);
|
|
179
|
+
console.log(` - Background processors: ${state.processors || 0}`);
|
|
180
|
+
|
|
181
|
+
// Calculate statistics
|
|
182
|
+
const avgContextTime = metrics.contextBuildTime.reduce((a, b) => a + b, 0) / metrics.contextBuildTime.length;
|
|
183
|
+
const maxMemory = Math.max(...metrics.memoryUsage);
|
|
184
|
+
const minMemory = Math.min(...metrics.memoryUsage);
|
|
185
|
+
|
|
186
|
+
console.log('\n📈 Performance Summary:');
|
|
187
|
+
console.log(` - Total runtime: ${((Date.now() - startTime) / 1000).toFixed(2)}s`);
|
|
188
|
+
console.log(` - Messages processed: ${metrics.messagesProcessed}`);
|
|
189
|
+
console.log(` - Avg context build time: ${avgContextTime.toFixed(2)}ms`);
|
|
190
|
+
console.log(` - Memory usage: ${(minMemory / 1024 / 1024).toFixed(2)} - ${(maxMemory / 1024 / 1024).toFixed(2)} MB`);
|
|
191
|
+
console.log(` - Errors encountered: ${metrics.errors}`);
|
|
192
|
+
|
|
193
|
+
// Stop the system
|
|
194
|
+
await memory.stop();
|
|
195
|
+
console.log('\n✅ Memory system stopped successfully');
|
|
196
|
+
|
|
197
|
+
} catch (error) {
|
|
198
|
+
console.error('\n❌ Test failed:', error);
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Clean up test workspace
|
|
203
|
+
fs.rmSync(workspaceRoot, { recursive: true, force: true });
|
|
204
|
+
console.log('\n🧹 Cleaned up test workspace');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Run the test
|
|
208
|
+
runPerformanceTest().catch(console.error);
|
|
@@ -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
|
+
}
|