From 356ce7647f3a1d4a0a5577a26538b7a1ce4b4fe8 Mon Sep 17 00:00:00 2001 From: Vignesh Natarajan Date: Sat, 14 Feb 2026 21:05:19 -0800 Subject: [PATCH] fix (agents): suppress NO_REPLY final text when message tool already sent text --- ...bedded-subscribe.handlers.messages.test.ts | 31 +++++++++++++++++++ ...pi-embedded-subscribe.handlers.messages.ts | 21 ++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/agents/pi-embedded-subscribe.handlers.messages.test.ts diff --git a/src/agents/pi-embedded-subscribe.handlers.messages.test.ts b/src/agents/pi-embedded-subscribe.handlers.messages.test.ts new file mode 100644 index 00000000000..6c508bdbdb6 --- /dev/null +++ b/src/agents/pi-embedded-subscribe.handlers.messages.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, it } from "vitest"; +import { resolveSilentReplyFallbackText } from "./pi-embedded-subscribe.handlers.messages.js"; + +describe("resolveSilentReplyFallbackText", () => { + it("replaces NO_REPLY with latest messaging tool text when available", () => { + expect( + resolveSilentReplyFallbackText({ + text: "NO_REPLY", + messagingToolSentTexts: ["first", "final delivered text"], + }), + ).toBe("final delivered text"); + }); + + it("keeps original text when response is not NO_REPLY", () => { + expect( + resolveSilentReplyFallbackText({ + text: "normal assistant reply", + messagingToolSentTexts: ["final delivered text"], + }), + ).toBe("normal assistant reply"); + }); + + it("keeps NO_REPLY when there is no messaging tool text to mirror", () => { + expect( + resolveSilentReplyFallbackText({ + text: "NO_REPLY", + messagingToolSentTexts: [], + }), + ).toBe("NO_REPLY"); + }); +}); diff --git a/src/agents/pi-embedded-subscribe.handlers.messages.ts b/src/agents/pi-embedded-subscribe.handlers.messages.ts index f4e738c6663..a304d1db24c 100644 --- a/src/agents/pi-embedded-subscribe.handlers.messages.ts +++ b/src/agents/pi-embedded-subscribe.handlers.messages.ts @@ -1,6 +1,7 @@ import type { AgentEvent, AgentMessage } from "@mariozechner/pi-agent-core"; import type { EmbeddedPiSubscribeContext } from "./pi-embedded-subscribe.handlers.types.js"; import { parseReplyDirectives } from "../auto-reply/reply/reply-directives.js"; +import { SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js"; import { emitAgentEvent } from "../infra/agent-events.js"; import { createInlineCodeState } from "../markdown/code-spans.js"; import { @@ -29,6 +30,21 @@ const stripTrailingDirective = (text: string): string => { return text.slice(0, openIndex); }; +export function resolveSilentReplyFallbackText(params: { + text: string; + messagingToolSentTexts: string[]; +}): string { + const trimmed = params.text.trim(); + if (trimmed !== SILENT_REPLY_TOKEN) { + return params.text; + } + const fallback = params.messagingToolSentTexts.at(-1)?.trim(); + if (!fallback) { + return params.text; + } + return fallback; +} + export function handleMessageStart( ctx: EmbeddedPiSubscribeContext, evt: AgentEvent & { message: AgentMessage }, @@ -214,7 +230,10 @@ export function handleMessageEnd( rawThinking: extractAssistantThinking(assistantMessage), }); - const text = ctx.stripBlockTags(rawText, { thinking: false, final: false }); + const text = resolveSilentReplyFallbackText({ + text: ctx.stripBlockTags(rawText, { thinking: false, final: false }), + messagingToolSentTexts: ctx.state.messagingToolSentTexts, + }); const rawThinking = ctx.state.includeReasoning || ctx.state.streamReasoning ? extractAssistantThinking(assistantMessage) || extractThinkingFromTaggedText(rawText)