@perkos/perkos-a2a 0.8.24 → 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 +270 -65
- package/dist/agentic-actions.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +237 -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,64 +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);
|
|
27453
27571
|
}
|
|
27454
27572
|
function cleanNestedResult(text, finalMarker) {
|
|
27455
27573
|
const escaped = finalMarker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
27456
|
-
return text.replace(new RegExp(
|
|
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));
|
|
27457
27585
|
}
|
|
27458
27586
|
function buildDelegatedMessage(input) {
|
|
27459
|
-
const { originalText, selfName, target, marker } = input;
|
|
27587
|
+
const { originalText, selfName, target, route, marker, notify } = input;
|
|
27460
27588
|
const depth = delegationDepth(originalText) + 1;
|
|
27461
27589
|
const clean = stripDelegationNoise(originalText);
|
|
27462
|
-
const header = `A2A-DELEGATION origin=${selfName} target=${target} depth=${depth}`;
|
|
27463
|
-
if (hasA2ARelayExplainTask(clean)) {
|
|
27464
|
-
if (target === "Perkos-Hermes-Tester") {
|
|
27465
|
-
return [
|
|
27466
|
-
header,
|
|
27467
|
-
"Complete this task directly. Briefly explain what an A2A relay is and why agents use it.",
|
|
27468
|
-
"Do not delegate this request further unless explicitly required by a new instruction.",
|
|
27469
|
-
"End with DELEGATED_A2A_DONE."
|
|
27470
|
-
].join("\n");
|
|
27471
|
-
}
|
|
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)) {
|
|
27472
27592
|
return [
|
|
27473
27593
|
header,
|
|
27474
|
-
"
|
|
27475
|
-
"
|
|
27476
|
-
|
|
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."
|
|
27477
27597
|
].join("\n");
|
|
27478
27598
|
}
|
|
27479
27599
|
return [
|
|
27480
27600
|
header,
|
|
27481
|
-
"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.",
|
|
27482
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.",
|
|
27483
27604
|
`End exactly with ${marker}.`
|
|
27484
27605
|
].join("\n");
|
|
27485
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
|
+
}
|
|
27486
27643
|
async function tryHandleAgenticTask(input) {
|
|
27487
|
-
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);
|
|
27488
27647
|
const cleanText = stripDelegationNoise(text);
|
|
27489
|
-
const wantsDelegation = includesAny(cleanText, ["usa comunicaci\xF3n a2a", "usar comunicaci\xF3n a2a", "p\xEDdele", "dile a", "delegate", "delegar", "tell", "ask"]);
|
|
27490
|
-
const wantsEthResearch = includesAny(
|
|
27491
|
-
const
|
|
27492
|
-
if (
|
|
27493
|
-
|
|
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) {
|
|
27494
27656
|
completeTaskWithReply(task, [
|
|
27495
27657
|
`${selfName} received and completed the A2A communication test.`,
|
|
27496
|
-
|
|
27658
|
+
finalMarker
|
|
27497
27659
|
].join("\n"));
|
|
27498
27660
|
return true;
|
|
27499
27661
|
}
|
|
27500
|
-
if (hasA2ARelayExplainTask(cleanText) &&
|
|
27501
|
-
|
|
27502
|
-
completeTaskWithReply(task, [answerA2ARelay(selfName), "", marker].join("\n"));
|
|
27662
|
+
if (hasA2ARelayExplainTask(cleanText) && !wantsDelegation && !envelope.route.length) {
|
|
27663
|
+
completeTaskWithReply(task, [answerA2ARelay(selfName), "", finalMarker].join("\n"));
|
|
27503
27664
|
return true;
|
|
27504
27665
|
}
|
|
27505
|
-
if (wantsEthResearch &&
|
|
27666
|
+
if (wantsEthResearch && !wantsDelegation && !envelope.route.length) {
|
|
27506
27667
|
const research = await researchEthPrice();
|
|
27507
|
-
const replyTarget2 =
|
|
27668
|
+
const replyTarget2 = detectReplyTarget(text, selfName, directory);
|
|
27508
27669
|
let replyResultText2 = "";
|
|
27509
27670
|
if (replyTarget2) {
|
|
27510
27671
|
const replyResult = await server.sendTask(replyTarget2, [
|
|
@@ -27516,21 +27677,18 @@ async function tryHandleAgenticTask(input) {
|
|
|
27516
27677
|
].join("\n"));
|
|
27517
27678
|
replyResultText2 = extractTaskFinalText(replyResult);
|
|
27518
27679
|
}
|
|
27519
|
-
const marker = requestedMarker(text, "ETH_RESEARCH_DONE");
|
|
27520
27680
|
completeTaskWithReply(task, [
|
|
27521
27681
|
research,
|
|
27522
27682
|
"",
|
|
27523
27683
|
replyTarget2 ? `Forwarded to ${replyTarget2}.` : void 0,
|
|
27524
27684
|
replyResultText2 ? `Reply delivery result:
|
|
27525
27685
|
${replyResultText2}` : void 0,
|
|
27526
|
-
|
|
27686
|
+
finalMarker
|
|
27527
27687
|
].filter(Boolean).join("\n"));
|
|
27528
27688
|
return true;
|
|
27529
27689
|
}
|
|
27530
|
-
if (!wantsDelegation) return false;
|
|
27531
|
-
|
|
27532
|
-
if (!target) return false;
|
|
27533
|
-
if (delegationDepth(text) >= 4) {
|
|
27690
|
+
if (!wantsDelegation && !envelope.route.length) return false;
|
|
27691
|
+
if (delegationDepth(text) >= Number(process.env.A2A_MAX_DELEGATION_DEPTH || 8)) {
|
|
27534
27692
|
completeTaskWithReply(task, [
|
|
27535
27693
|
`A2A delegation stopped by ${selfName}: max delegation depth reached.`,
|
|
27536
27694
|
"This prevents routing loops across heterogeneous agents.",
|
|
@@ -27538,22 +27696,32 @@ ${replyResultText2}` : void 0,
|
|
|
27538
27696
|
].join("\n"));
|
|
27539
27697
|
return true;
|
|
27540
27698
|
}
|
|
27541
|
-
const
|
|
27542
|
-
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
|
+
}
|
|
27543
27711
|
const delegatedMessage = wantsEthResearch ? [
|
|
27544
|
-
`A2A-DELEGATION origin=${selfName} target=${
|
|
27712
|
+
`A2A-DELEGATION origin=${selfName} target=${next} route=${route.join("|")} notify=${replyTarget || "none"} depth=${delegationDepth(text) + 1} marker=${finalMarker}`,
|
|
27545
27713
|
"Delegated research request. Research the current ETH price using available live/public APIs.",
|
|
27546
27714
|
"Return concise findings with source names, timestamp if available, and limitations.",
|
|
27547
|
-
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.",
|
|
27548
27716
|
"End with DELEGATED_ETH_RESEARCH_DONE."
|
|
27549
|
-
].filter(Boolean).join("\n") : buildDelegatedMessage({ originalText: text, selfName, target, marker: finalMarker });
|
|
27550
|
-
logger.info(`[perkos-a2a] Agentic delegation: ${selfName} -> ${
|
|
27551
|
-
const delegatedResult = await server.sendTask(
|
|
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);
|
|
27552
27720
|
const delegatedText = cleanNestedResult(extractTaskFinalText(delegatedResult), finalMarker);
|
|
27553
27721
|
let replyResultText = "";
|
|
27554
|
-
if (replyTarget && replyTarget !==
|
|
27722
|
+
if (replyTarget && replyTarget !== next && !delegatedText.includes("Forwarded final result to")) {
|
|
27555
27723
|
const replyMessage = [
|
|
27556
|
-
`Delegated result from ${
|
|
27724
|
+
`Delegated result from ${next}, forwarded by ${selfName}:`,
|
|
27557
27725
|
"",
|
|
27558
27726
|
delegatedText,
|
|
27559
27727
|
"",
|
|
@@ -27564,7 +27732,8 @@ ${replyResultText2}` : void 0,
|
|
|
27564
27732
|
}
|
|
27565
27733
|
completeTaskWithReply(task, [
|
|
27566
27734
|
`Agentic A2A delegation completed by ${selfName}.`,
|
|
27567
|
-
`Delegated target: ${
|
|
27735
|
+
`Delegated target: ${next}`,
|
|
27736
|
+
`Route: ${route.join(" -> ")}`,
|
|
27568
27737
|
replyTarget ? `Reply target: ${replyTarget}` : void 0,
|
|
27569
27738
|
"",
|
|
27570
27739
|
"Delegated result:",
|
|
@@ -27671,7 +27840,8 @@ function register(api) {
|
|
|
27671
27840
|
server,
|
|
27672
27841
|
task,
|
|
27673
27842
|
text,
|
|
27674
|
-
logger
|
|
27843
|
+
logger,
|
|
27844
|
+
runtimeResponder: (prompt, resultMarker) => generateRuntimeReply(pluginConfig, prompt, resultMarker)
|
|
27675
27845
|
}).catch((err) => {
|
|
27676
27846
|
const msg = err instanceof Error ? err.message : String(err);
|
|
27677
27847
|
logger.error(`[perkos-a2a] Agentic task handler failed: ${msg}`);
|
|
@@ -27809,7 +27979,7 @@ function register(api) {
|
|
|
27809
27979
|
});
|
|
27810
27980
|
api.registerTool({
|
|
27811
27981
|
name: "perkos_a2a_send",
|
|
27812
|
-
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.",
|
|
27813
27983
|
parameters: {
|
|
27814
27984
|
type: "object",
|
|
27815
27985
|
properties: {
|
|
@@ -27819,7 +27989,7 @@ function register(api) {
|
|
|
27819
27989
|
},
|
|
27820
27990
|
message: {
|
|
27821
27991
|
type: "string",
|
|
27822
|
-
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'."
|
|
27823
27993
|
}
|
|
27824
27994
|
},
|
|
27825
27995
|
required: ["target", "message"]
|