

























@@ -1945,7 +1945,7 @@ describe("dispatchReplyFromConfig", () => {
19451945await opts?.onPatchSummary?.({ phase: "end", summary: "1 modified" });
19461946await opts?.onCompactionStart?.();
19471947await opts?.onCompactionEnd?.();
1948-await opts?.onToolResult?.({ text: "🔧 exec: ok" });
1948+await opts?.onToolResult?.({ text: "exec: ok" });
19491949return { text: "done" } satisfies ReplyPayload;
19501950};
19511951@@ -1992,6 +1992,120 @@ describe("dispatchReplyFromConfig", () => {
19921992expect(event.shouldSendToolSummaries).toBe(true);
19931993});
199419941995+it("forwards direct native progress callbacks while verbose is off", async () => {
1996+setNoAbort();
1997+sessionStoreMocks.currentEntry = {
1998+verboseLevel: "off",
1999+};
2000+const cfg = emptyConfig;
2001+const dispatcher = createDispatcher();
2002+const ctx = buildTestCtx({
2003+Provider: "telegram",
2004+Surface: "telegram",
2005+ChatType: "direct",
2006+From: "telegram:123",
2007+SessionKey: "agent:main:telegram:dm:123",
2008+});
2009+const onToolStart = vi.fn();
2010+const onItemEvent = vi.fn();
2011+const onCommandOutput = vi.fn();
2012+2013+const replyResolver = async (
2014+_ctx: MsgContext,
2015+opts?: GetReplyOptions,
2016+_cfg?: OpenClawConfig,
2017+) => {
2018+await opts?.onToolStart?.({ name: "exec", phase: "start" });
2019+await opts?.onItemEvent?.({ itemId: "1", kind: "tool", progressText: "running exec" });
2020+await opts?.onCommandOutput?.({ phase: "end", name: "exec", status: "ok", exitCode: 0 });
2021+await opts?.onToolResult?.({ text: "🔧 exec: ok" });
2022+return { text: "done" } satisfies ReplyPayload;
2023+};
2024+2025+await dispatchReplyFromConfig({
2026+ ctx,
2027+ cfg,
2028+ dispatcher,
2029+ replyResolver,
2030+replyOptions: {
2031+sourceReplyDeliveryMode: "message_tool_only",
2032+suppressDefaultToolProgressMessages: true,
2033+allowProgressCallbacksWhenSourceDeliverySuppressed: true,
2034+ onToolStart,
2035+ onItemEvent,
2036+ onCommandOutput,
2037+},
2038+});
2039+2040+expect(onToolStart).toHaveBeenCalledWith({ name: "exec", phase: "start" });
2041+expect(onItemEvent).toHaveBeenCalledWith({
2042+itemId: "1",
2043+kind: "tool",
2044+progressText: "running exec",
2045+});
2046+expect(onCommandOutput).toHaveBeenCalledWith({
2047+phase: "end",
2048+name: "exec",
2049+status: "ok",
2050+exitCode: 0,
2051+});
2052+expect(dispatcher.sendToolResult).not.toHaveBeenCalled();
2053+expect(dispatcher.sendFinalReply).not.toHaveBeenCalled();
2054+});
2055+2056+it("suppresses direct native progress callbacks when send policy denies delivery", async () => {
2057+setNoAbort();
2058+sessionStoreMocks.currentEntry = {
2059+sendPolicy: "deny",
2060+verboseLevel: "off",
2061+};
2062+const cfg = emptyConfig;
2063+const dispatcher = createDispatcher();
2064+const ctx = buildTestCtx({
2065+Provider: "telegram",
2066+Surface: "telegram",
2067+ChatType: "direct",
2068+From: "telegram:123",
2069+SessionKey: "agent:main:telegram:dm:123",
2070+});
2071+const onToolStart = vi.fn();
2072+const onItemEvent = vi.fn();
2073+const onCommandOutput = vi.fn();
2074+2075+const replyResolver = async (
2076+_ctx: MsgContext,
2077+opts?: GetReplyOptions,
2078+_cfg?: OpenClawConfig,
2079+) => {
2080+await opts?.onToolStart?.({ name: "exec", phase: "start" });
2081+await opts?.onItemEvent?.({ itemId: "1", kind: "tool", progressText: "running exec" });
2082+await opts?.onCommandOutput?.({ phase: "end", name: "exec", status: "ok", exitCode: 0 });
2083+await opts?.onToolResult?.({ text: "exec: ok" });
2084+return { text: "done" } satisfies ReplyPayload;
2085+};
2086+2087+await dispatchReplyFromConfig({
2088+ ctx,
2089+ cfg,
2090+ dispatcher,
2091+ replyResolver,
2092+replyOptions: {
2093+sourceReplyDeliveryMode: "message_tool_only",
2094+suppressDefaultToolProgressMessages: true,
2095+allowProgressCallbacksWhenSourceDeliverySuppressed: true,
2096+ onToolStart,
2097+ onItemEvent,
2098+ onCommandOutput,
2099+},
2100+});
2101+2102+expect(onToolStart).not.toHaveBeenCalled();
2103+expect(onItemEvent).not.toHaveBeenCalled();
2104+expect(onCommandOutput).not.toHaveBeenCalled();
2105+expect(dispatcher.sendToolResult).not.toHaveBeenCalled();
2106+expect(dispatcher.sendFinalReply).not.toHaveBeenCalled();
2107+});
2108+19952109it("normalizes tool-result media before delivery and drops blocked file URLs", async () => {
19962110setNoAbort();
19972111replyMediaPathMocks.createReplyMediaPathNormalizer.mockReturnValue(
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。