mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-03 14:40:22 +00:00
fix: improve gpt execution flow and visibility
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi, type Mock } from "vitest";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import type { SessionBindingRecord } from "../../infra/outbound/session-binding-service.js";
|
||||
import type {
|
||||
@@ -858,7 +858,7 @@ describe("dispatchReplyFromConfig", () => {
|
||||
expect(dispatcher.sendFinalReply).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("suppresses native tool summaries but still forwards tool media", async () => {
|
||||
it("delivers native tool summaries and tool media", async () => {
|
||||
setNoAbort();
|
||||
const cfg = emptyConfig;
|
||||
const dispatcher = createDispatcher();
|
||||
@@ -883,13 +883,52 @@ describe("dispatchReplyFromConfig", () => {
|
||||
|
||||
await dispatchReplyFromConfig({ ctx, cfg, dispatcher, replyResolver });
|
||||
|
||||
expect(dispatcher.sendToolResult).toHaveBeenCalledTimes(1);
|
||||
const sent = firstToolResultPayload(dispatcher);
|
||||
expect(dispatcher.sendToolResult).toHaveBeenCalledTimes(2);
|
||||
expect(dispatcher.sendToolResult).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.objectContaining({ text: "🔧 tools/sessions_send" }),
|
||||
);
|
||||
const sent = (dispatcher.sendToolResult as Mock).mock.calls[1]?.[0] as ReplyPayload | undefined;
|
||||
expect(sent?.mediaUrl).toBe("https://example.com/tts-native.opus");
|
||||
expect(sent?.text).toBeUndefined();
|
||||
expect(dispatcher.sendFinalReply).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("emits concise tool-start progress updates for direct sessions", async () => {
|
||||
setNoAbort();
|
||||
const cfg = emptyConfig;
|
||||
const dispatcher = createDispatcher();
|
||||
const ctx = buildTestCtx({
|
||||
Provider: "telegram",
|
||||
ChatType: "direct",
|
||||
});
|
||||
|
||||
const replyResolver = async (
|
||||
_ctx: MsgContext,
|
||||
opts?: GetReplyOptions,
|
||||
_cfg?: OpenClawConfig,
|
||||
) => {
|
||||
await opts?.onToolStart?.({ name: "read", phase: "start" });
|
||||
await opts?.onToolStart?.({ name: "read", phase: "update" });
|
||||
await opts?.onToolStart?.({ name: "grep", phase: "start" });
|
||||
await opts?.onToolStart?.({ name: "exec", phase: "start" });
|
||||
return { text: "done" } satisfies ReplyPayload;
|
||||
};
|
||||
|
||||
await dispatchReplyFromConfig({ ctx, cfg, dispatcher, replyResolver });
|
||||
|
||||
expect(dispatcher.sendToolResult).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.objectContaining({ text: "Working: read" }),
|
||||
);
|
||||
expect(dispatcher.sendToolResult).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expect.objectContaining({ text: "Working: grep" }),
|
||||
);
|
||||
expect(dispatcher.sendToolResult).toHaveBeenCalledTimes(2);
|
||||
expect(dispatcher.sendFinalReply).toHaveBeenCalledWith({ text: "done" });
|
||||
});
|
||||
|
||||
it("delivers deterministic exec approval tool payloads for native commands", async () => {
|
||||
setNoAbort();
|
||||
const cfg = emptyConfig;
|
||||
|
||||
@@ -597,10 +597,12 @@ export async function dispatchReplyFromConfig(params: {
|
||||
}
|
||||
}
|
||||
|
||||
// Forum topics are threaded conversations within a group — verbose tool
|
||||
// summaries should be delivered into the topic thread, same as DMs.
|
||||
const shouldSendToolSummaries =
|
||||
(ctx.ChatType !== "group" || ctx.IsForum === true) && ctx.CommandSource !== "native";
|
||||
// Forum topics are threaded conversations within a group — tool visibility
|
||||
// should be delivered into the topic thread, same as DMs.
|
||||
const shouldSendToolSummaries = ctx.ChatType !== "group" || ctx.IsForum === true;
|
||||
const shouldSendToolStartStatuses = ctx.ChatType !== "group" || ctx.IsForum === true;
|
||||
const toolStartStatusesSent = new Set<string>();
|
||||
let toolStartStatusCount = 0;
|
||||
const acpDispatch = await dispatchAcpRuntime.tryDispatchAcpReply({
|
||||
ctx,
|
||||
cfg,
|
||||
@@ -699,6 +701,28 @@ export async function dispatchReplyFromConfig(params: {
|
||||
};
|
||||
return run();
|
||||
},
|
||||
onToolStart: ({ name, phase }) => {
|
||||
if (!shouldSendToolStartStatuses || phase !== "start") {
|
||||
return;
|
||||
}
|
||||
const normalizedName = typeof name === "string" ? name.trim() : "";
|
||||
if (
|
||||
!normalizedName ||
|
||||
toolStartStatusCount >= 2 ||
|
||||
toolStartStatusesSent.has(normalizedName)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
toolStartStatusesSent.add(normalizedName);
|
||||
toolStartStatusCount += 1;
|
||||
const payload: ReplyPayload = {
|
||||
text: `Working: ${normalizedName}`,
|
||||
};
|
||||
if (shouldRouteToOriginating) {
|
||||
return sendPayloadAsync(payload, undefined, false);
|
||||
}
|
||||
dispatcher.sendToolResult(payload);
|
||||
},
|
||||
onBlockReply: (payload: ReplyPayload, context?: BlockReplyContext) => {
|
||||
const run = async () => {
|
||||
// Suppress reasoning payloads — channels using this generic dispatch
|
||||
|
||||
Reference in New Issue
Block a user