From f299bb812b95a91654c2fd87206d43ecd48ca00f Mon Sep 17 00:00:00 2001 From: Altay Date: Sun, 5 Apr 2026 03:09:28 +0300 Subject: [PATCH] test(agents): stabilize announce cleanup assertions (#61088) * test(plugin-sdk): use telegram public config seam * test(agents): stabilize announce cleanup assertions --- extensions/telegram/channel-config-api.ts | 1 + src/agents/pi-tools-agent-config.test.ts | 4 +- src/agents/subagent-announce.test.ts | 28 +++- .../subagent-registry.steer-restart.test.ts | 121 ++++++++++-------- .../telegram-command-config.test.ts | 2 +- 5 files changed, 97 insertions(+), 59 deletions(-) diff --git a/extensions/telegram/channel-config-api.ts b/extensions/telegram/channel-config-api.ts index 77354ec5098..acafccd81fa 100644 --- a/extensions/telegram/channel-config-api.ts +++ b/extensions/telegram/channel-config-api.ts @@ -1,4 +1,5 @@ export { + TELEGRAM_COMMAND_NAME_PATTERN, normalizeTelegramCommandDescription, normalizeTelegramCommandName, resolveTelegramCustomCommands, diff --git a/src/agents/pi-tools-agent-config.test.ts b/src/agents/pi-tools-agent-config.test.ts index 5cf2746e63a..3dd68c98f6d 100644 --- a/src/agents/pi-tools-agent-config.test.ts +++ b/src/agents/pi-tools-agent-config.test.ts @@ -727,7 +727,7 @@ describe("Agent-specific tool filtering", () => { command: "echo done", host: "sandbox", }), - ).rejects.toThrow("exec host not allowed"); + ).rejects.toThrow(/requires a sandbox runtime/); }); it("should apply agent-specific exec host defaults over global defaults", async () => { @@ -777,7 +777,7 @@ describe("Agent-specific tool filtering", () => { host: "sandbox", yieldMs: 1000, }), - ).rejects.toThrow("exec host not allowed"); + ).rejects.toThrow(/requires a sandbox runtime/); }); it("applies explicit agentId exec defaults when sessionKey is opaque", async () => { diff --git a/src/agents/subagent-announce.test.ts b/src/agents/subagent-announce.test.ts index 426efde5736..2822e266e72 100644 --- a/src/agents/subagent-announce.test.ts +++ b/src/agents/subagent-announce.test.ts @@ -103,7 +103,10 @@ vi.mock("./subagent-announce-delivery.js", () => ({ params: { sessionKey: params.targetRequesterSessionKey, message: params.triggerMessage, - deliver: false, + deliver: + !params.requesterIsSubagent && + params.requesterOrigin?.channel !== "webchat" && + Boolean(params.requesterOrigin?.channel && params.requesterOrigin?.to), bestEffortDeliver: params.bestEffortDeliver, ...(params.requesterIsSubagent ? {} @@ -127,8 +130,27 @@ vi.mock("./subagent-announce-delivery.js", () => ({ const store = loadSessionStoreMock("/tmp/sessions.json") as Record; return store?.[sessionKey] ?? { sessionId: sessionKey }; }, - resolveAnnounceOrigin: (entry: { origin?: unknown } | undefined, requesterOrigin?: unknown) => - requesterOrigin ?? entry?.origin, + resolveAnnounceOrigin: ( + entry: + | { + lastChannel?: string; + lastTo?: string; + lastAccountId?: string; + lastThreadId?: string; + origin?: { provider?: string; channel?: string; accountId?: string }; + } + | undefined, + requesterOrigin?: { channel?: string; to?: string; accountId?: string; threadId?: string }, + ) => ({ + channel: + requesterOrigin?.channel ?? + entry?.lastChannel ?? + entry?.origin?.provider ?? + entry?.origin?.channel, + to: requesterOrigin?.to ?? entry?.lastTo, + accountId: requesterOrigin?.accountId ?? entry?.lastAccountId ?? entry?.origin?.accountId, + threadId: requesterOrigin?.threadId ?? entry?.lastThreadId, + }), resolveSubagentCompletionOrigin: async (params: { requesterOrigin?: unknown }) => params.requesterOrigin, resolveSubagentAnnounceTimeoutMs: () => 10_000, diff --git a/src/agents/subagent-registry.steer-restart.test.ts b/src/agents/subagent-registry.steer-restart.test.ts index 6ff488b3916..6fb58f73e18 100644 --- a/src/agents/subagent-registry.steer-restart.test.ts +++ b/src/agents/subagent-registry.steer-restart.test.ts @@ -263,9 +263,12 @@ describe("subagent registry steer restarts", () => { emitLifecycleEnd("run-new"); - await flushAnnounce(); - expect(announceSpy).toHaveBeenCalledTimes(1); - expect(runSubagentEndedHookMock).toHaveBeenCalledTimes(1); + await vi.waitFor(() => { + expect(announceSpy).toHaveBeenCalledTimes(1); + }); + await vi.waitFor(() => { + expect(runSubagentEndedHookMock).toHaveBeenCalledTimes(1); + }); expect(runSubagentEndedHookMock).toHaveBeenCalledWith( expect.objectContaining({ runId: "run-new", @@ -338,63 +341,75 @@ describe("subagent registry steer restarts", () => { }); }); - it("clears announce retry state when replacing after steer restart", () => { - registerRun({ - runId: "run-retry-reset-old", - childSessionKey: "agent:main:subagent:retry-reset", - task: "retry reset", - }); + it("clears announce retry state when replacing after steer restart", async () => { + await withPendingAgentWait(async () => { + registerRun({ + runId: "run-retry-reset-old", + childSessionKey: "agent:main:subagent:retry-reset", + task: "retry reset", + }); - const previous = listMainRuns()[0]; - expect(previous?.runId).toBe("run-retry-reset-old"); - if (previous) { - previous.announceRetryCount = 2; - previous.lastAnnounceRetryAt = Date.now(); - } + const previous = listMainRuns()[0]; + expect(previous?.runId).toBe("run-retry-reset-old"); + if (previous) { + previous.announceRetryCount = 2; + previous.lastAnnounceRetryAt = Date.now(); + } - const run = replaceRunAfterSteer({ - previousRunId: "run-retry-reset-old", - nextRunId: "run-retry-reset-new", - fallback: previous, + const run = replaceRunAfterSteer({ + previousRunId: "run-retry-reset-old", + nextRunId: "run-retry-reset-new", + fallback: previous, + }); + expect(run.announceRetryCount).toBeUndefined(); + expect(run.lastAnnounceRetryAt).toBeUndefined(); }); - expect(run.announceRetryCount).toBeUndefined(); - expect(run.lastAnnounceRetryAt).toBeUndefined(); }); it("clears terminal lifecycle state when replacing after steer restart", async () => { - registerRun({ - runId: "run-terminal-state-old", - childSessionKey: "agent:main:subagent:terminal-state", - task: "terminal state", + await withPendingAgentWait(async () => { + registerRun({ + runId: "run-terminal-state-old", + childSessionKey: "agent:main:subagent:terminal-state", + task: "terminal state", + }); + + const previous = listMainRuns()[0]; + expect(previous?.runId).toBe("run-terminal-state-old"); + if (previous) { + previous.endedHookEmittedAt = Date.now(); + previous.endedReason = "subagent-complete"; + previous.endedAt = Date.now(); + previous.outcome = { status: "ok" }; + } + + const run = replaceRunAfterSteer({ + previousRunId: "run-terminal-state-old", + nextRunId: "run-terminal-state-new", + fallback: previous, + }); + expect(run.endedHookEmittedAt).toBeUndefined(); + expect(run.endedReason).toBeUndefined(); + + emitLifecycleEnd("run-terminal-state-new"); + + await vi.waitFor(() => { + expect(runSubagentEndedHookMock).toHaveBeenCalledWith( + expect.objectContaining({ + runId: "run-terminal-state-new", + }), + expect.objectContaining({ + runId: "run-terminal-state-new", + }), + ); + }); + expect(emitSessionLifecycleEventMock).toHaveBeenCalledWith( + expect.objectContaining({ + sessionKey: "agent:main:subagent:terminal-state", + reason: "subagent-status", + }), + ); }); - - const previous = listMainRuns()[0]; - expect(previous?.runId).toBe("run-terminal-state-old"); - if (previous) { - previous.endedHookEmittedAt = Date.now(); - previous.endedReason = "subagent-complete"; - previous.endedAt = Date.now(); - previous.outcome = { status: "ok" }; - } - - const run = replaceRunAfterSteer({ - previousRunId: "run-terminal-state-old", - nextRunId: "run-terminal-state-new", - fallback: previous, - }); - expect(run.endedHookEmittedAt).toBeUndefined(); - expect(run.endedReason).toBeUndefined(); - - emitLifecycleEnd("run-terminal-state-new"); - - await flushAnnounce(); - expect(runSubagentEndedHookMock).not.toHaveBeenCalled(); - expect(emitSessionLifecycleEventMock).toHaveBeenCalledWith( - expect.objectContaining({ - sessionKey: "agent:main:subagent:terminal-state", - reason: "subagent-status", - }), - ); }); it("clears frozen completion fields when replacing after steer restart", () => { diff --git a/src/plugin-sdk/telegram-command-config.test.ts b/src/plugin-sdk/telegram-command-config.test.ts index 136edb67140..2ece9d7c496 100644 --- a/src/plugin-sdk/telegram-command-config.test.ts +++ b/src/plugin-sdk/telegram-command-config.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from "vitest"; -import { TELEGRAM_COMMAND_NAME_PATTERN as bundledTelegramCommandNamePattern } from "../../extensions/telegram/src/command-config.ts"; +import { TELEGRAM_COMMAND_NAME_PATTERN as bundledTelegramCommandNamePattern } from "../../extensions/telegram/channel-config-api.ts"; type BundledChannelContractSurfaceParams = Parameters< (typeof import("../channels/plugins/contract-surfaces.js"))["getBundledChannelContractSurfaceModule"]