Skip to content

Commit 426774a

Browse files
committed
refactor(agents): drop dead cron-channel A2A arm, gate on canonical key only (#92257)
The requesterChannel === "cron" arm was unreachable: agentChannel is always a DeliverableMessageChannel from resolveGatewayMessageChannel(messageProvider), never the literal "cron". The channel: "cron" in src/cron/isolated-agent/run.ts labels diagnostics events/lifecycle, not the tool channel. Gate on isCronRunSessionKey alone and fix the misleading comment. Tests drove the dead arm via agentChannel/requesterChannel "cron" plus a non-canonical key (agent:main:cron:run:abc, which isCronRunSessionKey rejects). Switch them to a canonical cron-run key (agent:main:cron:job:run:abc) and a normal delivery channel so they exercise the real production gate.
1 parent 2cb4865 commit 426774a

3 files changed

Lines changed: 13 additions & 13 deletions

File tree

src/agents/tools/sessions-send-tool.a2a.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ describe("runSessionsSendA2AFlow announce delivery", () => {
340340
});
341341

342342
it("with maxPingPongTurns 0 and requester != target, never steps the requester session but still announces in the target (#92257)", async () => {
343-
const requesterSessionKey = "agent:main:cron:run:abc";
343+
const requesterSessionKey = "agent:main:cron:job:run:abc";
344344
const targetSessionKey = "agent:other:discord:group:ops";
345345

346346
await runSessionsSendA2AFlow({
@@ -350,7 +350,7 @@ describe("runSessionsSendA2AFlow announce delivery", () => {
350350
announceTimeoutMs: 10_000,
351351
maxPingPongTurns: 0,
352352
requesterSessionKey,
353-
requesterChannel: "cron",
353+
requesterChannel: "telegram",
354354
roundOneReply: "Worker completed successfully",
355355
});
356356

@@ -381,8 +381,8 @@ describe("runSessionsSendA2AFlow announce delivery", () => {
381381
message: "Test message",
382382
announceTimeoutMs: 10_000,
383383
maxPingPongTurns: 0,
384-
requesterSessionKey: "agent:main:cron:run:abc",
385-
requesterChannel: "cron",
384+
requesterSessionKey: "agent:main:cron:job:run:abc",
385+
requesterChannel: "telegram",
386386
baseline: {
387387
text: "pre-existing cron output",
388388
fingerprint: "pre-existing-cron-output",

src/agents/tools/sessions-send-tool.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -557,13 +557,11 @@ export function createSessionsSendTool(opts?: {
557557
const sameSessionA2A = requesterSessionKey === resolvedKey;
558558
// An isolated cron run is the only requester for which the A2A ping-pong
559559
// is harmful: feeding the target's reply back as a new turn corrupts the
560-
// isolated run (#92257). Detect it with the canonical cron-run classifier
561-
// and the cron channel marker (see src/cron/isolated-agent/run.ts), not a
562-
// raw `:cron:` substring: a normal requester whose key merely contains
563-
// that segment (e.g. agent:main:slack:cron:job:run:uuid) must keep its
564-
// intended cross-session ping-pong.
565-
const isIsolatedCronRequester =
566-
isCronRunSessionKey(requesterSessionKey) || requesterChannel === "cron";
560+
// isolated run (#92257). Detect it with the canonical cron-run classifier,
561+
// not a raw `:cron:` substring: a normal requester whose key merely
562+
// contains that segment (e.g. agent:main:slack:cron:job:run:uuid) must
563+
// keep its intended cross-session ping-pong.
564+
const isIsolatedCronRequester = isCronRunSessionKey(requesterSessionKey);
567565

568566
// Capture the pre-run assistant snapshot before starting the nested run.
569567
// Fast in-process test doubles and short-circuit agent paths can finish

src/agents/tools/sessions.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,7 +1112,9 @@ describe("sessions_send gating", () => {
11121112
it("forces maxPingPongTurns to 0 for fire-and-forget sends from an isolated cron requester (#92257)", async () => {
11131113
const { runSessionsSendA2AFlow } = await import("./sessions-send-tool.a2a.js");
11141114
vi.mocked(runSessionsSendA2AFlow).mockClear();
1115-
const requesterSessionKey = "agent:main:cron:run:abc";
1115+
// Canonical isolated-cron run key (agent:<id>:cron:<job>:run:<run>); the
1116+
// gate fires on isCronRunSessionKey alone, with a normal delivery channel.
1117+
const requesterSessionKey = "agent:main:cron:job:run:abc";
11161118
const targetSessionKey = "agent:other:discord:group:ops";
11171119
loadConfigMock.mockReturnValue({
11181120
session: { scope: "per-sender", mainKey: "main", agentToAgent: { maxPingPongTurns: 5 } },
@@ -1123,7 +1125,7 @@ describe("sessions_send gating", () => {
11231125
});
11241126
const tool = createSessionsSendTool({
11251127
agentSessionKey: requesterSessionKey,
1126-
agentChannel: "cron",
1128+
agentChannel: "telegram",
11271129
});
11281130

11291131
callGatewayMock.mockImplementation(async (opts: unknown) => {

0 commit comments

Comments
 (0)