fix(agents): trim config write tool responses

This commit is contained in:
Peter Steinberger
2026-04-28 10:32:52 +01:00
parent 5820a48fca
commit f5922e6eb1
3 changed files with 80 additions and 4 deletions

View File

@@ -19,6 +19,7 @@ Docs: https://docs.openclaw.ai
- Gateway/install: carry env-backed config SecretRefs such as `channels.discord.token` into generated service environments when they are present only in the installing shell, while keeping gateway auth SecretRefs non-persisted. Fixes #67817; supersedes #73426. Thanks @wdimaculangan and @ztexydt-cqh.
- Auto-reply/commands: stop bare `/reset` and `/new` after reset hooks acknowledge the command, so non-ACP channels no longer fall through into empty provider calls while `/reset <message>` and `/new <message>` still seed the next model turn. Fixes #73367 and #73412. Thanks @hoyanhan, @wenxu007, and @amdhelper.
- Providers/DeepSeek: backfill DeepSeek V4 `reasoning_content` on plain assistant replay messages as well as tool-call turns, so thinking sessions with prior tool use no longer fail follow-up requests with missing reasoning content. Fixes #73417; refs #71372. Thanks @34262315716 and @Bartok9.
- Agents/gateway tool: strip full config payloads from `config.patch` and `config.apply` tool responses while preserving direct RPC responses, so config-heavy sessions no longer replay large redacted configs into transcript history. Fixes #47610; supersedes #73439. Thanks @HanenVit and @juan-flores077.
- Auto-reply: preserve voice-note media from silent turns while continuing to suppress text and non-voice media, so `NO_REPLY` TTS replies still deliver the requested audio bubble. (#73406) Thanks @zqchris.
- Channels/Mattermost: stop enqueueing regular inbound posts as system events, so Mattermost user messages reach the model only as user-role inbound-envelope content instead of also appearing as `System: Mattermost message...` directives. Fixes #71795. Thanks @juan-flores077.
- Agents/media: qualify bare `agents.defaults.imageModel` and `pdfModel` refs from unique configured image-capable providers, so Ollama vision models such as `moondream` and `qwen2.5vl:7b` do not fall through to the default provider. Fixes #38816; supersedes #73396. Thanks @alainasclaw and @vincentkoc.

View File

@@ -187,16 +187,48 @@ describe("gateway tool", () => {
});
it("passes config.apply through gateway call", async () => {
vi.mocked(callGatewayTool).mockImplementation(async (method: string) => {
if (method === "config.get") {
return {
hash: "hash-1",
config: {
tools: {
exec: {
ask: "on-miss",
security: "allowlist",
},
},
},
};
}
if (method === "config.apply") {
return {
ok: true,
path: "/tmp/openclaw.json",
config: { agents: { defaults: { systemPromptOverride: "You are a terse assistant." } } },
restart: { ok: true, config: "nested field preserved" },
};
}
return { ok: true };
});
const sessionKey = "agent:main:whatsapp:dm:+15555550123";
const tool = requireGatewayTool(sessionKey);
const raw =
'{\n agents: { defaults: { systemPromptOverride: "You are a terse assistant." } },\n tools: { exec: { ask: "on-miss", security: "allowlist" } }\n}\n';
await tool.execute("call2", {
const result = await tool.execute("call2", {
action: "config.apply",
raw,
});
expect(result.details).toEqual({
ok: true,
result: {
ok: true,
path: "/tmp/openclaw.json",
restart: { ok: true, config: "nested field preserved" },
},
});
expectConfigMutationCall({
callGatewayTool: vi.mocked(callGatewayTool),
action: "config.apply",
@@ -206,15 +238,47 @@ describe("gateway tool", () => {
});
it("passes config.patch through gateway call", async () => {
vi.mocked(callGatewayTool).mockImplementation(async (method: string) => {
if (method === "config.get") {
return {
hash: "hash-1",
config: {
tools: {
exec: {
ask: "on-miss",
security: "allowlist",
},
},
},
};
}
if (method === "config.patch") {
return {
ok: true,
noop: true,
path: "/tmp/openclaw.json",
config: { channels: { telegram: { groups: {} } } },
};
}
return { ok: true };
});
const sessionKey = "agent:main:whatsapp:dm:+15555550123";
const tool = requireGatewayTool(sessionKey);
const raw = '{\n channels: { telegram: { groups: { "*": { requireMention: false } } } }\n}\n';
await tool.execute("call4", {
const result = await tool.execute("call4", {
action: "config.patch",
raw,
});
expect(result.details).toEqual({
ok: true,
result: {
ok: true,
noop: true,
path: "/tmp/openclaw.json",
},
});
expectConfigMutationCall({
callGatewayTool: vi.mocked(callGatewayTool),
action: "config.patch",

View File

@@ -89,6 +89,17 @@ function getSnapshotConfig(snapshot: unknown): Record<string, unknown> {
return config as Record<string, unknown>;
}
// Direct RPC callers need the validated config echoed after writes; the
// agent-facing gateway tool does not, and replaying it bloats transcripts.
function stripConfigWriteResultPayload(result: unknown): unknown {
if (!isPlainObject(result) || !Object.hasOwn(result, "config")) {
return result;
}
const stripped = { ...result };
delete stripped.config;
return stripped;
}
function parseGatewayConfigMutationRaw(
raw: string,
action: "config.apply" | "config.patch",
@@ -481,7 +492,7 @@ export function createGatewayTool(opts?: {
note,
restartDelayMs,
});
return jsonResult({ ok: true, result });
return jsonResult({ ok: true, result: stripConfigWriteResultPayload(result) });
}
if (action === "config.patch") {
const { raw, baseHash, snapshotConfig, sessionKey, note, restartDelayMs } =
@@ -498,7 +509,7 @@ export function createGatewayTool(opts?: {
note,
restartDelayMs,
});
return jsonResult({ ok: true, result });
return jsonResult({ ok: true, result: stripConfigWriteResultPayload(result) });
}
if (action === "update.run") {
const { sessionKey, note, restartDelayMs } = resolveGatewayWriteMeta();