From 372e270871a2e45be1484df57651f1a1891610bb Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 7 May 2026 01:03:11 +0100 Subject: [PATCH] fix(delivery): require outbound send result for success --- CHANGELOG.md | 1 + src/agents/command/delivery.test.ts | 18 +++++++++++++++++- src/agents/command/delivery.ts | 4 ++-- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 319a51b80d2..5ceafc3edcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -127,6 +127,7 @@ Docs: https://docs.openclaw.ai - CLI/completion: guard the shell-profile source line written by `openclaw completion --install` with a file existence check (`[ -f ... ] && source ...` for bash/zsh, `test -f ...; and source ...` for fish) so uninstalling OpenClaw no longer makes new login shells error on a missing completion cache. (#78659) Thanks @sjf. - Cron/doctor: repair persisted cron jobs whose `payload.model` was stored as `"default"`, `"null"`, blank, or JSON `null` by removing the bad override during `openclaw doctor --fix` while keeping cron runtime model validation strict. Fixes #78549. Thanks @bizzle12368239. - Telegram: honor `accessGroup:*` sender allowlists for DMs, groups, native commands, and callback authorization before applying Telegram's numeric sender-ID checks. Fixes #78660. Thanks @manugc. +- Agent delivery: report `deliverySucceeded=false` when outbound delivery returns no adapter result, so claimed/empty delivery paths no longer masquerade as successful sends. Fixes #78532. Thanks @joeyfrasier. - Doctor/OpenAI Codex: revert the 2026.5.5 `doctor --fix` repair that rewrote valid `openai-codex/*` ChatGPT/Codex OAuth routes to `openai/*`, which could break OAuth-only GPT-5.5 setups or accidentally move users onto the OpenAI API-key route. If 2026.5.5 already changed your default model, run `openclaw models set openai-codex/gpt-5.5 && openclaw config validate` to switch the default agent back to the Codex OAuth PI route. Fixes #78407. - Discord/groups: instruct group-chat agents to stay silent when a message is addressed to someone else, replying only when invited or correcting key facts. (#78615) - Discord/groups: tell Discord-channel agents to wrap bare URLs as `` so link previews do not expand into uninvited embeds. (#78614) diff --git a/src/agents/command/delivery.test.ts b/src/agents/command/delivery.test.ts index 2e82a135ead..8168d6722d9 100644 --- a/src/agents/command/delivery.test.ts +++ b/src/agents/command/delivery.test.ts @@ -216,7 +216,12 @@ describe("normalizeAgentCommandReplyPayloads", () => { }); it("reports successful requested delivery", async () => { - deliverOutboundPayloadsMock.mockResolvedValue([]); + deliverOutboundPayloadsMock.mockResolvedValue([ + { + channel: "slack", + messageId: "m1", + }, + ]); const delivered = await deliverMediaReplyForTest({ key: "agent:tester:slack:direct:alice", @@ -226,6 +231,17 @@ describe("normalizeAgentCommandReplyPayloads", () => { expect(delivered.deliverySucceeded).toBe(true); }); + it("does not report success when delivery claims no adapter result", async () => { + deliverOutboundPayloadsMock.mockResolvedValue([]); + + const delivered = await deliverMediaReplyForTest({ + key: "agent:tester:slack:direct:alice", + agentId: "tester", + } as never); + + expect(delivered.deliverySucceeded).toBe(false); + }); + it("does not report success when best-effort delivery records an error", async () => { deliverOutboundPayloadsMock.mockImplementationOnce(async (params: unknown) => { (params as { onError?: (err: unknown) => void }).onError?.(new Error("send failed")); diff --git a/src/agents/command/delivery.ts b/src/agents/command/delivery.ts index 2cad2a18179..5e334107a25 100644 --- a/src/agents/command/delivery.ts +++ b/src/agents/command/delivery.ts @@ -381,7 +381,7 @@ export async function deliverAgentCommandResult(params: { } if (deliver && deliveryChannel && !isInternalMessageChannel(deliveryChannel)) { if (deliveryTarget) { - await deliverOutboundPayloads({ + const deliveryResults = await deliverOutboundPayloads({ cfg, channel: deliveryChannel, to: deliveryTarget, @@ -395,7 +395,7 @@ export async function deliverAgentCommandResult(params: { onPayload: logPayload, deps: createOutboundSendDeps(deps), }); - deliverySucceeded = !deliveryHadError; + deliverySucceeded = deliveryResults.length > 0 && !deliveryHadError; } }