mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 23:18:28 +00:00
fix: centralize verbose overrides and tool stream gating
This commit is contained in:
@@ -1,4 +1,9 @@
|
||||
import type { AgentEventPayload } from "../infra/agent-events.js";
|
||||
import { normalizeVerboseLevel } from "../auto-reply/thinking.js";
|
||||
import {
|
||||
type AgentEventPayload,
|
||||
getAgentRunContext,
|
||||
} from "../infra/agent-events.js";
|
||||
import { loadSessionEntry } from "./session-utils.js";
|
||||
import { formatForLog } from "./ws-log.js";
|
||||
|
||||
export type ChatRunEntry = {
|
||||
@@ -185,6 +190,24 @@ export function createAgentEventHandler({
|
||||
bridgeSendToSession(sessionKey, "chat", payload);
|
||||
};
|
||||
|
||||
const shouldEmitToolEvents = (runId: string, sessionKey?: string) => {
|
||||
const runContext = getAgentRunContext(runId);
|
||||
const runVerbose = normalizeVerboseLevel(runContext?.verboseLevel);
|
||||
if (runVerbose) return runVerbose === "on";
|
||||
if (!sessionKey) return false;
|
||||
try {
|
||||
const { cfg, entry } = loadSessionEntry(sessionKey);
|
||||
const sessionVerbose = normalizeVerboseLevel(entry?.verboseLevel);
|
||||
if (sessionVerbose) return sessionVerbose === "on";
|
||||
const defaultVerbose = normalizeVerboseLevel(
|
||||
cfg.agents?.defaults?.verboseDefault,
|
||||
);
|
||||
return defaultVerbose === "on";
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
return (evt: AgentEventPayload) => {
|
||||
const chatLink = chatRunState.registry.peek(evt.runId);
|
||||
const sessionKey =
|
||||
@@ -192,6 +215,10 @@ export function createAgentEventHandler({
|
||||
// Include sessionKey so Control UI can filter tool streams per session.
|
||||
const agentPayload = sessionKey ? { ...evt, sessionKey } : evt;
|
||||
const last = agentRunSeq.get(evt.runId) ?? 0;
|
||||
if (evt.stream === "tool" && !shouldEmitToolEvents(evt.runId, sessionKey)) {
|
||||
agentRunSeq.set(evt.runId, evt.seq);
|
||||
return;
|
||||
}
|
||||
if (evt.seq !== last + 1) {
|
||||
broadcast("agent", {
|
||||
runId: evt.runId,
|
||||
|
||||
@@ -785,7 +785,10 @@ describe("gateway server agent", () => {
|
||||
},
|
||||
});
|
||||
|
||||
registerAgentRunContext("run-tool-1", { sessionKey: "main" });
|
||||
registerAgentRunContext("run-tool-1", {
|
||||
sessionKey: "main",
|
||||
verboseLevel: "on",
|
||||
});
|
||||
|
||||
const agentEvtP = onceMessage(
|
||||
ws,
|
||||
@@ -813,6 +816,66 @@ describe("gateway server agent", () => {
|
||||
await server.close();
|
||||
});
|
||||
|
||||
test("suppresses tool stream events when verbose is off", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
|
||||
testState.sessionStorePath = path.join(dir, "sessions.json");
|
||||
await fs.writeFile(
|
||||
testState.sessionStorePath,
|
||||
JSON.stringify(
|
||||
{
|
||||
"agent:main:main": {
|
||||
sessionId: "sess-main",
|
||||
updatedAt: Date.now(),
|
||||
verboseLevel: "off",
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
const { server, ws } = await startServerWithClient();
|
||||
await connectOk(ws, {
|
||||
client: {
|
||||
name: "webchat",
|
||||
version: "1.0.0",
|
||||
platform: "test",
|
||||
mode: "webchat",
|
||||
},
|
||||
});
|
||||
|
||||
registerAgentRunContext("run-tool-off", { sessionKey: "agent:main:main" });
|
||||
|
||||
emitAgentEvent({
|
||||
runId: "run-tool-off",
|
||||
stream: "tool",
|
||||
data: { phase: "start", name: "read", toolCallId: "tool-1" },
|
||||
});
|
||||
emitAgentEvent({
|
||||
runId: "run-tool-off",
|
||||
stream: "assistant",
|
||||
data: { text: "hello" },
|
||||
});
|
||||
|
||||
const evt = await onceMessage(
|
||||
ws,
|
||||
(o) =>
|
||||
o.type === "event" &&
|
||||
o.event === "agent" &&
|
||||
o.payload?.runId === "run-tool-off",
|
||||
8000,
|
||||
);
|
||||
const payload =
|
||||
evt.payload && typeof evt.payload === "object"
|
||||
? (evt.payload as Record<string, unknown>)
|
||||
: {};
|
||||
expect(payload.stream).toBe("assistant");
|
||||
|
||||
ws.close();
|
||||
await server.close();
|
||||
});
|
||||
|
||||
test("agent.wait resolves after lifecycle end", async () => {
|
||||
const { server, ws } = await startServerWithClient();
|
||||
await connectOk(ws);
|
||||
|
||||
@@ -12,11 +12,14 @@ import {
|
||||
normalizeReasoningLevel,
|
||||
normalizeThinkLevel,
|
||||
normalizeUsageDisplay,
|
||||
normalizeVerboseLevel,
|
||||
} from "../auto-reply/thinking.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import type { SessionEntry } from "../config/sessions.js";
|
||||
import { isSubagentSessionKey } from "../routing/session-key.js";
|
||||
import {
|
||||
applyVerboseOverride,
|
||||
parseVerboseOverride,
|
||||
} from "../sessions/level-overrides.js";
|
||||
import { normalizeSendPolicy } from "../sessions/send-policy.js";
|
||||
import { parseSessionLabel } from "../sessions/session-label.js";
|
||||
import {
|
||||
@@ -103,13 +106,9 @@ export async function applySessionsPatchToStore(params: {
|
||||
|
||||
if ("verboseLevel" in patch) {
|
||||
const raw = patch.verboseLevel;
|
||||
if (raw === null) {
|
||||
delete next.verboseLevel;
|
||||
} else if (raw !== undefined) {
|
||||
const normalized = normalizeVerboseLevel(String(raw));
|
||||
if (!normalized) return invalid('invalid verboseLevel (use "on"|"off")');
|
||||
next.verboseLevel = normalized;
|
||||
}
|
||||
const parsed = parseVerboseOverride(raw);
|
||||
if (!parsed.ok) return invalid(parsed.error);
|
||||
applyVerboseOverride(next, parsed.value);
|
||||
}
|
||||
|
||||
if ("reasoningLevel" in patch) {
|
||||
|
||||
Reference in New Issue
Block a user