Skip to content

Commit 4cba08d

Browse files
authored
fix(whatsapp): remove exposeErrorText config (#74642)
* fix(whatsapp): remove exposeErrorText config * fix(whatsapp): mark internal system events trusted
1 parent 426107d commit 4cba08d

15 files changed

Lines changed: 13 additions & 149 deletions

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ Docs: https://docs.openclaw.ai
230230
- Channels/WhatsApp: detect explicit group `@mentions` again when the bot's own E.164 is in `allowFrom`, so shared-number setups no longer skip group pings that directly mention the bot. Fixes #49317. (#73453) Thanks @juan-flores077.
231231
- WhatsApp/reliability: publish real transport-liveness into WhatsApp channel status and force earlier reconnects on silent transport stalls, so quiet healthy sessions stay connected while wedged sockets recover before the later remote 408 path. (#72656) Thanks @Sathvik-1007.
232232
- Core/channels: tighten selected runtime, media, and plugin edge-case handling while preserving existing behavior. Thanks @jesse-merhi.
233-
- Channels/WhatsApp: strip leaked plural tool-call XML wrappers on every WhatsApp-visible outbound path and allow `channels.whatsapp.exposeErrorText` to suppress visible error text per channel or account. (#71830) Thanks @rubencu.
233+
- Channels/WhatsApp: strip leaked plural tool-call XML wrappers on every WhatsApp-visible outbound path and keep channel error payloads out of WhatsApp chats. (#71830) Thanks @rubencu.
234234
- Agents/embedded-runner: inject the resolved OAuth bearer (and forward the run abort signal) on the boundary-aware embedded stream fallback so models that route through `openai-codex-responses` and other boundary-aware transports stop failing with `401 Unauthorized: Missing bearer or basic authentication in header`. Fixes #73559. (#73588) Thanks @openperf.
235235
- Telegram/gateway: bound outbound Bot API calls and cache bundled plugin alias lookup so slow Telegram sends or WSL2 filesystem scans no longer wedge gateway replies. (#74210) Thanks @obviyus.
236236
- Configure/GitHub Copilot: reuse existing Copilot auth during configure and show the provider's manifest model catalog in the model picker. (#74276) Thanks @obviyus.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
bfd8b3ddcac047e486e9c43fdedc002cb9bf87b659f6563f9f11c850c5b2aaef config-baseline.json
1+
2af6bef21f530dc64e0379f7631bed410aee1d5c86604ef9fb149f546cfcb0e8 config-baseline.json
22
8d75df355b7f6e44b9c2f195d9df86130beb697e26061469df7d60b7e8a2f204 config-baseline.core.json
3-
9f5fad66a49fa618d64a963470aa69fed9fe4b4639cc4321f9ec04bfb2f8aa50 config-baseline.channel.json
3+
fab66aa304db5697e87259165ad261006719eb6e6cdbd25f957fcba2b7b324e9 config-baseline.channel.json
44
c4231c2194206547af8ad94342dc00aadb734f43cb49cc79d4c46bdbb80c3f95 config-baseline.plugin.json

docs/channels/whatsapp.md

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -398,22 +398,6 @@ When the linked self number is also present in `allowFrom`, WhatsApp self-chat s
398398
</Accordion>
399399
</AccordionGroup>
400400

401-
## Error visibility
402-
403-
`channels.whatsapp.exposeErrorText` controls whether agent/provider error text is delivered back into WhatsApp. The default is `true`. Set it to `false` to keep failures quiet on WhatsApp while preserving other channel behavior.
404-
405-
```json5
406-
{
407-
channels: {
408-
whatsapp: {
409-
exposeErrorText: false,
410-
},
411-
},
412-
}
413-
```
414-
415-
Per-account overrides use `channels.whatsapp.accounts.<id>.exposeErrorText`.
416-
417401
## Reply quoting
418402

419403
WhatsApp supports native reply quoting, where outbound replies visibly quote the inbound message. Control it with `channels.whatsapp.replyToMode`.
@@ -681,7 +665,7 @@ Primary reference:
681665
High-signal WhatsApp fields:
682666

683667
- access: `dmPolicy`, `allowFrom`, `groupPolicy`, `groupAllowFrom`, `groups`
684-
- delivery: `textChunkLimit`, `chunkMode`, `mediaMaxMb`, `sendReadReceipts`, `ackReaction`, `reactionLevel`, `exposeErrorText`
668+
- delivery: `textChunkLimit`, `chunkMode`, `mediaMaxMb`, `sendReadReceipts`, `ackReaction`, `reactionLevel`
685669
- multi-account: `accounts.<id>.enabled`, `accounts.<id>.authDir`, account-level overrides
686670
- operations: `configWrites`, `debounceMs`, `web.enabled`, `web.heartbeatSeconds`, `web.reconnect.*`, `web.whatsapp.*`
687671
- session behavior: `session.dmScope`, `historyLimit`, `dmHistoryLimit`, `dms.<id>.historyLimit`

extensions/whatsapp/src/accounts.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ export type ResolvedWhatsAppAccount = {
4545
direct?: WhatsAppAccountConfig["direct"];
4646
debounceMs?: number;
4747
replyToMode?: ReplyToMode;
48-
exposeErrorText?: boolean;
4948
};
5049

5150
export const DEFAULT_WHATSAPP_MEDIA_MAX_MB = 50;
@@ -157,7 +156,6 @@ export function resolveWhatsAppAccount(params: {
157156
direct: merged.direct,
158157
debounceMs: merged.debounceMs,
159158
replyToMode: merged.replyToMode,
160-
exposeErrorText: merged.exposeErrorText,
161159
};
162160
}
163161

extensions/whatsapp/src/auto-reply/monitor.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,6 @@ export async function monitorWebChannel(
176176
mediaMaxMb: account.mediaMaxMb,
177177
blockStreaming: account.blockStreaming,
178178
groups: account.groups,
179-
exposeErrorText: account.exposeErrorText,
180179
},
181180
},
182181
} satisfies ReturnType<typeof getRuntimeConfig>;
@@ -444,6 +443,7 @@ export async function monitorWebChannel(
444443
});
445444
enqueueSystemEvent(`WhatsApp gateway connected${selfE164 ? ` as ${selfE164}` : ""}.`, {
446445
sessionKey: connectRoute.sessionKey,
446+
trusted: true,
447447
});
448448

449449
const normalizedAccountId = normalizeReconnectAccountId(account.accountId);
@@ -503,6 +503,7 @@ export async function monitorWebChannel(
503503
`WhatsApp gateway disconnected (status ${decision.normalized.statusLabel})`,
504504
{
505505
sessionKey: connectRoute.sessionKey,
506+
trusted: true,
506507
},
507508
);
508509

extensions/whatsapp/src/auto-reply/monitor/inbound-dispatch.test.ts

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -522,43 +522,11 @@ describe("whatsapp inbound dispatch", () => {
522522
expect(rememberSentText).not.toHaveBeenCalled();
523523
});
524524

525-
it("suppresses error payload text when exposeErrorText is false", async () => {
525+
it("suppresses error payload text", async () => {
526526
const deliverReply = vi.fn(async () => undefined);
527527
const rememberSentText = vi.fn();
528528

529-
await dispatchBufferedReply({
530-
cfg: { channels: { whatsapp: { exposeErrorText: false } } } as never,
531-
deliverReply,
532-
rememberSentText,
533-
});
534-
535-
const deliver = getCapturedDeliver();
536-
expect(deliver).toBeTypeOf("function");
537-
538-
await deliver?.({ text: "provider exploded", isError: true }, { kind: "final" });
539-
540-
expect(deliverReply).not.toHaveBeenCalled();
541-
expect(rememberSentText).not.toHaveBeenCalled();
542-
});
543-
544-
it("honors account-level exposeErrorText overrides for error payloads", async () => {
545-
const deliverReply = vi.fn(async () => undefined);
546-
const rememberSentText = vi.fn();
547-
548-
await dispatchBufferedReply({
549-
cfg: {
550-
channels: {
551-
whatsapp: {
552-
accounts: {
553-
work: { exposeErrorText: false },
554-
},
555-
},
556-
},
557-
} as never,
558-
deliverReply,
559-
rememberSentText,
560-
route: makeRoute({ accountId: "work" }),
561-
});
529+
await dispatchBufferedReply({ deliverReply, rememberSentText });
562530

563531
const deliver = getCapturedDeliver();
564532
expect(deliver).toBeTypeOf("function");

extensions/whatsapp/src/auto-reply/monitor/inbound-dispatch.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { resolveMergedWhatsAppAccountConfig } from "../../account-config.js";
21
import {
32
type DeliverableWhatsAppOutboundPayload,
43
normalizeWhatsAppOutboundPayload,
@@ -89,12 +88,11 @@ function resolveWhatsAppDisableBlockStreaming(cfg: ReturnType<LoadConfigFn>): bo
8988
function resolveWhatsAppDeliverablePayload(
9089
payload: ReplyPayload,
9190
info: { kind: ReplyLifecycleKind },
92-
options?: { exposeErrorText?: boolean },
9391
): ReplyPayload | null {
9492
if (payload.isReasoning === true || payload.isCompactionNotice === true) {
9593
return null;
9694
}
97-
if (payload.isError === true && options?.exposeErrorText === false) {
95+
if (payload.isError === true) {
9896
return null;
9997
}
10098
if (info.kind === "tool") {
@@ -314,9 +312,6 @@ export async function dispatchWhatsAppBufferedReply(params: {
314312
});
315313
const mediaLocalRoots = getAgentScopedMediaLocalRoots(params.cfg, params.route.agentId);
316314
const disableBlockStreaming = resolveWhatsAppDisableBlockStreaming(params.cfg);
317-
const exposeErrorText =
318-
resolveMergedWhatsAppAccountConfig({ cfg: params.cfg, accountId: params.route.accountId })
319-
.exposeErrorText !== false;
320315
let didSendReply = false;
321316
let didLogHeartbeatStrip = false;
322317

@@ -333,9 +328,7 @@ export async function dispatchWhatsAppBufferedReply(params: {
333328
}
334329
},
335330
deliver: async (payload: ReplyPayload, info: { kind: ReplyLifecycleKind }) => {
336-
const deliveryPayload = resolveWhatsAppDeliverablePayload(payload, info, {
337-
exposeErrorText,
338-
});
331+
const deliveryPayload = resolveWhatsAppDeliverablePayload(payload, info);
339332
if (!deliveryPayload) {
340333
return;
341334
}

extensions/whatsapp/src/config-schema.test.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,6 @@ describe("whatsapp config schema", () => {
6363
}
6464
});
6565

66-
it("accepts exposeErrorText at channel and account scope", () => {
67-
const res = expectWhatsAppConfigValid({
68-
exposeErrorText: false,
69-
accounts: {
70-
work: { exposeErrorText: true },
71-
},
72-
});
73-
74-
if (res.success) {
75-
expect(res.data.exposeErrorText).toBe(false);
76-
expect(res.data.accounts?.work?.exposeErrorText).toBe(true);
77-
}
78-
});
79-
8066
it("accepts enabled", () => {
8167
expectWhatsAppConfigValid({
8268
enabled: true,

extensions/whatsapp/src/config-ui-hints.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,4 @@ export const whatsAppChannelConfigUiHints = {
2121
label: "WhatsApp Config Writes",
2222
help: "Allow WhatsApp to write config in response to channel events/commands (default: true).",
2323
},
24-
exposeErrorText: {
25-
label: "WhatsApp Error Text",
26-
help: "Deliver user-visible agent/provider error text into WhatsApp (default: true). Disable to keep failures quiet on WhatsApp.",
27-
},
2824
} satisfies Record<string, ChannelConfigUiHint>;

extensions/whatsapp/src/outbound-adapter.sendpayload.test.ts

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,11 @@ describe("whatsappOutbound sendPayload", () => {
167167
expect(sendWhatsApp).not.toHaveBeenCalled();
168168
});
169169

170-
it("suppresses routed error payloads when error text is hidden", async () => {
170+
it("suppresses routed error payloads", async () => {
171171
const sendWhatsApp = vi.fn();
172172

173173
const result = await whatsappOutbound.sendPayload!({
174-
cfg: { channels: { whatsapp: { exposeErrorText: false } } },
174+
cfg: {},
175175
to: "5511999999999@c.us",
176176
text: "",
177177
payload: { text: "provider exploded", isError: true },
@@ -182,35 +182,6 @@ describe("whatsappOutbound sendPayload", () => {
182182
expect(sendWhatsApp).not.toHaveBeenCalled();
183183
});
184184

185-
it("uses account-level error text visibility for routed payloads", async () => {
186-
const sendWhatsApp = vi.fn(async () => ({ messageId: "wa-1", toJid: "jid" }));
187-
188-
await whatsappOutbound.sendPayload!({
189-
cfg: {
190-
channels: {
191-
whatsapp: {
192-
exposeErrorText: false,
193-
accounts: {
194-
work: { exposeErrorText: true },
195-
},
196-
},
197-
},
198-
},
199-
accountId: "work",
200-
to: "5511999999999@c.us",
201-
text: "",
202-
payload: { text: "provider exploded", isError: true },
203-
deps: { sendWhatsApp },
204-
});
205-
206-
expect(sendWhatsApp).toHaveBeenCalledTimes(1);
207-
expect(sendWhatsApp).toHaveBeenCalledWith(
208-
"5511999999999@c.us",
209-
"provider exploded",
210-
expect.any(Object),
211-
);
212-
});
213-
214185
it("sanitizes HTML-only text to whitespace-only payload", () => {
215186
expect(
216187
whatsappOutbound

0 commit comments

Comments
 (0)