diff --git a/src/cron/isolated-agent.skips-delivery-without-whatsapp-recipient-besteffortdeliver-true.test.ts b/src/cron/isolated-agent.skips-delivery-without-whatsapp-recipient-besteffortdeliver-true.test.ts index f63c6b520..e9dceba63 100644 --- a/src/cron/isolated-agent.skips-delivery-without-whatsapp-recipient-besteffortdeliver-true.test.ts +++ b/src/cron/isolated-agent.skips-delivery-without-whatsapp-recipient-besteffortdeliver-true.test.ts @@ -192,6 +192,44 @@ async function runAnnounceFlowResult(bestEffort: boolean) { return outcome; } +async function runSignalAnnounceFlowResult(bestEffort: boolean) { + let outcome: + | { + res: Awaited>; + deps: CliDeps; + } + | undefined; + await withTempHome(async (home) => { + const storePath = await writeSessionStore(home, { lastProvider: "webchat", lastTo: "" }); + const deps = createCliDeps(); + mockAgentPayloads([{ text: "hello from cron" }]); + vi.mocked(runSubagentAnnounceFlow).mockResolvedValueOnce(false); + const res = await runCronIsolatedAgentTurn({ + cfg: makeCfg(home, storePath, { + channels: { signal: {} }, + }), + deps, + job: { + ...makeJob({ kind: "agentTurn", message: "do it" }), + delivery: { + mode: "announce", + channel: "signal", + to: "+15551234567", + bestEffort, + }, + }, + message: "do it", + sessionKey: "cron:job-1", + lane: "cron", + }); + outcome = { res, deps }; + }); + if (!outcome) { + throw new Error("signal announce flow did not produce an outcome"); + } + return outcome; +} + async function assertExplicitTelegramTargetAnnounce(params: { home: string; storePath: string; @@ -430,6 +468,15 @@ describe("runCronIsolatedAgentTurn", () => { expect(deps.sendMessageTelegram).toHaveBeenCalledTimes(1); }); + it("falls back to direct delivery for signal when announce reports false and best-effort is enabled", async () => { + const { res, deps } = await runSignalAnnounceFlowResult(true); + expect(res.status).toBe("ok"); + expect(res.delivered).toBe(true); + expect(res.deliveryAttempted).toBe(true); + expect(runSubagentAnnounceFlow).toHaveBeenCalledTimes(1); + expect(deps.sendMessageSignal).toHaveBeenCalledTimes(1); + }); + it("falls back to direct delivery when announce flow throws and best-effort is disabled", async () => { await withTempHome(async (home) => { const storePath = await writeSessionStore(home, { lastProvider: "webchat", lastTo: "" }); diff --git a/src/cron/isolated-agent.test-setup.ts b/src/cron/isolated-agent.test-setup.ts index 151b37dd1..6a776b323 100644 --- a/src/cron/isolated-agent.test-setup.ts +++ b/src/cron/isolated-agent.test-setup.ts @@ -2,6 +2,7 @@ import { vi } from "vitest"; import { loadModelCatalog } from "../agents/model-catalog.js"; import { runEmbeddedPiAgent } from "../agents/pi-embedded.js"; import { runSubagentAnnounceFlow } from "../agents/subagent-announce.js"; +import { signalOutbound } from "../channels/plugins/outbound/signal.js"; import { telegramOutbound } from "../channels/plugins/outbound/telegram.js"; import { setActivePluginRegistry } from "../plugins/runtime.js"; import { createOutboundTestPlugin, createTestRegistry } from "../test-utils/channel-plugins.js"; @@ -20,6 +21,11 @@ export function setupIsolatedAgentTurnMocks(params?: { fast?: boolean }): void { plugin: createOutboundTestPlugin({ id: "telegram", outbound: telegramOutbound }), source: "test", }, + { + pluginId: "signal", + plugin: createOutboundTestPlugin({ id: "signal", outbound: signalOutbound }), + source: "test", + }, ]), ); }