
















@@ -6,6 +6,7 @@ type CapturedReplyPayload = {
66text?: string;
77isReasoning?: boolean;
88isCompactionNotice?: boolean;
9+isError?: boolean;
910mediaUrl?: string;
1011mediaUrls?: string[];
1112};
@@ -434,6 +435,39 @@ describe("whatsapp inbound dispatch", () => {
434435expect(rememberSentText).toHaveBeenCalledTimes(4);
435436});
436437438+it("normalizes WhatsApp payload text before delivery and echo bookkeeping", async () => {
439+const deliverReply = vi.fn(async () => undefined);
440+const rememberSentText = vi.fn();
441+442+await dispatchBufferedReply({
443+ deliverReply,
444+ rememberSentText,
445+});
446+447+const deliver = getCapturedDeliver();
448+expect(deliver).toBeTypeOf("function");
449+450+await deliver?.(
451+{
452+text: 'Before\n<function_calls><invoke name="web_search"><parameter name="query">x</parameter></invoke></function_calls>\nAfter',
453+},
454+{ kind: "final" },
455+);
456+457+expect(deliverReply).toHaveBeenCalledWith(
458+expect.objectContaining({
459+replyResult: expect.objectContaining({ text: "Before\n\nAfter" }),
460+}),
461+);
462+expect(rememberSentText).toHaveBeenCalledWith(
463+"Before\n\nAfter",
464+expect.objectContaining({
465+combinedBody: "hi",
466+combinedBodySessionKey: "agent:main:whatsapp:direct:+1000",
467+}),
468+);
469+});
470+437471it("suppresses reasoning and compaction payloads before WhatsApp delivery", async () => {
438472const deliverReply = vi.fn(async () => undefined);
439473const rememberSentText = vi.fn();
@@ -455,6 +489,76 @@ describe("whatsapp inbound dispatch", () => {
455489expect(rememberSentText).not.toHaveBeenCalled();
456490});
457491492+it("suppresses payloads that normalize to no visible WhatsApp content", async () => {
493+const deliverReply = vi.fn(async () => undefined);
494+const rememberSentText = vi.fn();
495+496+await dispatchBufferedReply({
497+ deliverReply,
498+ rememberSentText,
499+});
500+501+const deliver = getCapturedDeliver();
502+expect(deliver).toBeTypeOf("function");
503+504+await deliver?.(
505+{
506+text: '<function_calls><invoke name="web_search"><parameter name="query">x</parameter></invoke></function_calls>',
507+},
508+{ kind: "final" },
509+);
510+511+expect(deliverReply).not.toHaveBeenCalled();
512+expect(rememberSentText).not.toHaveBeenCalled();
513+});
514+515+it("suppresses error payload text when exposeErrorText is false", async () => {
516+const deliverReply = vi.fn(async () => undefined);
517+const rememberSentText = vi.fn();
518+519+await dispatchBufferedReply({
520+cfg: { channels: { whatsapp: { exposeErrorText: false } } } as never,
521+ deliverReply,
522+ rememberSentText,
523+});
524+525+const deliver = getCapturedDeliver();
526+expect(deliver).toBeTypeOf("function");
527+528+await deliver?.({ text: "provider exploded", isError: true }, { kind: "final" });
529+530+expect(deliverReply).not.toHaveBeenCalled();
531+expect(rememberSentText).not.toHaveBeenCalled();
532+});
533+534+it("honors account-level exposeErrorText overrides for error payloads", async () => {
535+const deliverReply = vi.fn(async () => undefined);
536+const rememberSentText = vi.fn();
537+538+await dispatchBufferedReply({
539+cfg: {
540+channels: {
541+whatsapp: {
542+accounts: {
543+work: { exposeErrorText: false },
544+},
545+},
546+},
547+} as never,
548+ deliverReply,
549+ rememberSentText,
550+route: makeRoute({ accountId: "work" }),
551+});
552+553+const deliver = getCapturedDeliver();
554+expect(deliver).toBeTypeOf("function");
555+556+await deliver?.({ text: "provider exploded", isError: true }, { kind: "final" });
557+558+expect(deliverReply).not.toHaveBeenCalled();
559+expect(rememberSentText).not.toHaveBeenCalled();
560+});
561+458562it("maps WhatsApp blockStreaming=true to disableBlockStreaming=false", async () => {
459563await dispatchBufferedReply();
460564此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。