@moltium/core 0.1.18 → 0.1.20
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 +231 -0
- package/dist/index.d.cts +162 -3
- package/dist/index.d.ts +162 -3
- package/dist/index.js +964 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +959 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -2
package/dist/index.mjs
CHANGED
|
@@ -216,12 +216,221 @@ var scheduleTaskAction = {
|
|
|
216
216
|
}
|
|
217
217
|
};
|
|
218
218
|
|
|
219
|
+
// src/actions/built-in/moltbook.ts
|
|
220
|
+
function getMoltbook(context) {
|
|
221
|
+
return context.agent?.socialAdapters?.["moltbook"] || null;
|
|
222
|
+
}
|
|
223
|
+
var checkFeedAction = {
|
|
224
|
+
name: "check_feed",
|
|
225
|
+
description: "Browse the moltbook feed (personalized or global) to find posts to engage with",
|
|
226
|
+
async execute(context) {
|
|
227
|
+
const mb = getMoltbook(context);
|
|
228
|
+
if (!mb) return { success: false, error: "Moltbook not connected" };
|
|
229
|
+
const { sort, limit, global: useGlobal } = context.parameters;
|
|
230
|
+
try {
|
|
231
|
+
const posts = useGlobal ? await mb.getGlobalFeed({ sort: sort || "hot", limit: limit || 15 }) : await mb.getFeed({ sort: sort || "new", limit: limit || 15 });
|
|
232
|
+
return { success: true, data: { posts, count: posts.length } };
|
|
233
|
+
} catch (error) {
|
|
234
|
+
return { success: false, error: error.message };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
var checkDMsAction = {
|
|
239
|
+
name: "check_dms",
|
|
240
|
+
description: "Check for new DM requests and unread messages from other agents",
|
|
241
|
+
async execute(context) {
|
|
242
|
+
const mb = getMoltbook(context);
|
|
243
|
+
if (!mb) return { success: false, error: "Moltbook not connected" };
|
|
244
|
+
try {
|
|
245
|
+
const result = await mb.checkDMs();
|
|
246
|
+
return { success: true, data: result };
|
|
247
|
+
} catch (error) {
|
|
248
|
+
return { success: false, error: error.message };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
var searchMoltbookAction = {
|
|
253
|
+
name: "search_moltbook",
|
|
254
|
+
description: 'Search moltbook for posts and comments by meaning (semantic search). Params: { "query": "..." }',
|
|
255
|
+
async execute(context) {
|
|
256
|
+
const mb = getMoltbook(context);
|
|
257
|
+
if (!mb) return { success: false, error: "Moltbook not connected" };
|
|
258
|
+
const { query, limit } = context.parameters;
|
|
259
|
+
if (!query) return { success: false, error: "Missing required parameter: query" };
|
|
260
|
+
try {
|
|
261
|
+
const results = await mb.search(query, { limit: limit || 10 });
|
|
262
|
+
return { success: true, data: { results, count: results.length } };
|
|
263
|
+
} catch (error) {
|
|
264
|
+
return { success: false, error: error.message };
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
var commentOnPostAction = {
|
|
269
|
+
name: "comment_on_post",
|
|
270
|
+
description: 'Reply to a post with a comment. Params: { "post_id": "...", "content": "..." }',
|
|
271
|
+
async execute(context) {
|
|
272
|
+
const mb = getMoltbook(context);
|
|
273
|
+
if (!mb) return { success: false, error: "Moltbook not connected" };
|
|
274
|
+
const { post_id, content } = context.parameters;
|
|
275
|
+
if (!post_id || !content) return { success: false, error: "Missing required parameters: post_id, content" };
|
|
276
|
+
try {
|
|
277
|
+
const result = await mb.reply(post_id, content);
|
|
278
|
+
return { success: true, data: result };
|
|
279
|
+
} catch (error) {
|
|
280
|
+
return { success: false, error: error.message };
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
var upvotePostAction = {
|
|
285
|
+
name: "upvote_post",
|
|
286
|
+
description: 'Upvote a post you find valuable. Params: { "post_id": "..." }',
|
|
287
|
+
async execute(context) {
|
|
288
|
+
const mb = getMoltbook(context);
|
|
289
|
+
if (!mb) return { success: false, error: "Moltbook not connected" };
|
|
290
|
+
const { post_id } = context.parameters;
|
|
291
|
+
if (!post_id) return { success: false, error: "Missing required parameter: post_id" };
|
|
292
|
+
try {
|
|
293
|
+
await mb.like(post_id);
|
|
294
|
+
return { success: true, data: { post_id, action: "upvoted" } };
|
|
295
|
+
} catch (error) {
|
|
296
|
+
return { success: false, error: error.message };
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
var downvotePostAction = {
|
|
301
|
+
name: "downvote_post",
|
|
302
|
+
description: 'Downvote a post you disagree with. Params: { "post_id": "..." }',
|
|
303
|
+
async execute(context) {
|
|
304
|
+
const mb = getMoltbook(context);
|
|
305
|
+
if (!mb) return { success: false, error: "Moltbook not connected" };
|
|
306
|
+
const { post_id } = context.parameters;
|
|
307
|
+
if (!post_id) return { success: false, error: "Missing required parameter: post_id" };
|
|
308
|
+
try {
|
|
309
|
+
await mb.downvote(post_id);
|
|
310
|
+
return { success: true, data: { post_id, action: "downvoted" } };
|
|
311
|
+
} catch (error) {
|
|
312
|
+
return { success: false, error: error.message };
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
var browseSubmoltsAction = {
|
|
317
|
+
name: "browse_submolts",
|
|
318
|
+
description: "Discover moltbook communities (submolts) to subscribe to",
|
|
319
|
+
async execute(context) {
|
|
320
|
+
const mb = getMoltbook(context);
|
|
321
|
+
if (!mb) return { success: false, error: "Moltbook not connected" };
|
|
322
|
+
try {
|
|
323
|
+
const submolts = await mb.listSubmolts();
|
|
324
|
+
return { success: true, data: { submolts, count: submolts.length } };
|
|
325
|
+
} catch (error) {
|
|
326
|
+
return { success: false, error: error.message };
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
var followAgentAction = {
|
|
331
|
+
name: "follow_agent",
|
|
332
|
+
description: 'Follow another agent whose posts you enjoy. Params: { "agent_name": "..." }',
|
|
333
|
+
async execute(context) {
|
|
334
|
+
const mb = getMoltbook(context);
|
|
335
|
+
if (!mb) return { success: false, error: "Moltbook not connected" };
|
|
336
|
+
const { agent_name } = context.parameters;
|
|
337
|
+
if (!agent_name) return { success: false, error: "Missing required parameter: agent_name" };
|
|
338
|
+
try {
|
|
339
|
+
await mb.follow(agent_name);
|
|
340
|
+
return { success: true, data: { agent_name, action: "followed" } };
|
|
341
|
+
} catch (error) {
|
|
342
|
+
return { success: false, error: error.message };
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
var sendDMRequestAction = {
|
|
347
|
+
name: "send_dm_request",
|
|
348
|
+
description: 'Request to start a private conversation with another agent. Params: { "to": "...", "message": "..." }',
|
|
349
|
+
async execute(context) {
|
|
350
|
+
const mb = getMoltbook(context);
|
|
351
|
+
if (!mb) return { success: false, error: "Moltbook not connected" };
|
|
352
|
+
const { to, to_owner, message } = context.parameters;
|
|
353
|
+
if (!to && !to_owner || !message) return { success: false, error: "Missing required parameters: (to or to_owner), message" };
|
|
354
|
+
try {
|
|
355
|
+
const target = to || to_owner || "";
|
|
356
|
+
const byOwner = !to && !!to_owner;
|
|
357
|
+
const result = await mb.sendDMRequest(target, message, byOwner);
|
|
358
|
+
return { success: true, data: result };
|
|
359
|
+
} catch (error) {
|
|
360
|
+
return { success: false, error: error.message };
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
var sendDMAction = {
|
|
365
|
+
name: "send_dm",
|
|
366
|
+
description: 'Send a message in an existing DM conversation. Params: { "conversation_id": "...", "message": "..." }',
|
|
367
|
+
async execute(context) {
|
|
368
|
+
const mb = getMoltbook(context);
|
|
369
|
+
if (!mb) return { success: false, error: "Moltbook not connected" };
|
|
370
|
+
const { conversation_id, message, needs_human_input } = context.parameters;
|
|
371
|
+
if (!conversation_id || !message) return { success: false, error: "Missing required parameters: conversation_id, message" };
|
|
372
|
+
try {
|
|
373
|
+
const result = await mb.sendDM(conversation_id, message, needs_human_input);
|
|
374
|
+
return { success: true, data: result };
|
|
375
|
+
} catch (error) {
|
|
376
|
+
return { success: false, error: error.message };
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
var approveDMRequestAction = {
|
|
381
|
+
name: "approve_dm_request",
|
|
382
|
+
description: 'Approve a pending DM request from another agent. Params: { "conversation_id": "..." }',
|
|
383
|
+
async execute(context) {
|
|
384
|
+
const mb = getMoltbook(context);
|
|
385
|
+
if (!mb) return { success: false, error: "Moltbook not connected" };
|
|
386
|
+
const { conversation_id } = context.parameters;
|
|
387
|
+
if (!conversation_id) return { success: false, error: "Missing required parameter: conversation_id" };
|
|
388
|
+
try {
|
|
389
|
+
await mb.approveDMRequest(conversation_id);
|
|
390
|
+
return { success: true, data: { conversation_id, action: "approved" } };
|
|
391
|
+
} catch (error) {
|
|
392
|
+
return { success: false, error: error.message };
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
var readConversationAction = {
|
|
397
|
+
name: "read_conversation",
|
|
398
|
+
description: 'Read messages in a DM conversation (marks as read). Params: { "conversation_id": "..." }',
|
|
399
|
+
async execute(context) {
|
|
400
|
+
const mb = getMoltbook(context);
|
|
401
|
+
if (!mb) return { success: false, error: "Moltbook not connected" };
|
|
402
|
+
const { conversation_id } = context.parameters;
|
|
403
|
+
if (!conversation_id) return { success: false, error: "Missing required parameter: conversation_id" };
|
|
404
|
+
try {
|
|
405
|
+
const detail = await mb.readConversation(conversation_id);
|
|
406
|
+
return { success: true, data: detail };
|
|
407
|
+
} catch (error) {
|
|
408
|
+
return { success: false, error: error.message };
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
var moltbookActions = [
|
|
413
|
+
checkFeedAction,
|
|
414
|
+
checkDMsAction,
|
|
415
|
+
searchMoltbookAction,
|
|
416
|
+
commentOnPostAction,
|
|
417
|
+
upvotePostAction,
|
|
418
|
+
downvotePostAction,
|
|
419
|
+
browseSubmoltsAction,
|
|
420
|
+
followAgentAction,
|
|
421
|
+
sendDMRequestAction,
|
|
422
|
+
sendDMAction,
|
|
423
|
+
approveDMRequestAction,
|
|
424
|
+
readConversationAction
|
|
425
|
+
];
|
|
426
|
+
|
|
219
427
|
// src/actions/built-in/index.ts
|
|
220
428
|
var builtInActions = [
|
|
221
429
|
postSocialUpdateAction,
|
|
222
430
|
respondToMentionAction,
|
|
223
431
|
dmUserAction,
|
|
224
|
-
scheduleTaskAction
|
|
432
|
+
scheduleTaskAction,
|
|
433
|
+
...moltbookActions
|
|
225
434
|
];
|
|
226
435
|
|
|
227
436
|
// src/llm/prompt.ts
|
|
@@ -248,18 +457,33 @@ ${config.llm.systemPrompt}`);
|
|
|
248
457
|
}
|
|
249
458
|
function buildDecisionPrompt(context, availableActions) {
|
|
250
459
|
const actionList = availableActions.map((a) => `- ${a.name}: ${a.description}`).join("\n");
|
|
251
|
-
|
|
460
|
+
const recent = context.recentActions || [];
|
|
461
|
+
const recentNames = recent.map((r) => r.action);
|
|
462
|
+
const lastAction = recentNames.length > 0 ? recentNames[recentNames.length - 1] : "none";
|
|
463
|
+
let varietyHint = "";
|
|
464
|
+
if (recentNames.length > 0) {
|
|
465
|
+
varietyHint = `
|
|
466
|
+
Your recent actions (most recent last): ${recentNames.join(" \u2192 ")}
|
|
467
|
+
IMPORTANT: Do NOT repeat "${lastAction}" again. Choose a DIFFERENT action to keep your behavior varied and interesting.
|
|
468
|
+
Vary between: posting content, checking your feed, replying to others, checking DMs, browsing communities, searching for topics, and following interesting agents.`;
|
|
469
|
+
}
|
|
470
|
+
return `You are an autonomous AI agent on moltbook (a social network for AI agents).
|
|
471
|
+
Decide what action to take next based on the current context and your personality.
|
|
252
472
|
|
|
253
473
|
Available actions:
|
|
254
474
|
${actionList}
|
|
255
475
|
|
|
256
476
|
Current context:
|
|
257
477
|
${JSON.stringify(context, null, 2)}
|
|
478
|
+
${varietyHint}
|
|
479
|
+
|
|
480
|
+
Think about what would be most natural and valuable right now.
|
|
481
|
+
If the action generates content (post, comment, DM), include the actual content in parameters.
|
|
258
482
|
|
|
259
483
|
Respond with a JSON object:
|
|
260
484
|
{
|
|
261
485
|
"action": "<action_name>",
|
|
262
|
-
"reasoning": "<brief explanation>",
|
|
486
|
+
"reasoning": "<brief explanation of why you chose this>",
|
|
263
487
|
"parameters": { <any parameters for the action> }
|
|
264
488
|
}`;
|
|
265
489
|
}
|
|
@@ -1094,6 +1318,174 @@ var TwitterAdapter = class extends SocialAdapter {
|
|
|
1094
1318
|
}
|
|
1095
1319
|
};
|
|
1096
1320
|
|
|
1321
|
+
// src/a2a/client.ts
|
|
1322
|
+
import { A2AClient as SDKClient } from "@a2a-js/sdk/client";
|
|
1323
|
+
import { v4 as uuidv4 } from "uuid";
|
|
1324
|
+
var A2AClient = class {
|
|
1325
|
+
sdkClient;
|
|
1326
|
+
config;
|
|
1327
|
+
constructor(config) {
|
|
1328
|
+
this.config = {
|
|
1329
|
+
agentUrl: config.agentUrl,
|
|
1330
|
+
agentCardPath: config.agentCardPath || ".well-known/agent-card.json",
|
|
1331
|
+
timeout: config.timeout || 3e4
|
|
1332
|
+
};
|
|
1333
|
+
const agentUrl = this.config.agentUrl;
|
|
1334
|
+
const agentCardPath = this.config.agentCardPath;
|
|
1335
|
+
this.sdkClient = new SDKClient(agentUrl, agentCardPath);
|
|
1336
|
+
}
|
|
1337
|
+
/**
|
|
1338
|
+
* Send a text message to the agent and get a response
|
|
1339
|
+
*
|
|
1340
|
+
* @param options Message options including text content
|
|
1341
|
+
* @returns Promise resolving to the agent's response
|
|
1342
|
+
*
|
|
1343
|
+
* @example
|
|
1344
|
+
* ```typescript
|
|
1345
|
+
* const response = await client.sendMessage({
|
|
1346
|
+
* text: 'What are your thoughts on AI?',
|
|
1347
|
+
* contextId: 'conversation-123'
|
|
1348
|
+
* });
|
|
1349
|
+
*
|
|
1350
|
+
* if (response.success) {
|
|
1351
|
+
* console.log('Agent replied:', response.reply);
|
|
1352
|
+
* }
|
|
1353
|
+
* ```
|
|
1354
|
+
*/
|
|
1355
|
+
async sendMessage(options) {
|
|
1356
|
+
try {
|
|
1357
|
+
const response = await this.sdkClient.sendMessage({
|
|
1358
|
+
message: {
|
|
1359
|
+
kind: "message",
|
|
1360
|
+
messageId: uuidv4(),
|
|
1361
|
+
role: "user",
|
|
1362
|
+
contextId: options.contextId,
|
|
1363
|
+
metadata: options.metadata,
|
|
1364
|
+
parts: [
|
|
1365
|
+
{
|
|
1366
|
+
kind: "text",
|
|
1367
|
+
text: options.text
|
|
1368
|
+
}
|
|
1369
|
+
]
|
|
1370
|
+
}
|
|
1371
|
+
});
|
|
1372
|
+
const reply = this.extractReply(response);
|
|
1373
|
+
return {
|
|
1374
|
+
success: true,
|
|
1375
|
+
reply,
|
|
1376
|
+
agentUrl: this.config.agentUrl,
|
|
1377
|
+
raw: response
|
|
1378
|
+
};
|
|
1379
|
+
} catch (error) {
|
|
1380
|
+
return {
|
|
1381
|
+
success: false,
|
|
1382
|
+
error: error.message || "Unknown error occurred",
|
|
1383
|
+
agentUrl: this.config.agentUrl
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Extract text reply from A2A protocol response
|
|
1389
|
+
* Handles both Message and Task response types
|
|
1390
|
+
*/
|
|
1391
|
+
extractReply(response) {
|
|
1392
|
+
if (!("result" in response) || !response.result) {
|
|
1393
|
+
return JSON.stringify(response);
|
|
1394
|
+
}
|
|
1395
|
+
const result = response.result;
|
|
1396
|
+
if ("kind" in result && result.kind === "message" && "parts" in result) {
|
|
1397
|
+
const textParts = result.parts.filter((part) => part.kind === "text").map((part) => part.text);
|
|
1398
|
+
return textParts.join("\n");
|
|
1399
|
+
}
|
|
1400
|
+
return typeof result === "string" ? result : JSON.stringify(result);
|
|
1401
|
+
}
|
|
1402
|
+
/**
|
|
1403
|
+
* Get the agent card information
|
|
1404
|
+
*
|
|
1405
|
+
* @returns Promise resolving to the agent card
|
|
1406
|
+
*/
|
|
1407
|
+
async getAgentCard() {
|
|
1408
|
+
return this.sdkClient.getAgentCard();
|
|
1409
|
+
}
|
|
1410
|
+
/**
|
|
1411
|
+
* Get the base URL of the agent
|
|
1412
|
+
*/
|
|
1413
|
+
getAgentUrl() {
|
|
1414
|
+
return this.config.agentUrl;
|
|
1415
|
+
}
|
|
1416
|
+
};
|
|
1417
|
+
|
|
1418
|
+
// src/a2a/actions.ts
|
|
1419
|
+
function createA2ACommunicationAction(config = {}) {
|
|
1420
|
+
const {
|
|
1421
|
+
defaultAgentUrl,
|
|
1422
|
+
actionName = "talk_to_agent",
|
|
1423
|
+
description,
|
|
1424
|
+
verbose = false
|
|
1425
|
+
} = config;
|
|
1426
|
+
const defaultDescription = description || `Send a message to another A2A-compliant agent and receive their response. Parameters: message (required string) - the message to send, agentUrl (optional string) - target agent URL${defaultAgentUrl ? ` (defaults to ${defaultAgentUrl})` : ""}`;
|
|
1427
|
+
return {
|
|
1428
|
+
name: actionName,
|
|
1429
|
+
description: defaultDescription,
|
|
1430
|
+
async execute(context) {
|
|
1431
|
+
const { parameters } = context;
|
|
1432
|
+
const message = parameters.message;
|
|
1433
|
+
const agentUrl = parameters.agentUrl || defaultAgentUrl;
|
|
1434
|
+
if (!message) {
|
|
1435
|
+
return {
|
|
1436
|
+
success: false,
|
|
1437
|
+
error: 'Parameter "message" is required'
|
|
1438
|
+
};
|
|
1439
|
+
}
|
|
1440
|
+
if (!agentUrl) {
|
|
1441
|
+
return {
|
|
1442
|
+
success: false,
|
|
1443
|
+
error: 'Parameter "agentUrl" is required or defaultAgentUrl must be configured'
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1446
|
+
if (verbose) {
|
|
1447
|
+
console.log(`[A2A] Sending message to ${agentUrl}:`, message);
|
|
1448
|
+
}
|
|
1449
|
+
try {
|
|
1450
|
+
const client = new A2AClient({ agentUrl });
|
|
1451
|
+
const response = await client.sendMessage({ text: message });
|
|
1452
|
+
if (verbose) {
|
|
1453
|
+
console.log(`[A2A] Response from ${agentUrl}:`, response.reply);
|
|
1454
|
+
}
|
|
1455
|
+
if (!response.success) {
|
|
1456
|
+
return {
|
|
1457
|
+
success: false,
|
|
1458
|
+
error: response.error || "Failed to communicate with agent"
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1461
|
+
return {
|
|
1462
|
+
success: true,
|
|
1463
|
+
data: {
|
|
1464
|
+
reply: response.reply,
|
|
1465
|
+
agentUrl: response.agentUrl
|
|
1466
|
+
}
|
|
1467
|
+
};
|
|
1468
|
+
} catch (error) {
|
|
1469
|
+
if (verbose) {
|
|
1470
|
+
console.error(`[A2A] Error:`, error);
|
|
1471
|
+
}
|
|
1472
|
+
return {
|
|
1473
|
+
success: false,
|
|
1474
|
+
error: error.message || "Unknown error occurred"
|
|
1475
|
+
};
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
function createMultipleA2AActions(configs) {
|
|
1481
|
+
return Object.entries(configs).map(
|
|
1482
|
+
([name, config]) => createA2ACommunicationAction({
|
|
1483
|
+
...config,
|
|
1484
|
+
actionName: name
|
|
1485
|
+
})
|
|
1486
|
+
);
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1097
1489
|
// src/agent/Agent.ts
|
|
1098
1490
|
var logger4 = createLogger("Agent");
|
|
1099
1491
|
function parsePostFrequency(freq) {
|
|
@@ -1135,6 +1527,8 @@ var Agent = class {
|
|
|
1135
1527
|
tickTimer;
|
|
1136
1528
|
systemPrompt;
|
|
1137
1529
|
socialAdapters = {};
|
|
1530
|
+
recentActions = [];
|
|
1531
|
+
tickCount = 0;
|
|
1138
1532
|
constructor(config, hooks) {
|
|
1139
1533
|
this.config = config;
|
|
1140
1534
|
this.hooks = hooks || {};
|
|
@@ -1269,13 +1663,29 @@ var Agent = class {
|
|
|
1269
1663
|
}
|
|
1270
1664
|
async tick() {
|
|
1271
1665
|
if (this.state !== "running") return;
|
|
1666
|
+
this.tickCount++;
|
|
1272
1667
|
try {
|
|
1273
1668
|
if (this.hooks.onTick) {
|
|
1274
1669
|
await this.hooks.onTick(this);
|
|
1275
1670
|
}
|
|
1276
|
-
if (this.isSleeping())
|
|
1671
|
+
if (this.isSleeping()) {
|
|
1672
|
+
logger4.info(`Tick #${this.tickCount}: Sleeping \u2014 skipped`);
|
|
1673
|
+
return;
|
|
1674
|
+
}
|
|
1675
|
+
logger4.info(`Tick #${this.tickCount}: Thinking...`);
|
|
1277
1676
|
const decision = await this.think();
|
|
1677
|
+
logger4.info(`Tick #${this.tickCount}: Decision \u2192 ${decision.action} | Reasoning: ${decision.reasoning}`);
|
|
1678
|
+
if (decision.parameters && Object.keys(decision.parameters).length > 0) {
|
|
1679
|
+
logger4.info(`Tick #${this.tickCount}: Parameters \u2192 ${JSON.stringify(decision.parameters)}`);
|
|
1680
|
+
}
|
|
1278
1681
|
const result = await this.act(decision.action, decision.parameters);
|
|
1682
|
+
logger4.info(`Tick #${this.tickCount}: Result \u2192 success=${result.success}${result.data ? ` data=${JSON.stringify(result.data).slice(0, 200)}` : ""}`);
|
|
1683
|
+
this.recentActions.push({
|
|
1684
|
+
action: decision.action,
|
|
1685
|
+
reasoning: decision.reasoning,
|
|
1686
|
+
time: (/* @__PURE__ */ new Date()).toISOString()
|
|
1687
|
+
});
|
|
1688
|
+
if (this.recentActions.length > 10) this.recentActions.shift();
|
|
1279
1689
|
await this.learn({
|
|
1280
1690
|
type: "action",
|
|
1281
1691
|
action: decision.action,
|
|
@@ -1287,7 +1697,7 @@ var Agent = class {
|
|
|
1287
1697
|
await this.fireWebhook("onAction", decision, result);
|
|
1288
1698
|
}
|
|
1289
1699
|
} catch (error) {
|
|
1290
|
-
logger4.error(`Tick error: ${error instanceof Error ? error.message : error}`);
|
|
1700
|
+
logger4.error(`Tick #${this.tickCount} error: ${error instanceof Error ? error.message : error}`);
|
|
1291
1701
|
if (this.hooks.onError && error instanceof Error) {
|
|
1292
1702
|
await this.hooks.onError(error, this);
|
|
1293
1703
|
}
|
|
@@ -1320,6 +1730,46 @@ var Agent = class {
|
|
|
1320
1730
|
this.actionRegistry.register(action);
|
|
1321
1731
|
}
|
|
1322
1732
|
}
|
|
1733
|
+
const mb = this.config.social.moltbook;
|
|
1734
|
+
if (mb && mb.enabled !== false) {
|
|
1735
|
+
for (const action of moltbookActions) {
|
|
1736
|
+
if (!this.actionRegistry.has(action.name)) {
|
|
1737
|
+
this.actionRegistry.register(action);
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
logger4.info(`Registered ${moltbookActions.length} Moltbook actions`);
|
|
1741
|
+
}
|
|
1742
|
+
const a2a = this.config.a2a;
|
|
1743
|
+
if (a2a && a2a.enabled !== false) {
|
|
1744
|
+
if (a2a.peers && Object.keys(a2a.peers).length > 0) {
|
|
1745
|
+
const peerActions = createMultipleA2AActions(
|
|
1746
|
+
Object.entries(a2a.peers).reduce((acc, [actionName, url]) => {
|
|
1747
|
+
acc[actionName] = {
|
|
1748
|
+
defaultAgentUrl: url,
|
|
1749
|
+
verbose: a2a.verbose,
|
|
1750
|
+
description: `Communicate with peer agent at ${url}`
|
|
1751
|
+
};
|
|
1752
|
+
return acc;
|
|
1753
|
+
}, {})
|
|
1754
|
+
);
|
|
1755
|
+
for (const action of peerActions) {
|
|
1756
|
+
if (!this.actionRegistry.has(action.name)) {
|
|
1757
|
+
this.actionRegistry.register(action);
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
logger4.info(`Registered ${peerActions.length} A2A peer actions`);
|
|
1761
|
+
} else {
|
|
1762
|
+
const genericAction = createA2ACommunicationAction({
|
|
1763
|
+
defaultAgentUrl: a2a.defaultPeerUrl,
|
|
1764
|
+
actionName: a2a.genericActionName || "talk_to_agent",
|
|
1765
|
+
verbose: a2a.verbose
|
|
1766
|
+
});
|
|
1767
|
+
if (!this.actionRegistry.has(genericAction.name)) {
|
|
1768
|
+
this.actionRegistry.register(genericAction);
|
|
1769
|
+
}
|
|
1770
|
+
logger4.info(`Registered generic A2A action: ${genericAction.name}`);
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1323
1773
|
if (this.config.customActions) {
|
|
1324
1774
|
for (const action of this.config.customActions) {
|
|
1325
1775
|
this.actionRegistry.register(action);
|
|
@@ -1628,7 +2078,11 @@ Decide what action to take. Respond with JSON:
|
|
|
1628
2078
|
agentType: this.config.type,
|
|
1629
2079
|
currentTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1630
2080
|
state: this.state,
|
|
1631
|
-
|
|
2081
|
+
tickNumber: this.tickCount,
|
|
2082
|
+
personality: this.config.personality.traits,
|
|
2083
|
+
connectedPlatforms: Object.keys(this.socialAdapters),
|
|
2084
|
+
recentActions: this.recentActions.slice(-5),
|
|
2085
|
+
scheduledTasks: (this.config.scheduling || []).map((t) => t.name)
|
|
1632
2086
|
};
|
|
1633
2087
|
}
|
|
1634
2088
|
async fireWebhook(event, ...args) {
|
|
@@ -2305,27 +2759,516 @@ function authMiddleware(req, res, next) {
|
|
|
2305
2759
|
next();
|
|
2306
2760
|
}
|
|
2307
2761
|
|
|
2762
|
+
// src/a2a/AgentCardBuilder.ts
|
|
2763
|
+
var logger7 = createLogger("AgentCardBuilder");
|
|
2764
|
+
var AgentCardBuilder = class {
|
|
2765
|
+
/**
|
|
2766
|
+
* Creates an A2A AgentCard from Moltium configuration.
|
|
2767
|
+
* @param config The Moltium agent configuration
|
|
2768
|
+
* @param a2aConfig A2A-specific configuration (URLs, capabilities)
|
|
2769
|
+
* @returns A2A-compliant AgentCard
|
|
2770
|
+
*/
|
|
2771
|
+
static build(config, a2aConfig) {
|
|
2772
|
+
const baseUrl = a2aConfig.baseUrl;
|
|
2773
|
+
const skills = this.buildSkills(config);
|
|
2774
|
+
const capabilities = {
|
|
2775
|
+
streaming: a2aConfig.streaming ?? true,
|
|
2776
|
+
pushNotifications: a2aConfig.pushNotifications ?? false,
|
|
2777
|
+
stateTransitionHistory: a2aConfig.stateTransitionHistory ?? true
|
|
2778
|
+
};
|
|
2779
|
+
const defaultInputModes = ["text"];
|
|
2780
|
+
const defaultOutputModes = ["text"];
|
|
2781
|
+
const agentCard = {
|
|
2782
|
+
name: config.name,
|
|
2783
|
+
description: config.personality.bio || `${config.name} - An autonomous AI agent`,
|
|
2784
|
+
protocolVersion: "0.3.0",
|
|
2785
|
+
version: "0.1.0",
|
|
2786
|
+
url: `${baseUrl}/a2a/jsonrpc`,
|
|
2787
|
+
skills,
|
|
2788
|
+
capabilities,
|
|
2789
|
+
defaultInputModes,
|
|
2790
|
+
defaultOutputModes,
|
|
2791
|
+
additionalInterfaces: [
|
|
2792
|
+
{ url: `${baseUrl}/a2a/jsonrpc`, transport: "JSONRPC" },
|
|
2793
|
+
{ url: `${baseUrl}/a2a/rest`, transport: "HTTP+JSON" }
|
|
2794
|
+
]
|
|
2795
|
+
};
|
|
2796
|
+
logger7.debug("Built A2A AgentCard", {
|
|
2797
|
+
name: agentCard.name,
|
|
2798
|
+
skillCount: skills.length,
|
|
2799
|
+
baseUrl
|
|
2800
|
+
});
|
|
2801
|
+
return agentCard;
|
|
2802
|
+
}
|
|
2803
|
+
/**
|
|
2804
|
+
* Converts Moltium actions to A2A skills.
|
|
2805
|
+
*/
|
|
2806
|
+
static buildSkills(config) {
|
|
2807
|
+
const skills = [];
|
|
2808
|
+
skills.push({
|
|
2809
|
+
id: "chat",
|
|
2810
|
+
name: "Chat",
|
|
2811
|
+
description: `Interact with ${config.name}. ${config.personality.bio || ""}`,
|
|
2812
|
+
tags: ["chat", "conversation"]
|
|
2813
|
+
});
|
|
2814
|
+
const hasPosting = config.actions.some(
|
|
2815
|
+
(a) => a.includes("post") || a.includes("update")
|
|
2816
|
+
);
|
|
2817
|
+
if (hasPosting) {
|
|
2818
|
+
skills.push({
|
|
2819
|
+
id: "social_posting",
|
|
2820
|
+
name: "Social Posting",
|
|
2821
|
+
description: "Post updates and content to social platforms",
|
|
2822
|
+
tags: ["social", "posting"]
|
|
2823
|
+
});
|
|
2824
|
+
}
|
|
2825
|
+
const hasEngagement = config.actions.some(
|
|
2826
|
+
(a) => a.includes("comment") || a.includes("reply") || a.includes("respond")
|
|
2827
|
+
);
|
|
2828
|
+
if (hasEngagement) {
|
|
2829
|
+
skills.push({
|
|
2830
|
+
id: "social_engagement",
|
|
2831
|
+
name: "Social Engagement",
|
|
2832
|
+
description: "Engage with others through comments and replies",
|
|
2833
|
+
tags: ["social", "engagement"]
|
|
2834
|
+
});
|
|
2835
|
+
}
|
|
2836
|
+
const hasSearch = config.actions.some(
|
|
2837
|
+
(a) => a.includes("search") || a.includes("check_feed") || a.includes("browse")
|
|
2838
|
+
);
|
|
2839
|
+
if (hasSearch) {
|
|
2840
|
+
skills.push({
|
|
2841
|
+
id: "content_discovery",
|
|
2842
|
+
name: "Content Discovery",
|
|
2843
|
+
description: "Search and discover relevant content",
|
|
2844
|
+
tags: ["search", "discovery"]
|
|
2845
|
+
});
|
|
2846
|
+
}
|
|
2847
|
+
const hasAnalysis = config.actions.some(
|
|
2848
|
+
(a) => a.includes("analyze") || a.includes("think") || a.includes("research")
|
|
2849
|
+
);
|
|
2850
|
+
if (hasAnalysis) {
|
|
2851
|
+
skills.push({
|
|
2852
|
+
id: "analysis",
|
|
2853
|
+
name: "Analysis & Research",
|
|
2854
|
+
description: "Analyze problems and conduct research",
|
|
2855
|
+
tags: ["analysis", "research", "thinking"]
|
|
2856
|
+
});
|
|
2857
|
+
}
|
|
2858
|
+
return skills;
|
|
2859
|
+
}
|
|
2860
|
+
/**
|
|
2861
|
+
* Updates an agent card with runtime information.
|
|
2862
|
+
*/
|
|
2863
|
+
static updateWithRuntime(agentCard, actualPort, actualHost = "localhost") {
|
|
2864
|
+
const protocol = actualHost === "localhost" || actualHost === "127.0.0.1" ? "http" : "https";
|
|
2865
|
+
const baseUrl = `${protocol}://${actualHost}:${actualPort}`;
|
|
2866
|
+
return {
|
|
2867
|
+
...agentCard,
|
|
2868
|
+
url: `${baseUrl}/a2a/jsonrpc`,
|
|
2869
|
+
additionalInterfaces: [
|
|
2870
|
+
{ url: `${baseUrl}/a2a/jsonrpc`, transport: "JSONRPC" },
|
|
2871
|
+
{ url: `${baseUrl}/a2a/rest`, transport: "HTTP+JSON" }
|
|
2872
|
+
]
|
|
2873
|
+
};
|
|
2874
|
+
}
|
|
2875
|
+
};
|
|
2876
|
+
|
|
2877
|
+
// src/a2a/MoltiumExecutor.ts
|
|
2878
|
+
import { v4 as uuidv42 } from "uuid";
|
|
2879
|
+
var logger8 = createLogger("MoltiumExecutor");
|
|
2880
|
+
var MoltiumExecutor = class {
|
|
2881
|
+
agent;
|
|
2882
|
+
cancelledTasks = /* @__PURE__ */ new Set();
|
|
2883
|
+
constructor(agent) {
|
|
2884
|
+
this.agent = agent;
|
|
2885
|
+
}
|
|
2886
|
+
/**
|
|
2887
|
+
* Executes an A2A request using the Moltium agent.
|
|
2888
|
+
* Converts A2A messages to Moltium actions and publishes results back via the event bus.
|
|
2889
|
+
*/
|
|
2890
|
+
async execute(requestContext, eventBus) {
|
|
2891
|
+
const { taskId, contextId, userMessage, task } = requestContext;
|
|
2892
|
+
try {
|
|
2893
|
+
logger8.info(`[A2A] Executing request for task ${taskId}`);
|
|
2894
|
+
if (!task) {
|
|
2895
|
+
const initialTask = {
|
|
2896
|
+
kind: "task",
|
|
2897
|
+
id: taskId,
|
|
2898
|
+
contextId,
|
|
2899
|
+
status: {
|
|
2900
|
+
state: "submitted",
|
|
2901
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2902
|
+
},
|
|
2903
|
+
history: [userMessage]
|
|
2904
|
+
};
|
|
2905
|
+
eventBus.publish(initialTask);
|
|
2906
|
+
}
|
|
2907
|
+
if (this.cancelledTasks.has(taskId)) {
|
|
2908
|
+
this.publishCancellation(taskId, contextId, eventBus);
|
|
2909
|
+
return;
|
|
2910
|
+
}
|
|
2911
|
+
this.publishStatusUpdate(taskId, contextId, "working", eventBus, false);
|
|
2912
|
+
const userContent = this.extractMessageContent(userMessage);
|
|
2913
|
+
const isActionRequest = this.isActionRequest(userContent);
|
|
2914
|
+
let result;
|
|
2915
|
+
if (isActionRequest) {
|
|
2916
|
+
result = await this.executeAction(userContent, taskId);
|
|
2917
|
+
} else {
|
|
2918
|
+
result = await this.executeChat(userContent, taskId);
|
|
2919
|
+
}
|
|
2920
|
+
if (this.cancelledTasks.has(taskId)) {
|
|
2921
|
+
this.publishCancellation(taskId, contextId, eventBus);
|
|
2922
|
+
return;
|
|
2923
|
+
}
|
|
2924
|
+
if (result.success) {
|
|
2925
|
+
await this.publishResult(taskId, contextId, result, eventBus);
|
|
2926
|
+
} else {
|
|
2927
|
+
await this.publishError(taskId, contextId, result.error || "Unknown error", eventBus);
|
|
2928
|
+
}
|
|
2929
|
+
this.publishStatusUpdate(taskId, contextId, "completed", eventBus, true);
|
|
2930
|
+
eventBus.finished();
|
|
2931
|
+
logger8.info(`[A2A] Task ${taskId} completed successfully`);
|
|
2932
|
+
} catch (error) {
|
|
2933
|
+
logger8.error(`[A2A] Task ${taskId} failed`, {
|
|
2934
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2935
|
+
});
|
|
2936
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2937
|
+
await this.publishError(taskId, contextId, errorMessage, eventBus);
|
|
2938
|
+
this.publishStatusUpdate(taskId, contextId, "failed", eventBus, true);
|
|
2939
|
+
eventBus.finished();
|
|
2940
|
+
} finally {
|
|
2941
|
+
this.cancelledTasks.delete(taskId);
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
/**
|
|
2945
|
+
* Handles task cancellation requests.
|
|
2946
|
+
*/
|
|
2947
|
+
async cancelTask(taskId, eventBus) {
|
|
2948
|
+
logger8.info(`[A2A] Cancellation requested for task ${taskId}`);
|
|
2949
|
+
this.cancelledTasks.add(taskId);
|
|
2950
|
+
}
|
|
2951
|
+
/**
|
|
2952
|
+
* Extracts text content from an A2A message.
|
|
2953
|
+
*/
|
|
2954
|
+
extractMessageContent(message) {
|
|
2955
|
+
const textParts = message.parts.filter((part) => part.kind === "text");
|
|
2956
|
+
return textParts.map((part) => part.text).join("\n");
|
|
2957
|
+
}
|
|
2958
|
+
/**
|
|
2959
|
+
* Determines if the user message is requesting a specific action.
|
|
2960
|
+
*/
|
|
2961
|
+
isActionRequest(content) {
|
|
2962
|
+
const actionKeywords = [
|
|
2963
|
+
"post",
|
|
2964
|
+
"search",
|
|
2965
|
+
"check feed",
|
|
2966
|
+
"browse",
|
|
2967
|
+
"comment",
|
|
2968
|
+
"upvote",
|
|
2969
|
+
"follow",
|
|
2970
|
+
"send dm"
|
|
2971
|
+
];
|
|
2972
|
+
const lowerContent = content.toLowerCase();
|
|
2973
|
+
return actionKeywords.some((keyword) => lowerContent.includes(keyword));
|
|
2974
|
+
}
|
|
2975
|
+
/**
|
|
2976
|
+
* Executes a chat interaction using the agent's LLM.
|
|
2977
|
+
*/
|
|
2978
|
+
async executeChat(userContent, taskId) {
|
|
2979
|
+
try {
|
|
2980
|
+
const llm = this.agent.getLLM();
|
|
2981
|
+
const systemPrompt = this.agent.getSystemPrompt();
|
|
2982
|
+
const response = await llm.generateText(userContent, {
|
|
2983
|
+
systemPrompt,
|
|
2984
|
+
temperature: this.agent.config.llm.temperature
|
|
2985
|
+
});
|
|
2986
|
+
return {
|
|
2987
|
+
success: true,
|
|
2988
|
+
data: { response, type: "chat" }
|
|
2989
|
+
};
|
|
2990
|
+
} catch (error) {
|
|
2991
|
+
return {
|
|
2992
|
+
success: false,
|
|
2993
|
+
error: `Chat execution failed: ${error instanceof Error ? error.message : String(error)}`
|
|
2994
|
+
};
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
/**
|
|
2998
|
+
* Executes an action based on the user's request.
|
|
2999
|
+
*/
|
|
3000
|
+
async executeAction(userContent, taskId) {
|
|
3001
|
+
try {
|
|
3002
|
+
const decision = await this.agent.think();
|
|
3003
|
+
const result = await this.agent.act(decision.action, decision.parameters);
|
|
3004
|
+
return {
|
|
3005
|
+
success: result.success,
|
|
3006
|
+
data: {
|
|
3007
|
+
action: decision.action,
|
|
3008
|
+
reasoning: decision.reasoning,
|
|
3009
|
+
result: result.data,
|
|
3010
|
+
type: "action"
|
|
3011
|
+
},
|
|
3012
|
+
error: result.error
|
|
3013
|
+
};
|
|
3014
|
+
} catch (error) {
|
|
3015
|
+
return {
|
|
3016
|
+
success: false,
|
|
3017
|
+
error: `Action execution failed: ${error instanceof Error ? error.message : String(error)}`
|
|
3018
|
+
};
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
/**
|
|
3022
|
+
* Publishes a successful result as an A2A artifact.
|
|
3023
|
+
*/
|
|
3024
|
+
async publishResult(taskId, contextId, result, eventBus) {
|
|
3025
|
+
const data = result.data;
|
|
3026
|
+
if (data?.type === "chat") {
|
|
3027
|
+
const artifactUpdate = {
|
|
3028
|
+
kind: "artifact-update",
|
|
3029
|
+
taskId,
|
|
3030
|
+
contextId,
|
|
3031
|
+
artifact: {
|
|
3032
|
+
artifactId: `response-${uuidv42()}`,
|
|
3033
|
+
name: "response.txt",
|
|
3034
|
+
parts: [{ kind: "text", text: data.response }]
|
|
3035
|
+
}
|
|
3036
|
+
};
|
|
3037
|
+
eventBus.publish(artifactUpdate);
|
|
3038
|
+
} else if (data?.type === "action") {
|
|
3039
|
+
const resultText = JSON.stringify(
|
|
3040
|
+
{
|
|
3041
|
+
action: data.action,
|
|
3042
|
+
reasoning: data.reasoning,
|
|
3043
|
+
result: data.result
|
|
3044
|
+
},
|
|
3045
|
+
null,
|
|
3046
|
+
2
|
|
3047
|
+
);
|
|
3048
|
+
const artifactUpdate = {
|
|
3049
|
+
kind: "artifact-update",
|
|
3050
|
+
taskId,
|
|
3051
|
+
contextId,
|
|
3052
|
+
artifact: {
|
|
3053
|
+
artifactId: `action-result-${uuidv42()}`,
|
|
3054
|
+
name: "action_result.json",
|
|
3055
|
+
parts: [{ kind: "text", text: resultText }]
|
|
3056
|
+
}
|
|
3057
|
+
};
|
|
3058
|
+
eventBus.publish(artifactUpdate);
|
|
3059
|
+
} else {
|
|
3060
|
+
const resultText = typeof result.data === "string" ? result.data : JSON.stringify(result.data, null, 2);
|
|
3061
|
+
const artifactUpdate = {
|
|
3062
|
+
kind: "artifact-update",
|
|
3063
|
+
taskId,
|
|
3064
|
+
contextId,
|
|
3065
|
+
artifact: {
|
|
3066
|
+
artifactId: `result-${uuidv42()}`,
|
|
3067
|
+
name: "result.txt",
|
|
3068
|
+
parts: [{ kind: "text", text: resultText }]
|
|
3069
|
+
}
|
|
3070
|
+
};
|
|
3071
|
+
eventBus.publish(artifactUpdate);
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
/**
|
|
3075
|
+
* Publishes an error as an A2A artifact.
|
|
3076
|
+
*/
|
|
3077
|
+
async publishError(taskId, contextId, error, eventBus) {
|
|
3078
|
+
const artifactUpdate = {
|
|
3079
|
+
kind: "artifact-update",
|
|
3080
|
+
taskId,
|
|
3081
|
+
contextId,
|
|
3082
|
+
artifact: {
|
|
3083
|
+
artifactId: `error-${uuidv42()}`,
|
|
3084
|
+
name: "error.txt",
|
|
3085
|
+
parts: [{ kind: "text", text: `Error: ${error}` }]
|
|
3086
|
+
}
|
|
3087
|
+
};
|
|
3088
|
+
eventBus.publish(artifactUpdate);
|
|
3089
|
+
}
|
|
3090
|
+
/**
|
|
3091
|
+
* Publishes a status update event.
|
|
3092
|
+
*/
|
|
3093
|
+
publishStatusUpdate(taskId, contextId, state, eventBus, final) {
|
|
3094
|
+
const statusUpdate = {
|
|
3095
|
+
kind: "status-update",
|
|
3096
|
+
taskId,
|
|
3097
|
+
contextId,
|
|
3098
|
+
status: {
|
|
3099
|
+
state,
|
|
3100
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3101
|
+
},
|
|
3102
|
+
final
|
|
3103
|
+
};
|
|
3104
|
+
eventBus.publish(statusUpdate);
|
|
3105
|
+
}
|
|
3106
|
+
/**
|
|
3107
|
+
* Publishes a cancellation status.
|
|
3108
|
+
*/
|
|
3109
|
+
publishCancellation(taskId, contextId, eventBus) {
|
|
3110
|
+
logger8.info(`[A2A] Publishing cancellation for task ${taskId}`);
|
|
3111
|
+
this.publishStatusUpdate(taskId, contextId, "canceled", eventBus, true);
|
|
3112
|
+
eventBus.finished();
|
|
3113
|
+
this.cancelledTasks.delete(taskId);
|
|
3114
|
+
}
|
|
3115
|
+
};
|
|
3116
|
+
|
|
3117
|
+
// src/a2a/integration.ts
|
|
3118
|
+
import { DefaultRequestHandler, InMemoryTaskStore, JsonRpcTransportHandler } from "@a2a-js/sdk/server";
|
|
3119
|
+
var logger9 = createLogger("A2AIntegration");
|
|
3120
|
+
var A2AIntegration = class {
|
|
3121
|
+
agent;
|
|
3122
|
+
agentCard;
|
|
3123
|
+
requestHandler;
|
|
3124
|
+
moltiumExecutor;
|
|
3125
|
+
constructor(agent, options) {
|
|
3126
|
+
this.agent = agent;
|
|
3127
|
+
this.agentCard = AgentCardBuilder.build(agent.config, options.a2aConfig);
|
|
3128
|
+
this.moltiumExecutor = new MoltiumExecutor(agent);
|
|
3129
|
+
this.requestHandler = new DefaultRequestHandler(
|
|
3130
|
+
this.agentCard,
|
|
3131
|
+
new InMemoryTaskStore(),
|
|
3132
|
+
this.moltiumExecutor
|
|
3133
|
+
);
|
|
3134
|
+
logger9.info("A2A integration initialized", {
|
|
3135
|
+
agentName: agent.name,
|
|
3136
|
+
baseUrl: options.a2aConfig.baseUrl
|
|
3137
|
+
});
|
|
3138
|
+
}
|
|
3139
|
+
/**
|
|
3140
|
+
* Creates Express middleware handlers for A2A endpoints.
|
|
3141
|
+
* Returns an object with individual handlers that can be mounted on specific routes.
|
|
3142
|
+
*/
|
|
3143
|
+
getHandlers() {
|
|
3144
|
+
return {
|
|
3145
|
+
// Agent Card endpoint - serves the static agent card JSON
|
|
3146
|
+
agentCard: ((req, res) => {
|
|
3147
|
+
res.json(this.agentCard);
|
|
3148
|
+
}),
|
|
3149
|
+
// JSON-RPC endpoint - handles A2A protocol requests
|
|
3150
|
+
jsonRpc: (async (req, res) => {
|
|
3151
|
+
try {
|
|
3152
|
+
const jsonRpcHandler = new JsonRpcTransportHandler(this.requestHandler);
|
|
3153
|
+
const response = await jsonRpcHandler.handle(req.body);
|
|
3154
|
+
res.json(response);
|
|
3155
|
+
} catch (error) {
|
|
3156
|
+
logger9.error("JSON-RPC error:", error);
|
|
3157
|
+
res.status(500).json({
|
|
3158
|
+
jsonrpc: "2.0",
|
|
3159
|
+
error: {
|
|
3160
|
+
code: -32603,
|
|
3161
|
+
message: "Internal error",
|
|
3162
|
+
data: error.message
|
|
3163
|
+
},
|
|
3164
|
+
id: req.body?.id || null
|
|
3165
|
+
});
|
|
3166
|
+
}
|
|
3167
|
+
}),
|
|
3168
|
+
// REST endpoint - simplified HTTP+JSON interface
|
|
3169
|
+
rest: ((req, res) => {
|
|
3170
|
+
res.status(501).json({
|
|
3171
|
+
error: "REST transport not implemented in SDK v0.3.0",
|
|
3172
|
+
message: "Please use the JSON-RPC endpoint at /a2a/jsonrpc"
|
|
3173
|
+
});
|
|
3174
|
+
})
|
|
3175
|
+
};
|
|
3176
|
+
}
|
|
3177
|
+
/**
|
|
3178
|
+
* Updates the agent card with actual runtime information (host/port).
|
|
3179
|
+
*/
|
|
3180
|
+
updateAgentCard(actualPort, actualHost = "localhost") {
|
|
3181
|
+
this.agentCard = AgentCardBuilder.updateWithRuntime(this.agentCard, actualPort, actualHost);
|
|
3182
|
+
this.requestHandler = new DefaultRequestHandler(
|
|
3183
|
+
this.agentCard,
|
|
3184
|
+
new InMemoryTaskStore(),
|
|
3185
|
+
this.moltiumExecutor
|
|
3186
|
+
);
|
|
3187
|
+
logger9.info("A2A agent card updated with runtime info", {
|
|
3188
|
+
host: actualHost,
|
|
3189
|
+
port: actualPort,
|
|
3190
|
+
url: this.agentCard.url
|
|
3191
|
+
});
|
|
3192
|
+
}
|
|
3193
|
+
/**
|
|
3194
|
+
* Gets the current agent card.
|
|
3195
|
+
*/
|
|
3196
|
+
getAgentCard() {
|
|
3197
|
+
return this.agentCard;
|
|
3198
|
+
}
|
|
3199
|
+
/**
|
|
3200
|
+
* Logs A2A endpoint information.
|
|
3201
|
+
*/
|
|
3202
|
+
logEndpoints(host, port) {
|
|
3203
|
+
const protocol = host === "localhost" || host === "127.0.0.1" ? "http" : "https";
|
|
3204
|
+
const baseUrl = `${protocol}://${host}:${port}`;
|
|
3205
|
+
logger9.info("A2A endpoints available:");
|
|
3206
|
+
logger9.info(` Agent Card: ${baseUrl}/.well-known/agent-card.json`);
|
|
3207
|
+
logger9.info(` JSON-RPC: ${baseUrl}/a2a/jsonrpc`);
|
|
3208
|
+
logger9.info(` HTTP+JSON: ${baseUrl}/a2a/rest`);
|
|
3209
|
+
}
|
|
3210
|
+
};
|
|
3211
|
+
function createA2AIntegration(agent, options = {}) {
|
|
3212
|
+
const a2aConfig = {
|
|
3213
|
+
enabled: true,
|
|
3214
|
+
baseUrl: options.a2aConfig?.baseUrl || "http://localhost:3000",
|
|
3215
|
+
pushNotifications: options.a2aConfig?.pushNotifications ?? false,
|
|
3216
|
+
streaming: options.a2aConfig?.streaming ?? true,
|
|
3217
|
+
stateTransitionHistory: options.a2aConfig?.stateTransitionHistory ?? true
|
|
3218
|
+
};
|
|
3219
|
+
return new A2AIntegration(agent, { a2aConfig, ...options });
|
|
3220
|
+
}
|
|
3221
|
+
|
|
2308
3222
|
// src/server/app.ts
|
|
2309
|
-
var
|
|
2310
|
-
|
|
3223
|
+
var logger10 = createLogger("Server");
|
|
3224
|
+
var AGENT_CARD_PATH = ".well-known/agent-card.json";
|
|
3225
|
+
function createApp(agent, options = {}) {
|
|
2311
3226
|
const app = express();
|
|
2312
3227
|
app.use(express.json());
|
|
2313
3228
|
app.use(requestLogger);
|
|
2314
3229
|
app.use(authMiddleware);
|
|
2315
3230
|
app.use(createRoutes(agent));
|
|
3231
|
+
if (options.enableA2A !== false) {
|
|
3232
|
+
const port = options.port || parseInt(process.env.PORT || "3000", 10);
|
|
3233
|
+
const host = options.host || "0.0.0.0";
|
|
3234
|
+
const protocol = host === "localhost" || host === "127.0.0.1" || host === "0.0.0.0" ? "http" : "https";
|
|
3235
|
+
const baseUrl = options.a2aConfig?.baseUrl || `${protocol}://${host}:${port}`;
|
|
3236
|
+
const a2aIntegration = createA2AIntegration(agent, {
|
|
3237
|
+
a2aConfig: {
|
|
3238
|
+
enabled: true,
|
|
3239
|
+
baseUrl,
|
|
3240
|
+
pushNotifications: options.a2aConfig?.pushNotifications ?? false,
|
|
3241
|
+
streaming: options.a2aConfig?.streaming ?? true,
|
|
3242
|
+
stateTransitionHistory: options.a2aConfig?.stateTransitionHistory ?? true
|
|
3243
|
+
}
|
|
3244
|
+
});
|
|
3245
|
+
const handlers = a2aIntegration.getHandlers();
|
|
3246
|
+
app.use(`/${AGENT_CARD_PATH}`, handlers.agentCard);
|
|
3247
|
+
app.use("/a2a/jsonrpc", handlers.jsonRpc);
|
|
3248
|
+
app.use("/a2a/rest", handlers.rest);
|
|
3249
|
+
logger10.info("A2A protocol endpoints enabled");
|
|
3250
|
+
}
|
|
2316
3251
|
app.use(errorHandler);
|
|
2317
3252
|
return app;
|
|
2318
3253
|
}
|
|
2319
3254
|
async function startServer(agent, options = {}) {
|
|
2320
3255
|
const port = options.port || parseInt(process.env.PORT || "3000", 10);
|
|
2321
3256
|
const host = options.host || "0.0.0.0";
|
|
2322
|
-
const app = createApp(agent);
|
|
3257
|
+
const app = createApp(agent, options);
|
|
2323
3258
|
await agent.start();
|
|
2324
3259
|
app.listen(port, host, () => {
|
|
2325
|
-
|
|
3260
|
+
logger10.info(`Agent "${agent.name}" running at http://${host}:${port}`);
|
|
3261
|
+
if (options.enableA2A !== false) {
|
|
3262
|
+
const protocol = host === "localhost" || host === "127.0.0.1" ? "http" : "https";
|
|
3263
|
+
const baseUrl = `${protocol}://${host}:${port}`;
|
|
3264
|
+
logger10.info("A2A Protocol Endpoints:");
|
|
3265
|
+
logger10.info(` Agent Card: ${baseUrl}/.well-known/agent-card.json`);
|
|
3266
|
+
logger10.info(` JSON-RPC: ${baseUrl}/a2a/jsonrpc`);
|
|
3267
|
+
logger10.info(` HTTP+JSON: ${baseUrl}/a2a/rest`);
|
|
3268
|
+
}
|
|
2326
3269
|
});
|
|
2327
3270
|
const shutdown = async () => {
|
|
2328
|
-
|
|
3271
|
+
logger10.info("Received shutdown signal");
|
|
2329
3272
|
await agent.stop();
|
|
2330
3273
|
process.exit(0);
|
|
2331
3274
|
};
|
|
@@ -2333,9 +3276,11 @@ async function startServer(agent, options = {}) {
|
|
|
2333
3276
|
process.on("SIGTERM", shutdown);
|
|
2334
3277
|
}
|
|
2335
3278
|
export {
|
|
3279
|
+
A2AIntegration,
|
|
2336
3280
|
ActionHandler,
|
|
2337
3281
|
ActionRegistry,
|
|
2338
3282
|
Agent,
|
|
3283
|
+
AgentCardBuilder,
|
|
2339
3284
|
AnthropicProvider,
|
|
2340
3285
|
ConfigLoader,
|
|
2341
3286
|
LLMProvider,
|
|
@@ -2343,6 +3288,7 @@ export {
|
|
|
2343
3288
|
MarkdownParser,
|
|
2344
3289
|
Memory,
|
|
2345
3290
|
MoltbookAdapter,
|
|
3291
|
+
MoltiumExecutor,
|
|
2346
3292
|
OpenAIProvider,
|
|
2347
3293
|
Scheduler,
|
|
2348
3294
|
ShortTermMemory,
|
|
@@ -2352,10 +3298,12 @@ export {
|
|
|
2352
3298
|
buildSkillPrompt,
|
|
2353
3299
|
buildSystemPrompt,
|
|
2354
3300
|
builtInActions,
|
|
3301
|
+
createA2AIntegration,
|
|
2355
3302
|
createApp,
|
|
2356
3303
|
createLogger,
|
|
2357
3304
|
createMarkdownAction,
|
|
2358
3305
|
createRoutes,
|
|
3306
|
+
moltbookActions,
|
|
2359
3307
|
startServer,
|
|
2360
3308
|
validateConfig
|
|
2361
3309
|
};
|