






















@@ -83,13 +83,17 @@ function createMessageEndContext(
8383consumeReplyDirectives?: ReturnType<typeof vi.fn>;
8484warn?: ReturnType<typeof vi.fn>;
8585builtinToolNames?: ReadonlySet<string>;
86+sourceReplyDeliveryMode?: "automatic" | "message_tool_only";
8687state?: Record<string, unknown>;
8788} = {},
8889) {
8990return {
9091params: {
9192runId: "run-1",
9293session: { id: "session-1" },
94+ ...(params.sourceReplyDeliveryMode
95+ ? { sourceReplyDeliveryMode: params.sourceReplyDeliveryMode }
96+ : {}),
9397 ...(params.onAgentEvent ? { onAgentEvent: params.onAgentEvent } : {}),
9498 ...(params.onBlockReply ? { onBlockReply: params.onBlockReply } : { onBlockReply: vi.fn() }),
9599},
@@ -149,6 +153,17 @@ function firstMockArg(mock: { mock: { calls: unknown[][] } }, label: string): un
149153return firstMockCall(mock, label)[0];
150154}
151155156+function createMessageToolEnvelope(message: string, args: Record<string, unknown> = {}): string {
157+return JSON.stringify({
158+name: "message",
159+arguments: {
160+action: "send",
161+ message,
162+ ...args,
163+},
164+});
165+}
166+152167describe("resolveSilentReplyFallbackText", () => {
153168it("replaces NO_REPLY with latest messaging tool text when available", () => {
154169expect(
@@ -665,6 +680,42 @@ describe("handleMessageEnd", () => {
665680expect(metadata?.registeredTool).toBe(true);
666681});
667682683+it("unwraps only source-routed or message-tool-only standalone message-tool JSON", () => {
684+const visibleReply = "No specific tasks planned, but I'll keep watching for updates.";
685+const unroutedEnvelope = createMessageToolEnvelope(visibleReply);
686+const routedEnvelope = createMessageToolEnvelope(visibleReply, { target: "user:redacted" });
687+const toRoutedEnvelope = createMessageToolEnvelope(visibleReply, { to: "user:redacted" });
688+689+for (const [text, api, builtinToolNames, sourceReplyDeliveryMode, expected] of [
690+[unroutedEnvelope, undefined, new Set(["message"]), "message_tool_only", visibleReply],
691+[routedEnvelope, "openai-completions", new Set<string>(), undefined, visibleReply],
692+[toRoutedEnvelope, "openai-completions", new Set<string>(), undefined, visibleReply],
693+[routedEnvelope, undefined, new Set<string>(), undefined, routedEnvelope],
694+[unroutedEnvelope, undefined, new Set(["message"]), undefined, unroutedEnvelope],
695+] as const) {
696+const emitBlockReply = vi.fn();
697+const consumeReplyDirectives = vi.fn((text: string) => (text ? { text } : null));
698+const ctx = createMessageEndContext({
699+ emitBlockReply,
700+ consumeReplyDirectives,
701+ builtinToolNames,
702+ sourceReplyDeliveryMode,
703+});
704+705+void handleMessageEnd(ctx, {
706+type: "message_end",
707+message: {
708+role: "assistant",
709+ ...(api ? { api } : {}),
710+content: [{ type: "text", text }],
711+},
712+} as never);
713+714+expect(consumeReplyDirectives).toHaveBeenCalledWith(expected, { final: true });
715+expect(firstMockArg(emitBlockReply, "block reply")).toMatchObject({ text: expected });
716+}
717+});
718+668719it("does not warn when the assistant emitted a structured tool call", () => {
669720const warn = vi.fn();
670721const ctx = createMessageEndContext({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。