agentnet 0.0.1 → 0.0.3
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/README.md +317 -364
- package/_OLD_README.md +554 -0
- package/assets/network01.png +0 -0
- package/examples/customer-support/README.md +66 -0
- package/examples/customer-support/agents.yaml +457 -0
- package/examples/customer-support/index.js +408 -0
- package/examples/event-planner/README.md +69 -0
- package/examples/event-planner/agents.yaml +318 -0
- package/examples/event-planner/index.js +547 -0
- package/{src/examples → examples/simple}/simple.js +2 -2
- package/{src/examples → examples/smartness}/agents-smartness.yaml +8 -17
- package/{src/examples/def3.js → examples/smartness/index.js} +9 -9
- package/jest.config.js +1 -0
- package/package.json +6 -3
- package/src/agent/agent-loader.js +75 -12
- package/src/agent/agent.js +13 -3
- package/src/agent/runtime.js +9 -6
- package/src/llm/base.js +131 -0
- package/src/llm/gemini.js +137 -117
- package/src/llm/gpt.js +131 -104
- package/src/store/store.js +82 -48
- package/src/tests/agent.test.js +350 -0
- package/src/tools/migrate-version.js +250 -0
- package/src/transport/README.md +123 -0
- package/src/transport/base.js +237 -0
- package/src/transport/index.js +89 -0
- package/src/transport/kafka.js +474 -0
- package/src/transport/nats.js +521 -0
- package/src/transport/rabbitmq.js +722 -0
- package/src/transport/redis.js +532 -0
- package/src/utils/version.js +212 -0
- package/src/agent/runtimes/nats.js +0 -379
- package/src/examples/agents.yaml +0 -394
- package/src/examples/def.js +0 -74
- package/src/examples/def2.js +0 -65
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kafka Transport implementation
|
|
3
|
+
*/
|
|
4
|
+
import { Transport, DiscoveryMessage, safeConnect } from './base.js';
|
|
5
|
+
import { Message } from '../index.js';
|
|
6
|
+
import { logger } from '../utils/logger.js';
|
|
7
|
+
import {
|
|
8
|
+
TransportError,
|
|
9
|
+
DiscoveryError,
|
|
10
|
+
HandoffError,
|
|
11
|
+
TimeoutError,
|
|
12
|
+
withTimeout
|
|
13
|
+
} from '../errors/index.js';
|
|
14
|
+
|
|
15
|
+
// Constants
|
|
16
|
+
const HEARTBEAT_INTERVAL = 1000;
|
|
17
|
+
const TIMEOUT_TASK_REQUEST = 60000;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Kafka implementation of the Transport interface
|
|
21
|
+
*/
|
|
22
|
+
export class KafkaTransport extends Transport {
|
|
23
|
+
constructor() {
|
|
24
|
+
super('Kafka');
|
|
25
|
+
this.producer = null;
|
|
26
|
+
this.consumer = null;
|
|
27
|
+
this.admin = null;
|
|
28
|
+
this.requestMap = new Map(); // For request-response pattern
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Connect to Kafka
|
|
33
|
+
* @param {Object} config - Kafka connection configuration
|
|
34
|
+
* @returns {Promise<any>} - The Kafka connection objects
|
|
35
|
+
*/
|
|
36
|
+
async connect(config) {
|
|
37
|
+
if (this.connected) {
|
|
38
|
+
return { producer: this.producer, consumer: this.consumer };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
// Implementation will depend on the Kafka client library used
|
|
43
|
+
// For example, with KafkaJS:
|
|
44
|
+
// this.producer = kafka.producer();
|
|
45
|
+
// this.consumer = kafka.consumer({ groupId: config.groupId });
|
|
46
|
+
// this.admin = kafka.admin();
|
|
47
|
+
|
|
48
|
+
// await this.producer.connect();
|
|
49
|
+
// await this.consumer.connect();
|
|
50
|
+
// await this.admin.connect();
|
|
51
|
+
|
|
52
|
+
this.connected = true;
|
|
53
|
+
return { producer: this.producer, consumer: this.consumer };
|
|
54
|
+
} catch (error) {
|
|
55
|
+
throw new TransportError(
|
|
56
|
+
`Failed to connect to Kafka: ${error.message}`,
|
|
57
|
+
this.transportType,
|
|
58
|
+
{ details: error.message }
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Disconnect from Kafka
|
|
65
|
+
* @returns {Promise<void>}
|
|
66
|
+
*/
|
|
67
|
+
async disconnect() {
|
|
68
|
+
await super.disconnect();
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
// Disconnect all clients
|
|
72
|
+
if (this.producer) await this.producer.disconnect();
|
|
73
|
+
if (this.consumer) await this.consumer.disconnect();
|
|
74
|
+
if (this.admin) await this.admin.disconnect();
|
|
75
|
+
|
|
76
|
+
this.producer = null;
|
|
77
|
+
this.consumer = null;
|
|
78
|
+
this.admin = null;
|
|
79
|
+
} catch (error) {
|
|
80
|
+
logger.warn('Error disconnecting from Kafka', { error });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Publish a message to a Kafka topic
|
|
86
|
+
* @param {string} topic - The topic to publish to
|
|
87
|
+
* @param {string} message - The message to publish
|
|
88
|
+
* @returns {Promise<void>}
|
|
89
|
+
*/
|
|
90
|
+
async publish(topic, message) {
|
|
91
|
+
if (!this.connected || !this.producer) {
|
|
92
|
+
throw new TransportError(
|
|
93
|
+
'Cannot publish: not connected to Kafka',
|
|
94
|
+
this.transportType
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
// Example with KafkaJS:
|
|
100
|
+
// await this.producer.send({
|
|
101
|
+
// topic,
|
|
102
|
+
// messages: [{ value: message }]
|
|
103
|
+
// });
|
|
104
|
+
} catch (error) {
|
|
105
|
+
throw new TransportError(
|
|
106
|
+
`Failed to publish to topic ${topic}: ${error.message}`,
|
|
107
|
+
this.transportType,
|
|
108
|
+
{ topic }
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Subscribe to a Kafka topic
|
|
115
|
+
* @param {string} topic - The topic to subscribe to
|
|
116
|
+
* @param {Object} options - Subscription options
|
|
117
|
+
* @returns {Promise<any>} - The subscription identifier
|
|
118
|
+
*/
|
|
119
|
+
async subscribe(topic, options = {}) {
|
|
120
|
+
if (!this.connected || !this.consumer) {
|
|
121
|
+
throw new TransportError(
|
|
122
|
+
'Cannot subscribe: not connected to Kafka',
|
|
123
|
+
this.transportType
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
// Example with KafkaJS:
|
|
129
|
+
// await this.consumer.subscribe({ topic, fromBeginning: false });
|
|
130
|
+
// Create a unique subscription ID
|
|
131
|
+
const subscriptionId = `sub_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
132
|
+
return subscriptionId;
|
|
133
|
+
} catch (error) {
|
|
134
|
+
throw new TransportError(
|
|
135
|
+
`Failed to subscribe to topic ${topic}: ${error.message}`,
|
|
136
|
+
this.transportType,
|
|
137
|
+
{ topic }
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Start consuming messages
|
|
144
|
+
* @param {Function} messageHandler - Function to handle incoming messages
|
|
145
|
+
* @returns {Promise<void>}
|
|
146
|
+
*/
|
|
147
|
+
async startConsumer(messageHandler) {
|
|
148
|
+
if (!this.connected || !this.consumer) {
|
|
149
|
+
throw new TransportError(
|
|
150
|
+
'Cannot start consumer: not connected to Kafka',
|
|
151
|
+
this.transportType
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
// Example with KafkaJS:
|
|
157
|
+
// await this.consumer.run({
|
|
158
|
+
// eachMessage: async ({ topic, partition, message }) => {
|
|
159
|
+
// messageHandler(topic, message.value.toString());
|
|
160
|
+
// },
|
|
161
|
+
// });
|
|
162
|
+
} catch (error) {
|
|
163
|
+
throw new TransportError(
|
|
164
|
+
`Failed to start Kafka consumer: ${error.message}`,
|
|
165
|
+
this.transportType
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Send a request and wait for a response (implement request-response pattern on Kafka)
|
|
172
|
+
* @param {string} target - The target topic
|
|
173
|
+
* @param {string} message - The message to send
|
|
174
|
+
* @param {Object} options - Request options
|
|
175
|
+
* @returns {Promise<any>} - The response
|
|
176
|
+
*/
|
|
177
|
+
async request(target, message, options = {}) {
|
|
178
|
+
if (!this.connected || !this.producer) {
|
|
179
|
+
throw new TransportError(
|
|
180
|
+
'Cannot send request: not connected to Kafka',
|
|
181
|
+
this.transportType
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const correlationId = `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
186
|
+
const replyTo = `${target}_replies`;
|
|
187
|
+
const timeout = options.timeout || TIMEOUT_TASK_REQUEST;
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
// Create a promise that will be resolved when the response is received
|
|
191
|
+
const responsePromise = new Promise((resolve, reject) => {
|
|
192
|
+
// Set a timeout
|
|
193
|
+
const timeoutId = setTimeout(() => {
|
|
194
|
+
this.requestMap.delete(correlationId);
|
|
195
|
+
reject(new TimeoutError(`Request to ${target} timed out after ${timeout}ms`));
|
|
196
|
+
}, timeout);
|
|
197
|
+
|
|
198
|
+
// Store the resolver and timeout
|
|
199
|
+
this.requestMap.set(correlationId, {
|
|
200
|
+
resolve,
|
|
201
|
+
reject,
|
|
202
|
+
timeoutId
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Make sure we're subscribed to the reply topic
|
|
207
|
+
// const replySubscription = await this.subscribe(replyTo);
|
|
208
|
+
|
|
209
|
+
// Send the request with correlation ID and reply topic
|
|
210
|
+
// await this.producer.send({
|
|
211
|
+
// topic: target,
|
|
212
|
+
// messages: [{
|
|
213
|
+
// value: message,
|
|
214
|
+
// headers: {
|
|
215
|
+
// 'correlation-id': correlationId,
|
|
216
|
+
// 'reply-to': replyTo
|
|
217
|
+
// }
|
|
218
|
+
// }]
|
|
219
|
+
// });
|
|
220
|
+
|
|
221
|
+
// Wait for the response
|
|
222
|
+
return await responsePromise;
|
|
223
|
+
} catch (error) {
|
|
224
|
+
// Clean up
|
|
225
|
+
const requestData = this.requestMap.get(correlationId);
|
|
226
|
+
if (requestData) {
|
|
227
|
+
clearTimeout(requestData.timeoutId);
|
|
228
|
+
this.requestMap.delete(correlationId);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
throw new TransportError(
|
|
232
|
+
`Request to ${target} failed: ${error.message}`,
|
|
233
|
+
this.transportType,
|
|
234
|
+
{ target }
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Process incoming response messages
|
|
241
|
+
* @param {string} topic - The topic the message was received on
|
|
242
|
+
* @param {Object} message - The message object
|
|
243
|
+
*/
|
|
244
|
+
processResponseMessage(topic, message) {
|
|
245
|
+
try {
|
|
246
|
+
const headers = message.headers || {};
|
|
247
|
+
const correlationId = headers['correlation-id'];
|
|
248
|
+
|
|
249
|
+
if (correlationId && this.requestMap.has(correlationId)) {
|
|
250
|
+
const { resolve, timeoutId } = this.requestMap.get(correlationId);
|
|
251
|
+
clearTimeout(timeoutId);
|
|
252
|
+
this.requestMap.delete(correlationId);
|
|
253
|
+
resolve({
|
|
254
|
+
string: () => message.value.toString()
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
} catch (error) {
|
|
258
|
+
logger.error('Error processing response message', { error, topic });
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Set up discovery subscription
|
|
264
|
+
* This is a template implementation that would need to be adapted for Kafka's message model
|
|
265
|
+
*/
|
|
266
|
+
async setupDiscoverySubscription(discoveryTopic, namespace, agentName, discoveredAgents, acceptedNetworks) {
|
|
267
|
+
try {
|
|
268
|
+
// Subscribe to discovery topic
|
|
269
|
+
await this.subscribe(discoveryTopic);
|
|
270
|
+
logger.info(`Agent ${agentName} subscribed to discovery topic ${discoveryTopic}`);
|
|
271
|
+
|
|
272
|
+
// Set up message handler for discovery messages
|
|
273
|
+
const messageHandler = async (topic, message) => {
|
|
274
|
+
if (topic !== discoveryTopic) return;
|
|
275
|
+
|
|
276
|
+
try {
|
|
277
|
+
// Parse and validate the discovery message
|
|
278
|
+
let discoveryMessage;
|
|
279
|
+
try {
|
|
280
|
+
discoveryMessage = DiscoveryMessage.fromString(message);
|
|
281
|
+
} catch (parseError) {
|
|
282
|
+
logger.warn('Invalid discovery message format', { error: parseError.message });
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Process discovery message...
|
|
287
|
+
// The implementation would be similar to the NATS version but adapted for Kafka's async model
|
|
288
|
+
} catch (error) {
|
|
289
|
+
logger.error('Error processing discovery message', { error });
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
// Start listening for messages
|
|
294
|
+
// You would need to integrate this with your Kafka consumer implementation
|
|
295
|
+
} catch (error) {
|
|
296
|
+
throw new DiscoveryError(
|
|
297
|
+
`Failed to set up discovery subscription on topic ${discoveryTopic}`,
|
|
298
|
+
{ agentName, topic: discoveryTopic },
|
|
299
|
+
error
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Set up task handler for processing incoming requests
|
|
306
|
+
* @param {string} agentName - The agent name (used as the topic)
|
|
307
|
+
* @param {Function} processingFunction - The function to process requests
|
|
308
|
+
* @returns {Promise<void>}
|
|
309
|
+
*/
|
|
310
|
+
async setupTaskHandler(agentName, processingFunction) {
|
|
311
|
+
try {
|
|
312
|
+
// Subscribe to agent's topic
|
|
313
|
+
await this.subscribe(agentName);
|
|
314
|
+
logger.info(`Agent ${agentName} subscribed for task handling`);
|
|
315
|
+
|
|
316
|
+
// Set up message handler for task requests
|
|
317
|
+
const messageHandler = async (topic, message, headers) => {
|
|
318
|
+
if (topic !== agentName) return;
|
|
319
|
+
|
|
320
|
+
try {
|
|
321
|
+
// Parse and validate the payload
|
|
322
|
+
const payload = JSON.parse(message);
|
|
323
|
+
if (!payload || typeof payload !== 'object') {
|
|
324
|
+
throw new Error('Invalid payload: not a JSON object');
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const replyTo = headers['reply-to'];
|
|
328
|
+
const correlationId = headers['correlation-id'];
|
|
329
|
+
|
|
330
|
+
// Process the message using the Message class
|
|
331
|
+
const msg = new Message(payload);
|
|
332
|
+
|
|
333
|
+
// Process the task with timeout
|
|
334
|
+
const response = await withTimeout(
|
|
335
|
+
async () => processingFunction(msg),
|
|
336
|
+
TIMEOUT_TASK_REQUEST * 2,
|
|
337
|
+
`task processing for ${agentName}`
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
// Send response back if reply information is available
|
|
341
|
+
if (replyTo && correlationId) {
|
|
342
|
+
await this.publish(replyTo, response.serialize(), {
|
|
343
|
+
headers: { 'correlation-id': correlationId }
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
} catch (error) {
|
|
347
|
+
logger.error("Error processing task", { error, agentName });
|
|
348
|
+
|
|
349
|
+
// Send error response back if reply information is available
|
|
350
|
+
if (replyTo && correlationId) {
|
|
351
|
+
await this.publish(replyTo, JSON.stringify({
|
|
352
|
+
error: true,
|
|
353
|
+
message: error.message,
|
|
354
|
+
type: error.name || 'Error'
|
|
355
|
+
}), {
|
|
356
|
+
headers: { 'correlation-id': correlationId }
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
// Start listening for messages
|
|
363
|
+
// You would need to integrate this with your Kafka consumer implementation
|
|
364
|
+
} catch (error) {
|
|
365
|
+
throw new TransportError(
|
|
366
|
+
`Failed to set up task handler for ${agentName}: ${error.message}`,
|
|
367
|
+
this.transportType,
|
|
368
|
+
{ agentName }
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Creates a runtime for agent communication using Kafka
|
|
375
|
+
* @param {string} namespace - The agent namespace
|
|
376
|
+
* @param {string} agentName - The agent name
|
|
377
|
+
* @param {Array} discoverySchemas - The agent capability schemas for discovery
|
|
378
|
+
* @param {Object} config - Additional configuration
|
|
379
|
+
* @returns {Promise<Object>} - The runtime { handleTask, discoveredAgents }
|
|
380
|
+
*/
|
|
381
|
+
async createRuntime(namespace, agentName, discoverySchemas, config) {
|
|
382
|
+
const discoveredAgents = {};
|
|
383
|
+
|
|
384
|
+
try {
|
|
385
|
+
// Verify configuration
|
|
386
|
+
if (!config || !config.bindings || !config.bindings.discoveryTopic) {
|
|
387
|
+
throw new TransportError(
|
|
388
|
+
'Missing required Kafka configuration: discoveryTopic',
|
|
389
|
+
this.transportType,
|
|
390
|
+
{ agentName }
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const discoveryTopic = config.bindings.discoveryTopic;
|
|
395
|
+
const acceptedNetworks = config.bindings.acceptedNetworks || [];
|
|
396
|
+
logger.info(`Agent ${agentName} initialized with discovery topic ${discoveryTopic}`);
|
|
397
|
+
|
|
398
|
+
// Step 1: Subscribe to discovery topic
|
|
399
|
+
await this.setupDiscoverySubscription(discoveryTopic, namespace, agentName, discoveredAgents, acceptedNetworks);
|
|
400
|
+
|
|
401
|
+
// Step 2: Set up heartbeat
|
|
402
|
+
this.setupHeartbeat(discoveryTopic, namespace, agentName, discoverySchemas, HEARTBEAT_INTERVAL);
|
|
403
|
+
|
|
404
|
+
// Step 3: Create task handler function
|
|
405
|
+
const handleTask = async (fn) => {
|
|
406
|
+
if (typeof fn !== 'function') {
|
|
407
|
+
throw new Error('Task handler must be a function');
|
|
408
|
+
}
|
|
409
|
+
await this.setupTaskHandler(agentName, fn);
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
return { handleTask, discoveredAgents };
|
|
413
|
+
} catch (error) {
|
|
414
|
+
// Enhance the error with context if it's not already a TransportError
|
|
415
|
+
if (!(error instanceof TransportError)) {
|
|
416
|
+
error = new TransportError(
|
|
417
|
+
`Failed to initialize Kafka runtime: ${error.message}`,
|
|
418
|
+
this.transportType,
|
|
419
|
+
{ agentName }
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
logger.error("Kafka runtime initialization failed", { error, agentName });
|
|
424
|
+
throw error;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Factory function to create a Kafka transport instance
|
|
431
|
+
* @returns {KafkaTransport} - A Kafka transport instance
|
|
432
|
+
*/
|
|
433
|
+
export function createKafkaTransport() {
|
|
434
|
+
return new KafkaTransport();
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Adapter function to create a Kafka-based runtime for agent communication
|
|
439
|
+
* @param {string} namespace - The agent namespace
|
|
440
|
+
* @param {string} agentName - The agent name
|
|
441
|
+
* @param {Array} ioInterfaces - The IO interfaces (only the first one is used)
|
|
442
|
+
* @param {Array} discoverySchemas - The agent capability schemas for discovery
|
|
443
|
+
* @returns {Promise<Object>} - The runtime { handleTask, discoveredAgents }
|
|
444
|
+
*/
|
|
445
|
+
export async function KafkaIOAgentRuntime(namespace, agentName, ioInterfaces, discoverySchemas) {
|
|
446
|
+
if (ioInterfaces.length > 1) {
|
|
447
|
+
throw new TransportError(
|
|
448
|
+
'Only one IO Kafka interface is supported',
|
|
449
|
+
'Kafka',
|
|
450
|
+
{ agentName, interfacesCount: ioInterfaces.length }
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (ioInterfaces.length === 0) {
|
|
455
|
+
logger.warn(`No Kafka interfaces provided for agent ${agentName}, creating passive runtime`);
|
|
456
|
+
return { handleTask: async () => {}, discoveredAgents: {} };
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const io = ioInterfaces[0];
|
|
460
|
+
const transport = createKafkaTransport();
|
|
461
|
+
|
|
462
|
+
try {
|
|
463
|
+
// Connect to Kafka with retry logic
|
|
464
|
+
logger.info(`Connecting to Kafka for agent ${agentName}`);
|
|
465
|
+
await safeConnect(transport, io.config);
|
|
466
|
+
|
|
467
|
+
// Create runtime with the transport
|
|
468
|
+
return await transport.createRuntime(namespace, agentName, discoverySchemas, io.config);
|
|
469
|
+
} catch (error) {
|
|
470
|
+
// Make sure to clean up if initialization fails
|
|
471
|
+
await transport.disconnect();
|
|
472
|
+
throw error;
|
|
473
|
+
}
|
|
474
|
+
}
|