agentic-flow 1.8.11 → 1.8.14
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/CHANGELOG.md +58 -0
- package/dist/agents/claudeAgentDirect.js +168 -0
- package/dist/cli/federation-cli.d.ts +53 -0
- package/dist/cli/federation-cli.js +431 -0
- package/dist/cli-proxy.js +32 -4
- package/dist/federation/EphemeralAgent.js +258 -0
- package/dist/federation/FederationHub.js +283 -0
- package/dist/federation/FederationHubClient.js +212 -0
- package/dist/federation/FederationHubServer.js +436 -0
- package/dist/federation/SecurityManager.js +191 -0
- package/dist/federation/debug/agent-debug-stream.js +474 -0
- package/dist/federation/debug/debug-stream.js +419 -0
- package/dist/federation/index.js +12 -0
- package/dist/federation/integrations/realtime-federation.js +404 -0
- package/dist/federation/integrations/supabase-adapter-debug.js +400 -0
- package/dist/federation/integrations/supabase-adapter.js +258 -0
- package/dist/utils/cli.js +5 -0
- package/docs/architecture/FEDERATION-DATA-LIFECYCLE.md +520 -0
- package/docs/federation/AGENT-DEBUG-STREAMING.md +403 -0
- package/docs/federation/DEBUG-STREAMING-COMPLETE.md +432 -0
- package/docs/federation/DEBUG-STREAMING.md +537 -0
- package/docs/federation/DEPLOYMENT-VALIDATION-SUCCESS.md +394 -0
- package/docs/federation/DOCKER-FEDERATION-DEEP-REVIEW.md +478 -0
- package/docs/issues/ISSUE-SUPABASE-INTEGRATION.md +536 -0
- package/docs/releases/RELEASE-v1.8.13.md +426 -0
- package/docs/supabase/IMPLEMENTATION-SUMMARY.md +498 -0
- package/docs/supabase/INDEX.md +358 -0
- package/docs/supabase/QUICKSTART.md +365 -0
- package/docs/supabase/README.md +318 -0
- package/docs/supabase/SUPABASE-REALTIME-FEDERATION.md +575 -0
- package/docs/supabase/TEST-REPORT.md +446 -0
- package/docs/supabase/migrations/001_create_federation_tables.sql +339 -0
- package/docs/validation/reports/REGRESSION-TEST-V1.8.11.md +456 -0
- package/package.json +4 -1
- package/wasm/reasoningbank/reasoningbank_wasm_bg.js +2 -2
- package/wasm/reasoningbank/reasoningbank_wasm_bg.wasm +0 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Real-Time Federation System using Supabase
|
|
3
|
+
*
|
|
4
|
+
* Leverages Supabase Real-Time for:
|
|
5
|
+
* - Live agent coordination
|
|
6
|
+
* - Instant memory synchronization
|
|
7
|
+
* - Real-time presence tracking
|
|
8
|
+
* - Collaborative multi-agent workflows
|
|
9
|
+
* - Event-driven agent communication
|
|
10
|
+
*/
|
|
11
|
+
import { createClient } from '@supabase/supabase-js';
|
|
12
|
+
export class RealtimeFederationHub {
|
|
13
|
+
client;
|
|
14
|
+
config;
|
|
15
|
+
channels = new Map();
|
|
16
|
+
presenceChannel;
|
|
17
|
+
agentId;
|
|
18
|
+
tenantId;
|
|
19
|
+
heartbeatInterval;
|
|
20
|
+
eventHandlers = new Map();
|
|
21
|
+
constructor(config, agentId, tenantId = 'default') {
|
|
22
|
+
this.config = config;
|
|
23
|
+
this.agentId = agentId;
|
|
24
|
+
this.tenantId = tenantId;
|
|
25
|
+
const key = config.serviceRoleKey || config.anonKey;
|
|
26
|
+
this.client = createClient(config.url, key, {
|
|
27
|
+
realtime: {
|
|
28
|
+
params: {
|
|
29
|
+
eventsPerSecond: config.broadcastLatency === 'low' ? 10 : 2,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Initialize real-time federation
|
|
36
|
+
*/
|
|
37
|
+
async initialize() {
|
|
38
|
+
console.log('🌐 Initializing Real-Time Federation Hub...');
|
|
39
|
+
console.log(` Agent: ${this.agentId}`);
|
|
40
|
+
console.log(` Tenant: ${this.tenantId}`);
|
|
41
|
+
// Set up presence tracking
|
|
42
|
+
await this.setupPresence();
|
|
43
|
+
// Set up memory sync channel
|
|
44
|
+
if (this.config.memorySync !== false) {
|
|
45
|
+
await this.setupMemorySync();
|
|
46
|
+
}
|
|
47
|
+
// Set up coordination channel
|
|
48
|
+
await this.setupCoordination();
|
|
49
|
+
// Start heartbeat
|
|
50
|
+
this.startHeartbeat();
|
|
51
|
+
console.log('✅ Real-Time Federation Hub Active');
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Set up presence tracking for all agents in tenant
|
|
55
|
+
*/
|
|
56
|
+
async setupPresence() {
|
|
57
|
+
const channelName = `presence:${this.tenantId}`;
|
|
58
|
+
this.presenceChannel = this.client.channel(channelName, {
|
|
59
|
+
config: {
|
|
60
|
+
presence: {
|
|
61
|
+
key: this.agentId,
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
// Track agent join
|
|
66
|
+
this.presenceChannel.on('presence', { event: 'join' }, ({ key, newPresences }) => {
|
|
67
|
+
console.log(`🟢 Agent joined: ${key}`);
|
|
68
|
+
this.emit('agent:join', { agent_id: key, presences: newPresences });
|
|
69
|
+
});
|
|
70
|
+
// Track agent leave
|
|
71
|
+
this.presenceChannel.on('presence', { event: 'leave' }, ({ key, leftPresences }) => {
|
|
72
|
+
console.log(`🔴 Agent left: ${key}`);
|
|
73
|
+
this.emit('agent:leave', { agent_id: key, presences: leftPresences });
|
|
74
|
+
});
|
|
75
|
+
// Track agent sync (periodic updates)
|
|
76
|
+
this.presenceChannel.on('presence', { event: 'sync' }, () => {
|
|
77
|
+
const state = this.presenceChannel.presenceState();
|
|
78
|
+
this.emit('agents:sync', { agents: this.getActiveAgents(state) });
|
|
79
|
+
});
|
|
80
|
+
// Subscribe and track presence
|
|
81
|
+
await this.presenceChannel.subscribe(async (status) => {
|
|
82
|
+
if (status === 'SUBSCRIBED') {
|
|
83
|
+
await this.presenceChannel.track({
|
|
84
|
+
agent_id: this.agentId,
|
|
85
|
+
tenant_id: this.tenantId,
|
|
86
|
+
status: 'online',
|
|
87
|
+
started_at: new Date().toISOString(),
|
|
88
|
+
last_heartbeat: new Date().toISOString(),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Set up real-time memory synchronization
|
|
95
|
+
*/
|
|
96
|
+
async setupMemorySync() {
|
|
97
|
+
const channelName = `memories:${this.tenantId}`;
|
|
98
|
+
const channel = this.client.channel(channelName);
|
|
99
|
+
// Listen for new memories from database
|
|
100
|
+
channel
|
|
101
|
+
.on('postgres_changes', {
|
|
102
|
+
event: 'INSERT',
|
|
103
|
+
schema: 'public',
|
|
104
|
+
table: 'agent_memories',
|
|
105
|
+
filter: `tenant_id=eq.${this.tenantId}`,
|
|
106
|
+
}, (payload) => {
|
|
107
|
+
const memory = {
|
|
108
|
+
type: 'memory_added',
|
|
109
|
+
tenant_id: payload.new.tenant_id,
|
|
110
|
+
agent_id: payload.new.agent_id,
|
|
111
|
+
session_id: payload.new.session_id,
|
|
112
|
+
content: payload.new.content,
|
|
113
|
+
embedding: payload.new.embedding,
|
|
114
|
+
metadata: payload.new.metadata,
|
|
115
|
+
timestamp: payload.new.created_at,
|
|
116
|
+
};
|
|
117
|
+
this.emit('memory:added', memory);
|
|
118
|
+
})
|
|
119
|
+
.on('postgres_changes', {
|
|
120
|
+
event: 'UPDATE',
|
|
121
|
+
schema: 'public',
|
|
122
|
+
table: 'agent_memories',
|
|
123
|
+
filter: `tenant_id=eq.${this.tenantId}`,
|
|
124
|
+
}, (payload) => {
|
|
125
|
+
const memory = {
|
|
126
|
+
type: 'memory_updated',
|
|
127
|
+
tenant_id: payload.new.tenant_id,
|
|
128
|
+
agent_id: payload.new.agent_id,
|
|
129
|
+
session_id: payload.new.session_id,
|
|
130
|
+
content: payload.new.content,
|
|
131
|
+
embedding: payload.new.embedding,
|
|
132
|
+
metadata: payload.new.metadata,
|
|
133
|
+
timestamp: new Date().toISOString(),
|
|
134
|
+
};
|
|
135
|
+
this.emit('memory:updated', memory);
|
|
136
|
+
});
|
|
137
|
+
await channel.subscribe();
|
|
138
|
+
this.channels.set('memory-sync', channel);
|
|
139
|
+
console.log('💾 Real-time memory sync enabled');
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Set up coordination channel for agent-to-agent communication
|
|
143
|
+
*/
|
|
144
|
+
async setupCoordination() {
|
|
145
|
+
const channelName = `coordination:${this.tenantId}`;
|
|
146
|
+
const channel = this.client.channel(channelName);
|
|
147
|
+
// Listen for broadcast messages
|
|
148
|
+
channel.on('broadcast', { event: 'coordination' }, ({ payload }) => {
|
|
149
|
+
const message = payload;
|
|
150
|
+
// Only process if message is for us or broadcast
|
|
151
|
+
if (!message.to_agent || message.to_agent === this.agentId) {
|
|
152
|
+
this.emit('message:received', message);
|
|
153
|
+
// Emit specific event types
|
|
154
|
+
this.emit(`message:${message.type}`, message);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
await channel.subscribe();
|
|
158
|
+
this.channels.set('coordination', channel);
|
|
159
|
+
console.log('📡 Agent coordination channel active');
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Broadcast message to all agents in tenant
|
|
163
|
+
*/
|
|
164
|
+
async broadcast(type, payload) {
|
|
165
|
+
const channel = this.channels.get('coordination');
|
|
166
|
+
if (!channel) {
|
|
167
|
+
throw new Error('Coordination channel not initialized');
|
|
168
|
+
}
|
|
169
|
+
const message = {
|
|
170
|
+
from_agent: this.agentId,
|
|
171
|
+
type,
|
|
172
|
+
payload,
|
|
173
|
+
timestamp: new Date().toISOString(),
|
|
174
|
+
};
|
|
175
|
+
await channel.send({
|
|
176
|
+
type: 'broadcast',
|
|
177
|
+
event: 'coordination',
|
|
178
|
+
payload: message,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Send direct message to specific agent
|
|
183
|
+
*/
|
|
184
|
+
async sendMessage(toAgent, type, payload) {
|
|
185
|
+
const channel = this.channels.get('coordination');
|
|
186
|
+
if (!channel) {
|
|
187
|
+
throw new Error('Coordination channel not initialized');
|
|
188
|
+
}
|
|
189
|
+
const message = {
|
|
190
|
+
from_agent: this.agentId,
|
|
191
|
+
to_agent: toAgent,
|
|
192
|
+
type,
|
|
193
|
+
payload,
|
|
194
|
+
timestamp: new Date().toISOString(),
|
|
195
|
+
};
|
|
196
|
+
await channel.send({
|
|
197
|
+
type: 'broadcast',
|
|
198
|
+
event: 'coordination',
|
|
199
|
+
payload: message,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Assign task to another agent
|
|
204
|
+
*/
|
|
205
|
+
async assignTask(task) {
|
|
206
|
+
await this.sendMessage(task.assigned_to, 'task_assignment', task);
|
|
207
|
+
console.log(`📋 Task assigned: ${task.task_id} → ${task.assigned_to}`);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Report task completion
|
|
211
|
+
*/
|
|
212
|
+
async reportTaskComplete(taskId, result) {
|
|
213
|
+
await this.broadcast('task_complete', {
|
|
214
|
+
task_id: taskId,
|
|
215
|
+
result,
|
|
216
|
+
completed_by: this.agentId,
|
|
217
|
+
});
|
|
218
|
+
console.log(`✅ Task completed: ${taskId}`);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Request help from other agents
|
|
222
|
+
*/
|
|
223
|
+
async requestHelp(problem, context) {
|
|
224
|
+
await this.broadcast('request_help', {
|
|
225
|
+
problem,
|
|
226
|
+
context,
|
|
227
|
+
from: this.agentId,
|
|
228
|
+
});
|
|
229
|
+
console.log(`🆘 Help requested: ${problem}`);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Share knowledge with other agents
|
|
233
|
+
*/
|
|
234
|
+
async shareKnowledge(knowledge, metadata) {
|
|
235
|
+
await this.broadcast('share_knowledge', {
|
|
236
|
+
knowledge,
|
|
237
|
+
metadata,
|
|
238
|
+
from: this.agentId,
|
|
239
|
+
});
|
|
240
|
+
console.log(`💡 Knowledge shared: ${knowledge.substring(0, 50)}...`);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Update agent status
|
|
244
|
+
*/
|
|
245
|
+
async updateStatus(status, task) {
|
|
246
|
+
if (!this.presenceChannel)
|
|
247
|
+
return;
|
|
248
|
+
await this.presenceChannel.track({
|
|
249
|
+
agent_id: this.agentId,
|
|
250
|
+
tenant_id: this.tenantId,
|
|
251
|
+
status,
|
|
252
|
+
task,
|
|
253
|
+
last_heartbeat: new Date().toISOString(),
|
|
254
|
+
});
|
|
255
|
+
await this.broadcast('status_update', {
|
|
256
|
+
agent_id: this.agentId,
|
|
257
|
+
status,
|
|
258
|
+
task,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Get list of active agents
|
|
263
|
+
*/
|
|
264
|
+
getActiveAgents(presenceState) {
|
|
265
|
+
if (!this.presenceChannel)
|
|
266
|
+
return [];
|
|
267
|
+
const state = presenceState || this.presenceChannel.presenceState();
|
|
268
|
+
const agents = [];
|
|
269
|
+
for (const [agentId, presences] of Object.entries(state)) {
|
|
270
|
+
const presence = presences[0];
|
|
271
|
+
agents.push({
|
|
272
|
+
agent_id: agentId,
|
|
273
|
+
tenant_id: presence.tenant_id,
|
|
274
|
+
status: presence.status,
|
|
275
|
+
task: presence.task,
|
|
276
|
+
started_at: presence.started_at,
|
|
277
|
+
last_heartbeat: presence.last_heartbeat,
|
|
278
|
+
metadata: presence.metadata,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
return agents;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Start heartbeat to maintain presence
|
|
285
|
+
*/
|
|
286
|
+
startHeartbeat() {
|
|
287
|
+
const interval = this.config.presenceHeartbeat || 30000;
|
|
288
|
+
this.heartbeatInterval = setInterval(async () => {
|
|
289
|
+
if (this.presenceChannel) {
|
|
290
|
+
await this.presenceChannel.track({
|
|
291
|
+
agent_id: this.agentId,
|
|
292
|
+
tenant_id: this.tenantId,
|
|
293
|
+
last_heartbeat: new Date().toISOString(),
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}, interval);
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Stop heartbeat
|
|
300
|
+
*/
|
|
301
|
+
stopHeartbeat() {
|
|
302
|
+
if (this.heartbeatInterval) {
|
|
303
|
+
clearInterval(this.heartbeatInterval);
|
|
304
|
+
this.heartbeatInterval = undefined;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Subscribe to events
|
|
309
|
+
*/
|
|
310
|
+
on(event, handler) {
|
|
311
|
+
if (!this.eventHandlers.has(event)) {
|
|
312
|
+
this.eventHandlers.set(event, new Set());
|
|
313
|
+
}
|
|
314
|
+
this.eventHandlers.get(event).add(handler);
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Unsubscribe from events
|
|
318
|
+
*/
|
|
319
|
+
off(event, handler) {
|
|
320
|
+
const handlers = this.eventHandlers.get(event);
|
|
321
|
+
if (handlers) {
|
|
322
|
+
handlers.delete(handler);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Emit event to handlers
|
|
327
|
+
*/
|
|
328
|
+
emit(event, data) {
|
|
329
|
+
const handlers = this.eventHandlers.get(event);
|
|
330
|
+
if (handlers) {
|
|
331
|
+
handlers.forEach((handler) => {
|
|
332
|
+
try {
|
|
333
|
+
handler(data);
|
|
334
|
+
}
|
|
335
|
+
catch (error) {
|
|
336
|
+
console.error(`Error in event handler for ${event}:`, error);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Get real-time statistics
|
|
343
|
+
*/
|
|
344
|
+
async getStats() {
|
|
345
|
+
const activeAgents = this.getActiveAgents();
|
|
346
|
+
return {
|
|
347
|
+
tenant_id: this.tenantId,
|
|
348
|
+
agent_id: this.agentId,
|
|
349
|
+
active_agents: activeAgents.length,
|
|
350
|
+
agents: activeAgents,
|
|
351
|
+
channels: Array.from(this.channels.keys()),
|
|
352
|
+
heartbeat_interval: this.config.presenceHeartbeat || 30000,
|
|
353
|
+
memory_sync: this.config.memorySync !== false,
|
|
354
|
+
timestamp: new Date().toISOString(),
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Shutdown and cleanup
|
|
359
|
+
*/
|
|
360
|
+
async shutdown() {
|
|
361
|
+
console.log('🛑 Shutting down Real-Time Federation Hub...');
|
|
362
|
+
// Stop heartbeat
|
|
363
|
+
this.stopHeartbeat();
|
|
364
|
+
// Update status to offline
|
|
365
|
+
if (this.presenceChannel) {
|
|
366
|
+
await this.presenceChannel.track({
|
|
367
|
+
agent_id: this.agentId,
|
|
368
|
+
tenant_id: this.tenantId,
|
|
369
|
+
status: 'offline',
|
|
370
|
+
});
|
|
371
|
+
await this.presenceChannel.untrack();
|
|
372
|
+
}
|
|
373
|
+
// Unsubscribe from all channels
|
|
374
|
+
for (const [name, channel] of this.channels) {
|
|
375
|
+
await channel.unsubscribe();
|
|
376
|
+
console.log(` Unsubscribed from ${name}`);
|
|
377
|
+
}
|
|
378
|
+
if (this.presenceChannel) {
|
|
379
|
+
await this.presenceChannel.unsubscribe();
|
|
380
|
+
}
|
|
381
|
+
this.channels.clear();
|
|
382
|
+
this.eventHandlers.clear();
|
|
383
|
+
console.log('✅ Real-Time Federation Hub shutdown complete');
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Create real-time federation hub from environment
|
|
388
|
+
*/
|
|
389
|
+
export function createRealtimeHub(agentId, tenantId = 'default') {
|
|
390
|
+
const url = process.env.SUPABASE_URL;
|
|
391
|
+
const anonKey = process.env.SUPABASE_ANON_KEY;
|
|
392
|
+
const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
|
|
393
|
+
if (!url || !anonKey) {
|
|
394
|
+
throw new Error('Missing Supabase credentials. Set SUPABASE_URL and SUPABASE_ANON_KEY');
|
|
395
|
+
}
|
|
396
|
+
return new RealtimeFederationHub({
|
|
397
|
+
url,
|
|
398
|
+
anonKey,
|
|
399
|
+
serviceRoleKey,
|
|
400
|
+
presenceHeartbeat: parseInt(process.env.FEDERATION_HEARTBEAT_INTERVAL || '30000'),
|
|
401
|
+
memorySync: process.env.FEDERATION_MEMORY_SYNC !== 'false',
|
|
402
|
+
broadcastLatency: process.env.FEDERATION_BROADCAST_LATENCY || 'low',
|
|
403
|
+
}, agentId, tenantId);
|
|
404
|
+
}
|