agentic-flow 1.8.11 → 1.8.13
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/cli/federation-cli.d.ts +53 -0
- package/dist/cli/federation-cli.js +431 -0
- package/dist/cli-proxy.js +28 -1
- 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/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
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supabase Database Adapter with Debug Streaming
|
|
3
|
+
*
|
|
4
|
+
* Enhanced version of SupabaseFederationAdapter with comprehensive
|
|
5
|
+
* debug logging and performance tracking.
|
|
6
|
+
*/
|
|
7
|
+
import { createClient } from '@supabase/supabase-js';
|
|
8
|
+
import { DebugLevel, createDebugStream } from '../debug/debug-stream.js';
|
|
9
|
+
export class SupabaseFederationAdapterDebug {
|
|
10
|
+
client;
|
|
11
|
+
config;
|
|
12
|
+
debug;
|
|
13
|
+
constructor(config) {
|
|
14
|
+
this.config = config;
|
|
15
|
+
// Initialize debug stream
|
|
16
|
+
this.debug = createDebugStream({
|
|
17
|
+
level: config.debug?.level ?? DebugLevel.BASIC,
|
|
18
|
+
output: config.debug?.output ?? 'console',
|
|
19
|
+
format: config.debug?.format ?? 'human',
|
|
20
|
+
outputFile: config.debug?.outputFile,
|
|
21
|
+
});
|
|
22
|
+
const startTime = Date.now();
|
|
23
|
+
// Use service role key for server-side operations
|
|
24
|
+
const key = config.serviceRoleKey || config.anonKey;
|
|
25
|
+
this.client = createClient(config.url, key);
|
|
26
|
+
this.debug.logConnection('client_created', {
|
|
27
|
+
url: config.url,
|
|
28
|
+
hasServiceRole: !!config.serviceRoleKey,
|
|
29
|
+
vectorBackend: config.vectorBackend,
|
|
30
|
+
});
|
|
31
|
+
const duration = Date.now() - startTime;
|
|
32
|
+
this.debug.logTrace('constructor_complete', { duration });
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Initialize Supabase schema for federation
|
|
36
|
+
*/
|
|
37
|
+
async initialize() {
|
|
38
|
+
const startTime = Date.now();
|
|
39
|
+
this.debug.logConnection('initialize_start', {
|
|
40
|
+
vectorBackend: this.config.vectorBackend,
|
|
41
|
+
});
|
|
42
|
+
try {
|
|
43
|
+
// Check if tables exist
|
|
44
|
+
await this.ensureTables();
|
|
45
|
+
if (this.config.vectorBackend === 'pgvector') {
|
|
46
|
+
await this.ensureVectorExtension();
|
|
47
|
+
}
|
|
48
|
+
const duration = Date.now() - startTime;
|
|
49
|
+
this.debug.logConnection('initialize_complete', { duration }, duration);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
const duration = Date.now() - startTime;
|
|
53
|
+
this.debug.logConnection('initialize_error', { duration }, duration, error);
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Ensure required tables exist
|
|
59
|
+
*/
|
|
60
|
+
async ensureTables() {
|
|
61
|
+
const startTime = Date.now();
|
|
62
|
+
this.debug.logTrace('checking_tables');
|
|
63
|
+
const tables = ['agent_sessions', 'agent_memories', 'agent_tasks', 'agent_events'];
|
|
64
|
+
const results = {};
|
|
65
|
+
for (const table of tables) {
|
|
66
|
+
const tableStart = Date.now();
|
|
67
|
+
try {
|
|
68
|
+
const { data, error } = await this.client
|
|
69
|
+
.from(table)
|
|
70
|
+
.select('id')
|
|
71
|
+
.limit(1);
|
|
72
|
+
const exists = !error || error.code !== 'PGRST116';
|
|
73
|
+
results[table] = exists;
|
|
74
|
+
const tableDuration = Date.now() - tableStart;
|
|
75
|
+
this.debug.logDatabase('table_check', {
|
|
76
|
+
table,
|
|
77
|
+
exists,
|
|
78
|
+
}, tableDuration);
|
|
79
|
+
if (!exists) {
|
|
80
|
+
this.debug.logDatabase('table_missing', { table });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
const tableDuration = Date.now() - tableStart;
|
|
85
|
+
this.debug.logDatabase('table_check_error', { table }, tableDuration, error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const duration = Date.now() - startTime;
|
|
89
|
+
this.debug.logDatabase('tables_checked', { results }, duration);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Ensure pgvector extension is enabled
|
|
93
|
+
*/
|
|
94
|
+
async ensureVectorExtension() {
|
|
95
|
+
const startTime = Date.now();
|
|
96
|
+
this.debug.logTrace('checking_pgvector');
|
|
97
|
+
try {
|
|
98
|
+
const { error } = await this.client.rpc('exec_sql', {
|
|
99
|
+
sql: 'CREATE EXTENSION IF NOT EXISTS vector;'
|
|
100
|
+
});
|
|
101
|
+
const duration = Date.now() - startTime;
|
|
102
|
+
if (error) {
|
|
103
|
+
this.debug.logDatabase('pgvector_check_failed', {
|
|
104
|
+
message: error.message,
|
|
105
|
+
}, duration, error);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
this.debug.logDatabase('pgvector_ready', {}, duration);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
const duration = Date.now() - startTime;
|
|
113
|
+
this.debug.logDatabase('pgvector_error', {}, duration, err);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Store agent memory in Supabase
|
|
118
|
+
*/
|
|
119
|
+
async storeMemory(memory) {
|
|
120
|
+
const startTime = Date.now();
|
|
121
|
+
this.debug.logMemory('store_start', memory.agent_id, memory.tenant_id, {
|
|
122
|
+
id: memory.id,
|
|
123
|
+
content_length: memory.content.length,
|
|
124
|
+
has_embedding: !!memory.embedding,
|
|
125
|
+
embedding_dims: memory.embedding?.length,
|
|
126
|
+
});
|
|
127
|
+
try {
|
|
128
|
+
const { error } = await this.client
|
|
129
|
+
.from('agent_memories')
|
|
130
|
+
.insert({
|
|
131
|
+
id: memory.id,
|
|
132
|
+
tenant_id: memory.tenant_id,
|
|
133
|
+
agent_id: memory.agent_id,
|
|
134
|
+
session_id: memory.session_id,
|
|
135
|
+
content: memory.content,
|
|
136
|
+
embedding: memory.embedding,
|
|
137
|
+
metadata: memory.metadata,
|
|
138
|
+
created_at: memory.created_at || new Date().toISOString(),
|
|
139
|
+
expires_at: memory.expires_at,
|
|
140
|
+
});
|
|
141
|
+
const duration = Date.now() - startTime;
|
|
142
|
+
if (error) {
|
|
143
|
+
this.debug.logMemory('store_error', memory.agent_id, memory.tenant_id, {
|
|
144
|
+
error: error.message,
|
|
145
|
+
}, duration);
|
|
146
|
+
throw new Error(`Failed to store memory: ${error.message}`);
|
|
147
|
+
}
|
|
148
|
+
this.debug.logMemory('store_complete', memory.agent_id, memory.tenant_id, {
|
|
149
|
+
id: memory.id,
|
|
150
|
+
}, duration);
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
const duration = Date.now() - startTime;
|
|
154
|
+
this.debug.logMemory('store_failed', memory.agent_id, memory.tenant_id, {}, duration);
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Query memories by tenant and agent
|
|
160
|
+
*/
|
|
161
|
+
async queryMemories(tenantId, agentId, limit = 100) {
|
|
162
|
+
const startTime = Date.now();
|
|
163
|
+
this.debug.logMemory('query_start', agentId, tenantId, {
|
|
164
|
+
limit,
|
|
165
|
+
hasAgentFilter: !!agentId,
|
|
166
|
+
});
|
|
167
|
+
try {
|
|
168
|
+
let query = this.client
|
|
169
|
+
.from('agent_memories')
|
|
170
|
+
.select('*')
|
|
171
|
+
.eq('tenant_id', tenantId)
|
|
172
|
+
.order('created_at', { ascending: false })
|
|
173
|
+
.limit(limit);
|
|
174
|
+
if (agentId) {
|
|
175
|
+
query = query.eq('agent_id', agentId);
|
|
176
|
+
}
|
|
177
|
+
const { data, error } = await query;
|
|
178
|
+
const duration = Date.now() - startTime;
|
|
179
|
+
if (error) {
|
|
180
|
+
this.debug.logMemory('query_error', agentId, tenantId, {
|
|
181
|
+
error: error.message,
|
|
182
|
+
}, duration);
|
|
183
|
+
throw new Error(`Failed to query memories: ${error.message}`);
|
|
184
|
+
}
|
|
185
|
+
this.debug.logMemory('query_complete', agentId, tenantId, {
|
|
186
|
+
count: data?.length || 0,
|
|
187
|
+
}, duration);
|
|
188
|
+
return data;
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
const duration = Date.now() - startTime;
|
|
192
|
+
this.debug.logMemory('query_failed', agentId, tenantId, {}, duration);
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Semantic search using pgvector
|
|
198
|
+
*/
|
|
199
|
+
async semanticSearch(embedding, tenantId, limit = 10) {
|
|
200
|
+
const startTime = Date.now();
|
|
201
|
+
this.debug.logMemory('semantic_search_start', undefined, tenantId, {
|
|
202
|
+
embedding_dims: embedding.length,
|
|
203
|
+
limit,
|
|
204
|
+
});
|
|
205
|
+
if (this.config.vectorBackend !== 'pgvector') {
|
|
206
|
+
this.debug.logMemory('semantic_search_disabled', undefined, tenantId, {
|
|
207
|
+
backend: this.config.vectorBackend,
|
|
208
|
+
});
|
|
209
|
+
throw new Error('pgvector backend not enabled');
|
|
210
|
+
}
|
|
211
|
+
try {
|
|
212
|
+
const { data, error } = await this.client.rpc('search_memories', {
|
|
213
|
+
query_embedding: embedding,
|
|
214
|
+
query_tenant_id: tenantId,
|
|
215
|
+
match_count: limit,
|
|
216
|
+
});
|
|
217
|
+
const duration = Date.now() - startTime;
|
|
218
|
+
if (error) {
|
|
219
|
+
this.debug.logMemory('semantic_search_error', undefined, tenantId, {
|
|
220
|
+
error: error.message,
|
|
221
|
+
}, duration);
|
|
222
|
+
throw new Error(`Semantic search failed: ${error.message}`);
|
|
223
|
+
}
|
|
224
|
+
this.debug.logMemory('semantic_search_complete', undefined, tenantId, {
|
|
225
|
+
results: data?.length || 0,
|
|
226
|
+
}, duration);
|
|
227
|
+
return data;
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
const duration = Date.now() - startTime;
|
|
231
|
+
this.debug.logMemory('semantic_search_failed', undefined, tenantId, {}, duration);
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Register agent session
|
|
237
|
+
*/
|
|
238
|
+
async registerSession(sessionId, tenantId, agentId, metadata) {
|
|
239
|
+
const startTime = Date.now();
|
|
240
|
+
this.debug.logDatabase('register_session_start', {
|
|
241
|
+
sessionId,
|
|
242
|
+
tenantId,
|
|
243
|
+
agentId,
|
|
244
|
+
});
|
|
245
|
+
try {
|
|
246
|
+
const { error } = await this.client
|
|
247
|
+
.from('agent_sessions')
|
|
248
|
+
.insert({
|
|
249
|
+
session_id: sessionId,
|
|
250
|
+
tenant_id: tenantId,
|
|
251
|
+
agent_id: agentId,
|
|
252
|
+
metadata,
|
|
253
|
+
started_at: new Date().toISOString(),
|
|
254
|
+
status: 'active',
|
|
255
|
+
});
|
|
256
|
+
const duration = Date.now() - startTime;
|
|
257
|
+
if (error) {
|
|
258
|
+
this.debug.logDatabase('register_session_error', {
|
|
259
|
+
sessionId,
|
|
260
|
+
error: error.message,
|
|
261
|
+
}, duration, error);
|
|
262
|
+
throw new Error(`Failed to register session: ${error.message}`);
|
|
263
|
+
}
|
|
264
|
+
this.debug.logDatabase('register_session_complete', {
|
|
265
|
+
sessionId,
|
|
266
|
+
}, duration);
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
const duration = Date.now() - startTime;
|
|
270
|
+
this.debug.logDatabase('register_session_failed', { sessionId }, duration);
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Update session status
|
|
276
|
+
*/
|
|
277
|
+
async updateSessionStatus(sessionId, status) {
|
|
278
|
+
const startTime = Date.now();
|
|
279
|
+
this.debug.logDatabase('update_session_start', {
|
|
280
|
+
sessionId,
|
|
281
|
+
status,
|
|
282
|
+
});
|
|
283
|
+
try {
|
|
284
|
+
const updates = { status };
|
|
285
|
+
if (status !== 'active') {
|
|
286
|
+
updates.ended_at = new Date().toISOString();
|
|
287
|
+
}
|
|
288
|
+
const { error } = await this.client
|
|
289
|
+
.from('agent_sessions')
|
|
290
|
+
.update(updates)
|
|
291
|
+
.eq('session_id', sessionId);
|
|
292
|
+
const duration = Date.now() - startTime;
|
|
293
|
+
if (error) {
|
|
294
|
+
this.debug.logDatabase('update_session_error', {
|
|
295
|
+
sessionId,
|
|
296
|
+
error: error.message,
|
|
297
|
+
}, duration, error);
|
|
298
|
+
throw new Error(`Failed to update session: ${error.message}`);
|
|
299
|
+
}
|
|
300
|
+
this.debug.logDatabase('update_session_complete', {
|
|
301
|
+
sessionId,
|
|
302
|
+
status,
|
|
303
|
+
}, duration);
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
const duration = Date.now() - startTime;
|
|
307
|
+
this.debug.logDatabase('update_session_failed', { sessionId }, duration);
|
|
308
|
+
throw error;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Get hub statistics
|
|
313
|
+
*/
|
|
314
|
+
async getStats(tenantId) {
|
|
315
|
+
const startTime = Date.now();
|
|
316
|
+
this.debug.logDatabase('get_stats_start', { tenantId });
|
|
317
|
+
try {
|
|
318
|
+
// Total memories
|
|
319
|
+
let memoriesQuery = this.client
|
|
320
|
+
.from('agent_memories')
|
|
321
|
+
.select('id', { count: 'exact', head: true });
|
|
322
|
+
if (tenantId) {
|
|
323
|
+
memoriesQuery = memoriesQuery.eq('tenant_id', tenantId);
|
|
324
|
+
}
|
|
325
|
+
const { count: totalMemories } = await memoriesQuery;
|
|
326
|
+
// Active sessions
|
|
327
|
+
let sessionsQuery = this.client
|
|
328
|
+
.from('agent_sessions')
|
|
329
|
+
.select('session_id', { count: 'exact', head: true })
|
|
330
|
+
.eq('status', 'active');
|
|
331
|
+
if (tenantId) {
|
|
332
|
+
sessionsQuery = sessionsQuery.eq('tenant_id', tenantId);
|
|
333
|
+
}
|
|
334
|
+
const { count: activeSessions } = await sessionsQuery;
|
|
335
|
+
const duration = Date.now() - startTime;
|
|
336
|
+
const stats = {
|
|
337
|
+
total_memories: totalMemories || 0,
|
|
338
|
+
active_sessions: activeSessions || 0,
|
|
339
|
+
backend: 'supabase',
|
|
340
|
+
vector_backend: this.config.vectorBackend,
|
|
341
|
+
timestamp: new Date().toISOString(),
|
|
342
|
+
};
|
|
343
|
+
this.debug.logDatabase('get_stats_complete', stats, duration);
|
|
344
|
+
return stats;
|
|
345
|
+
}
|
|
346
|
+
catch (error) {
|
|
347
|
+
const duration = Date.now() - startTime;
|
|
348
|
+
this.debug.logDatabase('get_stats_error', {}, duration, error);
|
|
349
|
+
throw error;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Get debug stream for external use
|
|
354
|
+
*/
|
|
355
|
+
getDebugStream() {
|
|
356
|
+
return this.debug;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Print performance metrics
|
|
360
|
+
*/
|
|
361
|
+
printMetrics() {
|
|
362
|
+
this.debug.printMetrics();
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Close connection
|
|
366
|
+
*/
|
|
367
|
+
async close() {
|
|
368
|
+
const startTime = Date.now();
|
|
369
|
+
this.debug.logConnection('close_start');
|
|
370
|
+
this.debug.close();
|
|
371
|
+
const duration = Date.now() - startTime;
|
|
372
|
+
this.debug.logConnection('close_complete', {}, duration);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Create Supabase adapter from environment variables with debug support
|
|
377
|
+
*/
|
|
378
|
+
export function createSupabaseAdapterDebug() {
|
|
379
|
+
const url = process.env.SUPABASE_URL;
|
|
380
|
+
const anonKey = process.env.SUPABASE_ANON_KEY;
|
|
381
|
+
const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
|
|
382
|
+
if (!url || !anonKey) {
|
|
383
|
+
throw new Error('Missing Supabase credentials. Set SUPABASE_URL and SUPABASE_ANON_KEY');
|
|
384
|
+
}
|
|
385
|
+
const debugLevel = process.env.DEBUG_LEVEL?.toUpperCase() || 'BASIC';
|
|
386
|
+
return new SupabaseFederationAdapterDebug({
|
|
387
|
+
url,
|
|
388
|
+
anonKey,
|
|
389
|
+
serviceRoleKey,
|
|
390
|
+
vectorBackend: process.env.FEDERATION_VECTOR_BACKEND || 'hybrid',
|
|
391
|
+
syncInterval: parseInt(process.env.FEDERATION_SYNC_INTERVAL || '60000'),
|
|
392
|
+
debug: {
|
|
393
|
+
enabled: process.env.DEBUG_ENABLED !== 'false',
|
|
394
|
+
level: DebugLevel[debugLevel] || DebugLevel.BASIC,
|
|
395
|
+
output: process.env.DEBUG_OUTPUT || 'console',
|
|
396
|
+
format: process.env.DEBUG_FORMAT || 'human',
|
|
397
|
+
outputFile: process.env.DEBUG_OUTPUT_FILE,
|
|
398
|
+
},
|
|
399
|
+
});
|
|
400
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supabase Database Adapter for Federation Hub
|
|
3
|
+
*
|
|
4
|
+
* Provides PostgreSQL backend using Supabase for:
|
|
5
|
+
* - Hub persistence
|
|
6
|
+
* - Agent metadata
|
|
7
|
+
* - Memory storage (optional pgvector)
|
|
8
|
+
* - Real-time subscriptions
|
|
9
|
+
*/
|
|
10
|
+
import { createClient } from '@supabase/supabase-js';
|
|
11
|
+
export class SupabaseFederationAdapter {
|
|
12
|
+
client;
|
|
13
|
+
config;
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
// Use service role key for server-side operations
|
|
17
|
+
const key = config.serviceRoleKey || config.anonKey;
|
|
18
|
+
this.client = createClient(config.url, key);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Initialize Supabase schema for federation
|
|
22
|
+
*/
|
|
23
|
+
async initialize() {
|
|
24
|
+
console.log('🔧 Initializing Supabase Federation Schema...');
|
|
25
|
+
// Check if tables exist, create if needed
|
|
26
|
+
await this.ensureTables();
|
|
27
|
+
if (this.config.vectorBackend === 'pgvector') {
|
|
28
|
+
await this.ensureVectorExtension();
|
|
29
|
+
}
|
|
30
|
+
console.log('✅ Supabase Federation Schema Ready');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Ensure required tables exist
|
|
34
|
+
*/
|
|
35
|
+
async ensureTables() {
|
|
36
|
+
// Note: In production, use Supabase migrations
|
|
37
|
+
// This is a runtime check for development
|
|
38
|
+
const { data, error } = await this.client
|
|
39
|
+
.from('agent_sessions')
|
|
40
|
+
.select('id')
|
|
41
|
+
.limit(1);
|
|
42
|
+
if (error && error.code === 'PGRST116') {
|
|
43
|
+
console.log('⚠️ Tables not found. Please run Supabase migrations.');
|
|
44
|
+
console.log('📖 See: docs/supabase/migrations/');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Ensure pgvector extension is enabled
|
|
49
|
+
*/
|
|
50
|
+
async ensureVectorExtension() {
|
|
51
|
+
try {
|
|
52
|
+
// This requires service role key with proper permissions
|
|
53
|
+
const { error } = await this.client.rpc('exec_sql', {
|
|
54
|
+
sql: 'CREATE EXTENSION IF NOT EXISTS vector;'
|
|
55
|
+
});
|
|
56
|
+
if (error) {
|
|
57
|
+
console.warn('⚠️ pgvector extension check failed:', error.message);
|
|
58
|
+
console.log('📖 Enable manually: CREATE EXTENSION vector;');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
console.warn('⚠️ Could not verify pgvector extension');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Store agent memory in Supabase
|
|
67
|
+
*/
|
|
68
|
+
async storeMemory(memory) {
|
|
69
|
+
const { error } = await this.client
|
|
70
|
+
.from('agent_memories')
|
|
71
|
+
.insert({
|
|
72
|
+
id: memory.id,
|
|
73
|
+
tenant_id: memory.tenant_id,
|
|
74
|
+
agent_id: memory.agent_id,
|
|
75
|
+
session_id: memory.session_id,
|
|
76
|
+
content: memory.content,
|
|
77
|
+
embedding: memory.embedding,
|
|
78
|
+
metadata: memory.metadata,
|
|
79
|
+
created_at: memory.created_at || new Date().toISOString(),
|
|
80
|
+
expires_at: memory.expires_at,
|
|
81
|
+
});
|
|
82
|
+
if (error) {
|
|
83
|
+
throw new Error(`Failed to store memory: ${error.message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Query memories by tenant and agent
|
|
88
|
+
*/
|
|
89
|
+
async queryMemories(tenantId, agentId, limit = 100) {
|
|
90
|
+
let query = this.client
|
|
91
|
+
.from('agent_memories')
|
|
92
|
+
.select('*')
|
|
93
|
+
.eq('tenant_id', tenantId)
|
|
94
|
+
.order('created_at', { ascending: false })
|
|
95
|
+
.limit(limit);
|
|
96
|
+
if (agentId) {
|
|
97
|
+
query = query.eq('agent_id', agentId);
|
|
98
|
+
}
|
|
99
|
+
const { data, error } = await query;
|
|
100
|
+
if (error) {
|
|
101
|
+
throw new Error(`Failed to query memories: ${error.message}`);
|
|
102
|
+
}
|
|
103
|
+
return data;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Semantic search using pgvector
|
|
107
|
+
*/
|
|
108
|
+
async semanticSearch(embedding, tenantId, limit = 10) {
|
|
109
|
+
if (this.config.vectorBackend !== 'pgvector') {
|
|
110
|
+
throw new Error('pgvector backend not enabled');
|
|
111
|
+
}
|
|
112
|
+
// Use pgvector cosine similarity search
|
|
113
|
+
const { data, error } = await this.client.rpc('search_memories', {
|
|
114
|
+
query_embedding: embedding,
|
|
115
|
+
query_tenant_id: tenantId,
|
|
116
|
+
match_count: limit,
|
|
117
|
+
});
|
|
118
|
+
if (error) {
|
|
119
|
+
throw new Error(`Semantic search failed: ${error.message}`);
|
|
120
|
+
}
|
|
121
|
+
return data;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Register agent session
|
|
125
|
+
*/
|
|
126
|
+
async registerSession(sessionId, tenantId, agentId, metadata) {
|
|
127
|
+
const { error } = await this.client
|
|
128
|
+
.from('agent_sessions')
|
|
129
|
+
.insert({
|
|
130
|
+
session_id: sessionId,
|
|
131
|
+
tenant_id: tenantId,
|
|
132
|
+
agent_id: agentId,
|
|
133
|
+
metadata,
|
|
134
|
+
started_at: new Date().toISOString(),
|
|
135
|
+
status: 'active',
|
|
136
|
+
});
|
|
137
|
+
if (error) {
|
|
138
|
+
throw new Error(`Failed to register session: ${error.message}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Update session status
|
|
143
|
+
*/
|
|
144
|
+
async updateSessionStatus(sessionId, status) {
|
|
145
|
+
const updates = { status };
|
|
146
|
+
if (status !== 'active') {
|
|
147
|
+
updates.ended_at = new Date().toISOString();
|
|
148
|
+
}
|
|
149
|
+
const { error } = await this.client
|
|
150
|
+
.from('agent_sessions')
|
|
151
|
+
.update(updates)
|
|
152
|
+
.eq('session_id', sessionId);
|
|
153
|
+
if (error) {
|
|
154
|
+
throw new Error(`Failed to update session: ${error.message}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get active sessions for tenant
|
|
159
|
+
*/
|
|
160
|
+
async getActiveSessions(tenantId) {
|
|
161
|
+
const { data, error } = await this.client
|
|
162
|
+
.from('agent_sessions')
|
|
163
|
+
.select('*')
|
|
164
|
+
.eq('tenant_id', tenantId)
|
|
165
|
+
.eq('status', 'active')
|
|
166
|
+
.order('started_at', { ascending: false });
|
|
167
|
+
if (error) {
|
|
168
|
+
throw new Error(`Failed to get sessions: ${error.message}`);
|
|
169
|
+
}
|
|
170
|
+
return data || [];
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Subscribe to real-time memory updates
|
|
174
|
+
*/
|
|
175
|
+
subscribeToMemories(tenantId, callback) {
|
|
176
|
+
const subscription = this.client
|
|
177
|
+
.channel(`memories:${tenantId}`)
|
|
178
|
+
.on('postgres_changes', {
|
|
179
|
+
event: 'INSERT',
|
|
180
|
+
schema: 'public',
|
|
181
|
+
table: 'agent_memories',
|
|
182
|
+
filter: `tenant_id=eq.${tenantId}`,
|
|
183
|
+
}, callback)
|
|
184
|
+
.subscribe();
|
|
185
|
+
// Return unsubscribe function
|
|
186
|
+
return () => {
|
|
187
|
+
subscription.unsubscribe();
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Clean up expired memories
|
|
192
|
+
*/
|
|
193
|
+
async cleanupExpiredMemories() {
|
|
194
|
+
const { data, error } = await this.client
|
|
195
|
+
.from('agent_memories')
|
|
196
|
+
.delete()
|
|
197
|
+
.lt('expires_at', new Date().toISOString())
|
|
198
|
+
.select('id');
|
|
199
|
+
if (error) {
|
|
200
|
+
throw new Error(`Cleanup failed: ${error.message}`);
|
|
201
|
+
}
|
|
202
|
+
return data?.length || 0;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Get hub statistics
|
|
206
|
+
*/
|
|
207
|
+
async getStats(tenantId) {
|
|
208
|
+
// Total memories
|
|
209
|
+
let memoriesQuery = this.client
|
|
210
|
+
.from('agent_memories')
|
|
211
|
+
.select('id', { count: 'exact', head: true });
|
|
212
|
+
if (tenantId) {
|
|
213
|
+
memoriesQuery = memoriesQuery.eq('tenant_id', tenantId);
|
|
214
|
+
}
|
|
215
|
+
const { count: totalMemories } = await memoriesQuery;
|
|
216
|
+
// Active sessions
|
|
217
|
+
let sessionsQuery = this.client
|
|
218
|
+
.from('agent_sessions')
|
|
219
|
+
.select('session_id', { count: 'exact', head: true })
|
|
220
|
+
.eq('status', 'active');
|
|
221
|
+
if (tenantId) {
|
|
222
|
+
sessionsQuery = sessionsQuery.eq('tenant_id', tenantId);
|
|
223
|
+
}
|
|
224
|
+
const { count: activeSessions } = await sessionsQuery;
|
|
225
|
+
return {
|
|
226
|
+
total_memories: totalMemories || 0,
|
|
227
|
+
active_sessions: activeSessions || 0,
|
|
228
|
+
backend: 'supabase',
|
|
229
|
+
vector_backend: this.config.vectorBackend,
|
|
230
|
+
timestamp: new Date().toISOString(),
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Close connection
|
|
235
|
+
*/
|
|
236
|
+
async close() {
|
|
237
|
+
// Supabase client doesn't need explicit closing
|
|
238
|
+
console.log('✅ Supabase connection closed');
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Create Supabase adapter from environment variables
|
|
243
|
+
*/
|
|
244
|
+
export function createSupabaseAdapter() {
|
|
245
|
+
const url = process.env.SUPABASE_URL;
|
|
246
|
+
const anonKey = process.env.SUPABASE_ANON_KEY;
|
|
247
|
+
const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
|
|
248
|
+
if (!url || !anonKey) {
|
|
249
|
+
throw new Error('Missing Supabase credentials. Set SUPABASE_URL and SUPABASE_ANON_KEY');
|
|
250
|
+
}
|
|
251
|
+
return new SupabaseFederationAdapter({
|
|
252
|
+
url,
|
|
253
|
+
anonKey,
|
|
254
|
+
serviceRoleKey,
|
|
255
|
+
vectorBackend: process.env.FEDERATION_VECTOR_BACKEND || 'hybrid',
|
|
256
|
+
syncInterval: parseInt(process.env.FEDERATION_SYNC_INTERVAL || '60000'),
|
|
257
|
+
});
|
|
258
|
+
}
|
package/dist/utils/cli.js
CHANGED
|
@@ -44,6 +44,11 @@ export function parseArgs() {
|
|
|
44
44
|
options.mode = 'agent-manager';
|
|
45
45
|
return options;
|
|
46
46
|
}
|
|
47
|
+
// Check for federation command
|
|
48
|
+
if (args[0] === 'federation') {
|
|
49
|
+
options.mode = 'federation';
|
|
50
|
+
return options;
|
|
51
|
+
}
|
|
47
52
|
// Check for reasoningbank command
|
|
48
53
|
if (args[0] === 'reasoningbank') {
|
|
49
54
|
options.mode = 'reasoningbank';
|