@perkos/perkos-a2a 0.8.23 → 0.8.25
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/agent.js +1 -0
- package/dist/agent.js.map +1 -1
- package/dist/agentic-actions.d.ts +9 -0
- package/dist/agentic-actions.d.ts.map +1 -1
- package/dist/agentic-actions.js +277 -65
- package/dist/agentic-actions.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +241 -67
- package/dist/index.js.map +2 -2
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -27341,6 +27341,12 @@ var A2AServer = class {
|
|
|
27341
27341
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
27342
27342
|
|
|
27343
27343
|
// src/agentic-actions.ts
|
|
27344
|
+
var FALLBACK_AGENT_ALIASES = [
|
|
27345
|
+
["Perkos-Claw-Tester", ["perkos-claw-tester", "openclaw", "open claw", "claw"]],
|
|
27346
|
+
["Perkos-Hermes-Tester", ["perkos-hermes-tester", "hermes"]],
|
|
27347
|
+
["Morpheus-A2A-Receiver", ["morpheus-a2a-receiver", "morpheus"]],
|
|
27348
|
+
["Apollo", ["apollo"]]
|
|
27349
|
+
];
|
|
27344
27350
|
function includesAny(text, terms) {
|
|
27345
27351
|
const lower = text.toLowerCase();
|
|
27346
27352
|
return terms.some((term) => lower.includes(term.toLowerCase()));
|
|
@@ -27360,29 +27366,124 @@ function requestedMarker(text, fallback) {
|
|
|
27360
27366
|
function stripDelegationNoise(text) {
|
|
27361
27367
|
return text.replace(/^Delegated by .*?\.\s*/i, "").replace(/^Delegated request:\s*/i, "").replace(/^A2A-DELEGATION[^\n]*\n?/gim, "").replace(/End with DELEGATED_A2A_DONE\.?/gi, "").trim();
|
|
27362
27368
|
}
|
|
27363
|
-
function
|
|
27364
|
-
|
|
27365
|
-
|
|
27366
|
-
|
|
27367
|
-
|
|
27368
|
-
|
|
27369
|
-
|
|
27370
|
-
|
|
27371
|
-
|
|
27372
|
-
|
|
27369
|
+
function normalizeKey(value) {
|
|
27370
|
+
return value.toLowerCase().normalize("NFKD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, " ").trim().replace(/\s+/g, " ");
|
|
27371
|
+
}
|
|
27372
|
+
function compactKey(value) {
|
|
27373
|
+
return normalizeKey(value).replace(/\s+/g, "");
|
|
27374
|
+
}
|
|
27375
|
+
function addAlias(aliases, alias, canonical) {
|
|
27376
|
+
const normalized = normalizeKey(alias);
|
|
27377
|
+
if (normalized) aliases.set(normalized, canonical);
|
|
27378
|
+
const compact = compactKey(alias);
|
|
27379
|
+
if (compact) aliases.set(compact, canonical);
|
|
27380
|
+
}
|
|
27381
|
+
function aliasesForName(name) {
|
|
27382
|
+
const base = normalizeKey(name);
|
|
27383
|
+
const compact = compactKey(name);
|
|
27384
|
+
const withoutTester = base.replace(/\btester\b/g, "").replace(/\s+/g, " ").trim();
|
|
27385
|
+
const parts = base.split(" ").filter(Boolean);
|
|
27386
|
+
return Array.from(new Set([
|
|
27387
|
+
name,
|
|
27388
|
+
base,
|
|
27389
|
+
compact,
|
|
27390
|
+
withoutTester,
|
|
27391
|
+
parts.at(-1) || "",
|
|
27392
|
+
parts.at(0) || ""
|
|
27393
|
+
].filter(Boolean)));
|
|
27394
|
+
}
|
|
27395
|
+
async function buildAgentDirectory(server, selfName) {
|
|
27396
|
+
const names = /* @__PURE__ */ new Set();
|
|
27397
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
27398
|
+
names.add(selfName);
|
|
27399
|
+
for (const [name, extraAliases] of FALLBACK_AGENT_ALIASES) {
|
|
27400
|
+
names.add(name);
|
|
27401
|
+
for (const alias of [name, ...extraAliases]) addAlias(aliases, alias, name);
|
|
27402
|
+
}
|
|
27403
|
+
if (server.discoverPeers) {
|
|
27404
|
+
try {
|
|
27405
|
+
const peers = await server.discoverPeers();
|
|
27406
|
+
for (const [name, entry] of Object.entries(peers)) {
|
|
27407
|
+
const canonical = entry.card?.name || name;
|
|
27408
|
+
if (!canonical || canonical === selfName) continue;
|
|
27409
|
+
names.add(canonical);
|
|
27410
|
+
for (const alias of aliasesForName(canonical)) addAlias(aliases, alias, canonical);
|
|
27411
|
+
if (name !== canonical) for (const alias of aliasesForName(name)) addAlias(aliases, alias, canonical);
|
|
27412
|
+
}
|
|
27413
|
+
} catch {
|
|
27414
|
+
}
|
|
27415
|
+
}
|
|
27416
|
+
for (const name of names) for (const alias of aliasesForName(name)) addAlias(aliases, alias, name);
|
|
27417
|
+
return { names: Array.from(names), aliases };
|
|
27418
|
+
}
|
|
27419
|
+
function resolveAgentName(raw, directory, selfName, allowSelf = false) {
|
|
27420
|
+
const cleaned = raw.replace(/^@/, "").trim();
|
|
27421
|
+
if (!cleaned) return void 0;
|
|
27422
|
+
const normalized = normalizeKey(cleaned);
|
|
27423
|
+
const compact = compactKey(cleaned);
|
|
27424
|
+
for (const name of directory.names) {
|
|
27425
|
+
if (!allowSelf && name === selfName) continue;
|
|
27426
|
+
if (normalizeKey(name) === normalized || compactKey(name) === compact) return name;
|
|
27427
|
+
}
|
|
27428
|
+
const exact = directory.aliases.get(normalized) || directory.aliases.get(compact);
|
|
27429
|
+
if (exact && (allowSelf || exact !== selfName)) return exact;
|
|
27430
|
+
const entries = Array.from(directory.aliases.entries()).sort((a, b) => b[0].length - a[0].length);
|
|
27431
|
+
for (const [alias, canonical] of entries) {
|
|
27432
|
+
if (!allowSelf && canonical === selfName) continue;
|
|
27433
|
+
if (alias.length < 4) continue;
|
|
27434
|
+
if (normalized.includes(alias) || compact.includes(alias.replace(/\s+/g, ""))) return canonical;
|
|
27373
27435
|
}
|
|
27374
27436
|
return void 0;
|
|
27375
27437
|
}
|
|
27376
|
-
function
|
|
27377
|
-
const
|
|
27378
|
-
|
|
27379
|
-
|
|
27438
|
+
function findMentionedAgents(text, directory, selfName) {
|
|
27439
|
+
const normalized = normalizeKey(stripDelegationNoise(text));
|
|
27440
|
+
const compact = compactKey(stripDelegationNoise(text));
|
|
27441
|
+
const hits = [];
|
|
27442
|
+
const seen = /* @__PURE__ */ new Set();
|
|
27443
|
+
const entries = Array.from(directory.aliases.entries()).sort((a, b) => b[0].length - a[0].length);
|
|
27444
|
+
for (const [alias, canonical] of entries) {
|
|
27445
|
+
if (canonical === selfName || alias.length < 3 || seen.has(canonical)) continue;
|
|
27446
|
+
const idx = normalized.indexOf(alias);
|
|
27447
|
+
const compactIdx = compact.indexOf(alias.replace(/\s+/g, ""));
|
|
27448
|
+
const index = idx >= 0 ? idx : compactIdx;
|
|
27449
|
+
if (index >= 0) {
|
|
27450
|
+
hits.push({ name: canonical, index });
|
|
27451
|
+
seen.add(canonical);
|
|
27452
|
+
}
|
|
27380
27453
|
}
|
|
27381
|
-
|
|
27382
|
-
|
|
27454
|
+
return hits.sort((a, b) => a.index - b.index).map((hit) => hit.name);
|
|
27455
|
+
}
|
|
27456
|
+
function parseArrowRoute(text, directory, selfName) {
|
|
27457
|
+
const clean = stripDelegationNoise(text);
|
|
27458
|
+
const routeLine = clean.split(/\n/).find((line) => /(?:->|→|=>|➡)/.test(line));
|
|
27459
|
+
if (!routeLine) return [];
|
|
27460
|
+
const route = routeLine.split(/(?:->|→|=>|➡)/).map((part) => resolveAgentName(part, directory, selfName) || (normalizeKey(part).includes(normalizeKey(selfName)) ? selfName : void 0)).filter(Boolean);
|
|
27461
|
+
return Array.from(new Set(route));
|
|
27462
|
+
}
|
|
27463
|
+
function detectTarget(text, selfName, directory) {
|
|
27464
|
+
const route = parseDelegationEnvelope(text).route;
|
|
27465
|
+
if (route.length) return nextRouteTarget(route, selfName);
|
|
27466
|
+
const arrowRoute = parseArrowRoute(text, directory, selfName);
|
|
27467
|
+
if (arrowRoute.length) return nextRouteTarget(arrowRoute, selfName) || arrowRoute.find((name) => name !== selfName);
|
|
27468
|
+
return findMentionedAgents(text, directory, selfName)[0];
|
|
27469
|
+
}
|
|
27470
|
+
function detectReplyTarget(text, selfName, directory) {
|
|
27471
|
+
const envelope = parseDelegationEnvelope(text);
|
|
27472
|
+
if (envelope.notify && envelope.notify !== selfName) return envelope.notify;
|
|
27473
|
+
const clean = stripDelegationNoise(text);
|
|
27474
|
+
const patterns = [
|
|
27475
|
+
/(?:reply|respond|notify|send|return|env[ií]a|enviar|responde|notifica)(?:\s+the\s+result)?\s+(?:to|a)\s+([A-Za-z0-9_. -]{2,80}?)(?:[.;,\n]|$)/i,
|
|
27476
|
+
/(?:notify|notifica|reply|responde)\s+([A-Za-z0-9_. -]{2,80}?)(?:[.;,\n]|$)/i,
|
|
27477
|
+
/(?:final|ultimo|último).*?(?:notify|notifica|responde|reply).*?(?:to|a)?\s+([A-Za-z0-9_. -]{2,80}?)(?:[.;,\n]|$)/i
|
|
27478
|
+
];
|
|
27479
|
+
for (const pattern of patterns) {
|
|
27480
|
+
const match = clean.match(pattern);
|
|
27481
|
+
const resolved = match?.[1] ? resolveAgentName(match[1], directory, selfName, true) : void 0;
|
|
27482
|
+
if (resolved) return resolved;
|
|
27383
27483
|
}
|
|
27384
|
-
if (
|
|
27385
|
-
|
|
27484
|
+
if (includesAny(clean, ["responda a", "env\xEDe", "envia", "send", "reply", "notify", "notifica", "morpheus"])) {
|
|
27485
|
+
const mentioned = findMentionedAgents(clean, directory, selfName);
|
|
27486
|
+
return mentioned.at(-1);
|
|
27386
27487
|
}
|
|
27387
27488
|
return void 0;
|
|
27388
27489
|
}
|
|
@@ -27447,60 +27548,124 @@ function answerA2ARelay(selfName) {
|
|
|
27447
27548
|
"It handles registration, discovery, message routing, heartbeats, and delivery fallback while each agent keeps its own runtime and local permissions."
|
|
27448
27549
|
].join("\n");
|
|
27449
27550
|
}
|
|
27551
|
+
function parseDelegationEnvelope(text) {
|
|
27552
|
+
const line = text.match(/^A2A-DELEGATION[^\n]*/im)?.[0] || "";
|
|
27553
|
+
const routeRaw = line.match(/\broute=([^\s]+)/i)?.[1];
|
|
27554
|
+
const notifyRaw = line.match(/\bnotify=([^\s]+)/i)?.[1];
|
|
27555
|
+
const markerRaw = line.match(/\bmarker=([A-Z0-9_]{6,})/i)?.[1];
|
|
27556
|
+
const depthRaw = line.match(/\bdepth=(\d+)/i)?.[1];
|
|
27557
|
+
return {
|
|
27558
|
+
route: routeRaw ? routeRaw.split(/[|,]/).map((x) => x.trim()).filter(Boolean) : [],
|
|
27559
|
+
notify: notifyRaw && notifyRaw !== "none" ? notifyRaw : void 0,
|
|
27560
|
+
marker: markerRaw,
|
|
27561
|
+
depth: depthRaw ? Number(depthRaw) : 0
|
|
27562
|
+
};
|
|
27563
|
+
}
|
|
27450
27564
|
function delegationDepth(text) {
|
|
27451
|
-
|
|
27452
|
-
|
|
27565
|
+
return parseDelegationEnvelope(text).depth;
|
|
27566
|
+
}
|
|
27567
|
+
function nextRouteTarget(route, selfName) {
|
|
27568
|
+
const selfIndex = route.findIndex((name) => name === selfName);
|
|
27569
|
+
if (selfIndex >= 0) return route.slice(selfIndex + 1).find((name) => name !== selfName);
|
|
27570
|
+
return route.find((name) => name !== selfName);
|
|
27571
|
+
}
|
|
27572
|
+
function cleanNestedResult(text, finalMarker) {
|
|
27573
|
+
const escaped = finalMarker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
27574
|
+
return text.replace(new RegExp(`
|
|
27575
|
+
?${escaped}s*$`, "g"), "").replace(/\n?DELEGATED_A2A_DONE\s*$/g, "").trim();
|
|
27576
|
+
}
|
|
27577
|
+
function routeForDelegation(selfName, target, text, directory) {
|
|
27578
|
+
const envelopeRoute = parseDelegationEnvelope(text).route;
|
|
27579
|
+
if (envelopeRoute.length) return Array.from(new Set(envelopeRoute));
|
|
27580
|
+
const arrowRoute = parseArrowRoute(text, directory, selfName);
|
|
27581
|
+
if (arrowRoute.length) return Array.from(new Set(arrowRoute.includes(selfName) ? arrowRoute : [selfName, ...arrowRoute]));
|
|
27582
|
+
const mentions = findMentionedAgents(text, directory, selfName).filter((name) => name !== selfName);
|
|
27583
|
+
const route = [selfName, target, ...mentions.filter((name) => name !== target)];
|
|
27584
|
+
return Array.from(new Set(route));
|
|
27453
27585
|
}
|
|
27454
27586
|
function buildDelegatedMessage(input) {
|
|
27455
|
-
const { originalText, selfName, target, marker } = input;
|
|
27587
|
+
const { originalText, selfName, target, route, marker, notify } = input;
|
|
27456
27588
|
const depth = delegationDepth(originalText) + 1;
|
|
27457
27589
|
const clean = stripDelegationNoise(originalText);
|
|
27458
|
-
const header = `A2A-DELEGATION origin=${selfName} target=${target} depth=${depth}`;
|
|
27459
|
-
if (hasA2ARelayExplainTask(clean)) {
|
|
27460
|
-
if (target === "Perkos-Hermes-Tester") {
|
|
27461
|
-
return [
|
|
27462
|
-
header,
|
|
27463
|
-
"Complete this task directly. Briefly explain what an A2A relay is and why agents use it.",
|
|
27464
|
-
"Do not delegate this request further unless explicitly required by a new instruction.",
|
|
27465
|
-
"End with DELEGATED_A2A_DONE."
|
|
27466
|
-
].join("\n");
|
|
27467
|
-
}
|
|
27590
|
+
const header = `A2A-DELEGATION origin=${selfName} target=${target} route=${route.join("|")} notify=${notify || "none"} depth=${depth} marker=${marker}`;
|
|
27591
|
+
if (hasA2ARelayExplainTask(clean) && !nextRouteTarget(route, target)) {
|
|
27468
27592
|
return [
|
|
27469
27593
|
header,
|
|
27470
|
-
"
|
|
27471
|
-
"
|
|
27472
|
-
|
|
27594
|
+
"Complete this task directly. Briefly explain what an A2A relay is and why agents use it.",
|
|
27595
|
+
"Do not delegate this request further unless explicitly required by a new instruction.",
|
|
27596
|
+
"End with DELEGATED_A2A_DONE."
|
|
27473
27597
|
].join("\n");
|
|
27474
27598
|
}
|
|
27475
27599
|
return [
|
|
27476
27600
|
header,
|
|
27477
|
-
"Complete or delegate the following task once. Do not echo this request back to agents already named in the route.",
|
|
27601
|
+
"Complete or delegate the following task once. Follow the A2A-DELEGATION route exactly. Do not echo this request back to agents already named in the route.",
|
|
27478
27602
|
clean,
|
|
27603
|
+
notify ? `When the route is complete, send or return the final useful result to ${notify}.` : "Return the final useful result to the caller.",
|
|
27479
27604
|
`End exactly with ${marker}.`
|
|
27480
27605
|
].join("\n");
|
|
27481
27606
|
}
|
|
27607
|
+
async function completeTerminalTask(input) {
|
|
27608
|
+
const { selfName, server, task, text, marker, replyTarget, runtimeResponder } = input;
|
|
27609
|
+
const cleanText = stripDelegationNoise(text);
|
|
27610
|
+
const wantsEthResearch = includesAny(cleanText, ["precio de eth", "eth price", "ethereum price", "precio actual de eth"]);
|
|
27611
|
+
let finalText;
|
|
27612
|
+
if (includesAny(cleanText, ["A2A_COMM_TEST", "communication test", "ping test"])) {
|
|
27613
|
+
finalText = [`${selfName} received and completed the A2A communication test.`, marker].join("\n");
|
|
27614
|
+
} else if (hasA2ARelayExplainTask(cleanText)) {
|
|
27615
|
+
finalText = [answerA2ARelay(selfName), "", marker].join("\n");
|
|
27616
|
+
} else if (wantsEthResearch) {
|
|
27617
|
+
finalText = [await researchEthPrice(), "", marker].join("\n");
|
|
27618
|
+
} else if (runtimeResponder) {
|
|
27619
|
+
const reply = await runtimeResponder(cleanText, marker);
|
|
27620
|
+
finalText = [reply, "", marker].join("\n");
|
|
27621
|
+
} else {
|
|
27622
|
+
finalText = [`${selfName} completed the delegated A2A task.`, cleanText, marker].join("\n");
|
|
27623
|
+
}
|
|
27624
|
+
if (replyTarget && replyTarget !== selfName) {
|
|
27625
|
+
const replyResult = await server.sendTask(replyTarget, [
|
|
27626
|
+
`Final A2A result from ${selfName}:`,
|
|
27627
|
+
"",
|
|
27628
|
+
cleanNestedResult(finalText, marker),
|
|
27629
|
+
"",
|
|
27630
|
+
"A2A_FINAL_NOTIFICATION_OK"
|
|
27631
|
+
].join("\n"));
|
|
27632
|
+
finalText = [
|
|
27633
|
+
cleanNestedResult(finalText, marker),
|
|
27634
|
+
"",
|
|
27635
|
+
`Forwarded final result to ${replyTarget}.`,
|
|
27636
|
+
extractTaskFinalText(replyResult),
|
|
27637
|
+
"",
|
|
27638
|
+
marker
|
|
27639
|
+
].join("\n");
|
|
27640
|
+
}
|
|
27641
|
+
completeTaskWithReply(task, finalText);
|
|
27642
|
+
}
|
|
27482
27643
|
async function tryHandleAgenticTask(input) {
|
|
27483
|
-
const { selfName, server, task, text, logger } = input;
|
|
27644
|
+
const { selfName, server, task, text, logger, runtimeResponder } = input;
|
|
27645
|
+
const directory = await buildAgentDirectory(server, selfName);
|
|
27646
|
+
const envelope = parseDelegationEnvelope(text);
|
|
27484
27647
|
const cleanText = stripDelegationNoise(text);
|
|
27485
|
-
const wantsDelegation = includesAny(cleanText, ["usa comunicaci\xF3n a2a", "usar comunicaci\xF3n a2a", "p\xEDdele", "dile a", "delegate", "delegar", "tell", "ask"]);
|
|
27486
|
-
const wantsEthResearch = includesAny(
|
|
27487
|
-
const
|
|
27488
|
-
if (
|
|
27489
|
-
|
|
27648
|
+
const wantsDelegation = includesAny(cleanText, ["usa comunicaci\xF3n a2a", "usar comunicaci\xF3n a2a", "p\xEDdele", "pidele", "dile a", "delegate", "delegar", "tell", "ask", "route", "chain", "handoff", "notify", "notifica"]);
|
|
27649
|
+
const wantsEthResearch = includesAny(cleanText, ["precio de eth", "eth price", "ethereum price", "precio actual de eth"]);
|
|
27650
|
+
const finalMarker = envelope.marker || requestedMarker(text, envelope.route.length ? "DELEGATED_A2A_DONE" : "TRI_HERMES_DONE");
|
|
27651
|
+
if (/^(Final A2A result|Delegated result from|Research result from)\b/i.test(cleanText) && !envelope.route.length) {
|
|
27652
|
+
completeTaskWithReply(task, [`${selfName} received the final A2A notification.`, "", cleanText, "", requestedMarker(text, "A2A_NOTIFICATION_RECEIVED")].join("\n"));
|
|
27653
|
+
return true;
|
|
27654
|
+
}
|
|
27655
|
+
if (includesAny(cleanText, ["A2A_COMM_TEST", "communication test", "ping test"]) && !wantsDelegation && !envelope.route.length) {
|
|
27490
27656
|
completeTaskWithReply(task, [
|
|
27491
27657
|
`${selfName} received and completed the A2A communication test.`,
|
|
27492
|
-
|
|
27658
|
+
finalMarker
|
|
27493
27659
|
].join("\n"));
|
|
27494
27660
|
return true;
|
|
27495
27661
|
}
|
|
27496
|
-
if (hasA2ARelayExplainTask(cleanText) &&
|
|
27497
|
-
|
|
27498
|
-
completeTaskWithReply(task, [answerA2ARelay(selfName), "", marker].join("\n"));
|
|
27662
|
+
if (hasA2ARelayExplainTask(cleanText) && !wantsDelegation && !envelope.route.length) {
|
|
27663
|
+
completeTaskWithReply(task, [answerA2ARelay(selfName), "", finalMarker].join("\n"));
|
|
27499
27664
|
return true;
|
|
27500
27665
|
}
|
|
27501
|
-
if (wantsEthResearch &&
|
|
27666
|
+
if (wantsEthResearch && !wantsDelegation && !envelope.route.length) {
|
|
27502
27667
|
const research = await researchEthPrice();
|
|
27503
|
-
const replyTarget2 =
|
|
27668
|
+
const replyTarget2 = detectReplyTarget(text, selfName, directory);
|
|
27504
27669
|
let replyResultText2 = "";
|
|
27505
27670
|
if (replyTarget2) {
|
|
27506
27671
|
const replyResult = await server.sendTask(replyTarget2, [
|
|
@@ -27512,21 +27677,18 @@ async function tryHandleAgenticTask(input) {
|
|
|
27512
27677
|
].join("\n"));
|
|
27513
27678
|
replyResultText2 = extractTaskFinalText(replyResult);
|
|
27514
27679
|
}
|
|
27515
|
-
const marker = requestedMarker(text, "ETH_RESEARCH_DONE");
|
|
27516
27680
|
completeTaskWithReply(task, [
|
|
27517
27681
|
research,
|
|
27518
27682
|
"",
|
|
27519
27683
|
replyTarget2 ? `Forwarded to ${replyTarget2}.` : void 0,
|
|
27520
27684
|
replyResultText2 ? `Reply delivery result:
|
|
27521
27685
|
${replyResultText2}` : void 0,
|
|
27522
|
-
|
|
27686
|
+
finalMarker
|
|
27523
27687
|
].filter(Boolean).join("\n"));
|
|
27524
27688
|
return true;
|
|
27525
27689
|
}
|
|
27526
|
-
if (!wantsDelegation) return false;
|
|
27527
|
-
|
|
27528
|
-
if (!target) return false;
|
|
27529
|
-
if (delegationDepth(text) >= 4) {
|
|
27690
|
+
if (!wantsDelegation && !envelope.route.length) return false;
|
|
27691
|
+
if (delegationDepth(text) >= Number(process.env.A2A_MAX_DELEGATION_DEPTH || 8)) {
|
|
27530
27692
|
completeTaskWithReply(task, [
|
|
27531
27693
|
`A2A delegation stopped by ${selfName}: max delegation depth reached.`,
|
|
27532
27694
|
"This prevents routing loops across heterogeneous agents.",
|
|
@@ -27534,22 +27696,32 @@ ${replyResultText2}` : void 0,
|
|
|
27534
27696
|
].join("\n"));
|
|
27535
27697
|
return true;
|
|
27536
27698
|
}
|
|
27537
|
-
const
|
|
27538
|
-
const
|
|
27699
|
+
const target = detectTarget(text, selfName, directory);
|
|
27700
|
+
const replyTarget = detectReplyTarget(text, selfName, directory);
|
|
27701
|
+
if (!target) {
|
|
27702
|
+
await completeTerminalTask({ selfName, server, task, text, marker: finalMarker, replyTarget, runtimeResponder });
|
|
27703
|
+
return true;
|
|
27704
|
+
}
|
|
27705
|
+
const route = routeForDelegation(selfName, target, text, directory);
|
|
27706
|
+
const next = nextRouteTarget(route, selfName) || target;
|
|
27707
|
+
if (!next || next === selfName) {
|
|
27708
|
+
await completeTerminalTask({ selfName, server, task, text, marker: finalMarker, replyTarget, runtimeResponder });
|
|
27709
|
+
return true;
|
|
27710
|
+
}
|
|
27539
27711
|
const delegatedMessage = wantsEthResearch ? [
|
|
27540
|
-
`A2A-DELEGATION origin=${selfName} target=${
|
|
27712
|
+
`A2A-DELEGATION origin=${selfName} target=${next} route=${route.join("|")} notify=${replyTarget || "none"} depth=${delegationDepth(text) + 1} marker=${finalMarker}`,
|
|
27541
27713
|
"Delegated research request. Research the current ETH price using available live/public APIs.",
|
|
27542
27714
|
"Return concise findings with source names, timestamp if available, and limitations.",
|
|
27543
|
-
replyTarget ? `If possible, send the final result to ${replyTarget} via A2A.` : "",
|
|
27715
|
+
replyTarget ? `If possible, send the final result to ${replyTarget} via A2A.` : "Return the final useful result to the caller.",
|
|
27544
27716
|
"End with DELEGATED_ETH_RESEARCH_DONE."
|
|
27545
|
-
].filter(Boolean).join("\n") : buildDelegatedMessage({ originalText: text, selfName, target, marker: finalMarker });
|
|
27546
|
-
logger.info(`[perkos-a2a] Agentic delegation: ${selfName} -> ${
|
|
27547
|
-
const delegatedResult = await server.sendTask(
|
|
27548
|
-
const delegatedText = extractTaskFinalText(delegatedResult);
|
|
27717
|
+
].filter(Boolean).join("\n") : buildDelegatedMessage({ originalText: text, selfName, target: next, route, marker: finalMarker, notify: replyTarget });
|
|
27718
|
+
logger.info(`[perkos-a2a] Agentic delegation: ${selfName} -> ${next}${replyTarget ? ` -> notify ${replyTarget}` : ""}; route=${route.join(" -> ")}`);
|
|
27719
|
+
const delegatedResult = await server.sendTask(next, delegatedMessage);
|
|
27720
|
+
const delegatedText = cleanNestedResult(extractTaskFinalText(delegatedResult), finalMarker);
|
|
27549
27721
|
let replyResultText = "";
|
|
27550
|
-
if (replyTarget && replyTarget !==
|
|
27722
|
+
if (replyTarget && replyTarget !== next && !delegatedText.includes("Forwarded final result to")) {
|
|
27551
27723
|
const replyMessage = [
|
|
27552
|
-
`Delegated result from ${
|
|
27724
|
+
`Delegated result from ${next}, forwarded by ${selfName}:`,
|
|
27553
27725
|
"",
|
|
27554
27726
|
delegatedText,
|
|
27555
27727
|
"",
|
|
@@ -27560,7 +27732,8 @@ ${replyResultText2}` : void 0,
|
|
|
27560
27732
|
}
|
|
27561
27733
|
completeTaskWithReply(task, [
|
|
27562
27734
|
`Agentic A2A delegation completed by ${selfName}.`,
|
|
27563
|
-
`Delegated target: ${
|
|
27735
|
+
`Delegated target: ${next}`,
|
|
27736
|
+
`Route: ${route.join(" -> ")}`,
|
|
27564
27737
|
replyTarget ? `Reply target: ${replyTarget}` : void 0,
|
|
27565
27738
|
"",
|
|
27566
27739
|
"Delegated result:",
|
|
@@ -27667,7 +27840,8 @@ function register(api) {
|
|
|
27667
27840
|
server,
|
|
27668
27841
|
task,
|
|
27669
27842
|
text,
|
|
27670
|
-
logger
|
|
27843
|
+
logger,
|
|
27844
|
+
runtimeResponder: (prompt, resultMarker) => generateRuntimeReply(pluginConfig, prompt, resultMarker)
|
|
27671
27845
|
}).catch((err) => {
|
|
27672
27846
|
const msg = err instanceof Error ? err.message : String(err);
|
|
27673
27847
|
logger.error(`[perkos-a2a] Agentic task handler failed: ${msg}`);
|
|
@@ -27805,7 +27979,7 @@ function register(api) {
|
|
|
27805
27979
|
});
|
|
27806
27980
|
api.registerTool({
|
|
27807
27981
|
name: "perkos_a2a_send",
|
|
27808
|
-
description: "Send a task to another agent
|
|
27982
|
+
description: "Send a task to another agent via PerkOS A2A. Use this to delegate work, ask a peer to research, or route a chain/circular workflow. The message may name additional agents and a final notification target; the bridge preserves the route and prevents loops.",
|
|
27809
27983
|
parameters: {
|
|
27810
27984
|
type: "object",
|
|
27811
27985
|
properties: {
|
|
@@ -27815,7 +27989,7 @@ function register(api) {
|
|
|
27815
27989
|
},
|
|
27816
27990
|
message: {
|
|
27817
27991
|
type: "string",
|
|
27818
|
-
description: "The task message to send to
|
|
27992
|
+
description: "The task message to send. You can include routes like 'Apollo -> OpenClaw -> Hermes -> Morpheus' or natural language such as 'ask Hermes to research X, then notify Morpheus'."
|
|
27819
27993
|
}
|
|
27820
27994
|
},
|
|
27821
27995
|
required: ["target", "message"]
|