Config/Compaction: expose safeguard preserve and quality settings (#25557)

Merged via squash.

Prepared head SHA: ea9904039a
Co-authored-by: rodrigouroz <384037+rodrigouroz@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
This commit is contained in:
Rodrigo Uroz
2026-03-07 12:13:13 -03:00
committed by GitHub
parent bdd0f74188
commit 4c0b873a4d
9 changed files with 88 additions and 0 deletions

View File

@@ -28,6 +28,7 @@ Docs: https://docs.openclaw.ai
- Tools/Diffs guidance: restore a short system-prompt hint for enabled diffs while keeping the detailed instructions in the companion skill, so diffs usage guidance stays out of user-prompt space. (#36904) thanks @gumadeiras.
- Tools/Diffs guidance loading: move diffs usage guidance from unconditional prompt-hook injection to the plugin companion skill path, reducing unrelated-turn prompt noise while keeping diffs tool behavior unchanged. (#32630) thanks @sircrumpet.
- Docs/Web search: remove outdated Brave free-tier wording and replace prescriptive AI ToS guidance with neutral compliance language in Brave setup docs. (#26860) Thanks @HenryLoenwind.
- Config/Compaction safeguard tuning: expose `agents.defaults.compaction.recentTurnsPreserve` and quality-guard retry knobs through the validated config surface and embedded-runner wiring, with regression coverage for real config loading and schema metadata. (#25557) thanks @rodrigouroz.
### Breaking
@@ -232,6 +233,7 @@ Docs: https://docs.openclaw.ai
- Gateway/Windows restart supervision: relaunch task-managed gateways through Scheduled Task with quoted helper-script command paths, distinguish restart-capable supervisors per platform, and stop orphaned Windows gateway children during self-restart. (#38825) Thanks @obviyus.
- Telegram/native topic command routing: resolve forum-topic native commands through the same conversation route as inbound messages so topic `agentId` overrides and bound topic sessions target the active session instead of the default topic-parent session. (#38871) Thanks @obviyus.
- Markdown/assistant image hardening: flatten remote markdown images to plain text across the Control UI, exported HTML, and shared Swift chat while keeping inline `data:image/...` markdown renderable, so model output no longer triggers automatic remote image fetches. (#38895) Thanks @obviyus.
- Config/compaction safeguard settings: regression-test `agents.defaults.compaction.recentTurnsPreserve` through `loadConfig()` and cover the new help metadata entry so the exposed preserve knob stays wired through schema validation and config UX. (#25557) thanks @rodrigouroz.
## 2026.3.2

View File

@@ -87,6 +87,7 @@ export function buildEmbeddedExtensionFactories(params: {
qualityGuardEnabled: qualityGuardCfg?.enabled ?? false,
qualityGuardMaxRetries: qualityGuardCfg?.maxRetries,
model: params.model,
recentTurnsPreserve: compactionCfg?.recentTurnsPreserve,
});
factories.push(compactionSafeguardExtension);
}

View File

@@ -5,7 +5,9 @@ import type { AgentMessage } from "@mariozechner/pi-agent-core";
import type { Api, Model } from "@mariozechner/pi-ai";
import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
import { describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../config/config.js";
import * as compactionModule from "../compaction.js";
import { buildEmbeddedExtensionFactories } from "../pi-embedded-runner/extensions.js";
import { castAgentMessage } from "../test-helpers/agent-message-fixtures.js";
import {
getCompactionSafeguardRuntime,
@@ -403,6 +405,39 @@ describe("compaction-safeguard runtime registry", () => {
model,
});
});
it("wires oversized safeguard runtime values when config validation is bypassed", () => {
const sessionManager = {} as unknown as Parameters<
typeof buildEmbeddedExtensionFactories
>[0]["sessionManager"];
const cfg = {
agents: {
defaults: {
compaction: {
mode: "safeguard",
recentTurnsPreserve: 99,
qualityGuard: { maxRetries: 99 },
},
},
},
} as OpenClawConfig;
buildEmbeddedExtensionFactories({
cfg,
sessionManager,
provider: "anthropic",
modelId: "claude-3-opus",
model: {
contextWindow: 200_000,
} as Parameters<typeof buildEmbeddedExtensionFactories>[0]["model"],
});
const runtime = getCompactionSafeguardRuntime(sessionManager);
expect(runtime?.qualityGuardMaxRetries).toBe(99);
expect(runtime?.recentTurnsPreserve).toBe(99);
expect(resolveQualityGuardMaxRetries(runtime?.qualityGuardMaxRetries)).toBe(3);
expect(resolveRecentTurnsPreserve(runtime?.recentTurnsPreserve)).toBe(12);
});
});
describe("compaction-safeguard recent-turn preservation", () => {

View File

@@ -89,4 +89,43 @@ describe("config compaction settings", () => {
},
);
});
it("preserves recent turn safeguard values through loadConfig()", async () => {
await withTempHomeConfig(
{
agents: {
defaults: {
compaction: {
mode: "safeguard",
recentTurnsPreserve: 4,
},
},
},
},
async () => {
const cfg = loadConfig();
expect(cfg.agents?.defaults?.compaction?.recentTurnsPreserve).toBe(4);
},
);
});
it("preserves oversized quality guard retry values for runtime clamping", async () => {
await withTempHomeConfig(
{
agents: {
defaults: {
compaction: {
qualityGuard: {
maxRetries: 99,
},
},
},
},
},
async () => {
const cfg = loadConfig();
expect(cfg.agents?.defaults?.compaction?.qualityGuard?.maxRetries).toBe(99);
},
);
});
});

View File

@@ -372,6 +372,7 @@ const TARGET_KEYS = [
"agents.defaults.compaction.maxHistoryShare",
"agents.defaults.compaction.identifierPolicy",
"agents.defaults.compaction.identifierInstructions",
"agents.defaults.compaction.recentTurnsPreserve",
"agents.defaults.compaction.qualityGuard",
"agents.defaults.compaction.qualityGuard.enabled",
"agents.defaults.compaction.qualityGuard.maxRetries",
@@ -796,6 +797,10 @@ describe("config help copy quality", () => {
expect(identifierPolicy.includes('"off"')).toBe(true);
expect(identifierPolicy.includes('"custom"')).toBe(true);
const recentTurnsPreserve = FIELD_HELP["agents.defaults.compaction.recentTurnsPreserve"];
expect(/recent.*turn|verbatim/i.test(recentTurnsPreserve)).toBe(true);
expect(/default:\s*3/i.test(recentTurnsPreserve)).toBe(true);
const postCompactionSections = FIELD_HELP["agents.defaults.compaction.postCompactionSections"];
expect(/Session Startup|Red Lines/i.test(postCompactionSections)).toBe(true);
expect(/Every Session|Safety/i.test(postCompactionSections)).toBe(true);

View File

@@ -999,6 +999,8 @@ export const FIELD_HELP: Record<string, string> = {
'Identifier-preservation policy for compaction summaries: "strict" prepends built-in opaque-identifier retention guidance (default), "off" disables this prefix, and "custom" uses identifierInstructions. Keep "strict" unless you have a specific compatibility need.',
"agents.defaults.compaction.identifierInstructions":
'Custom identifier-preservation instruction text used when identifierPolicy="custom". Keep this explicit and safety-focused so compaction summaries do not rewrite opaque IDs, URLs, hosts, or ports.',
"agents.defaults.compaction.recentTurnsPreserve":
"Number of most recent user/assistant turns kept verbatim outside safeguard summarization (default: 3). Raise this to preserve exact recent dialogue context, or lower it to maximize compaction savings.",
"agents.defaults.compaction.qualityGuard":
"Optional quality-audit retry settings for safeguard compaction summaries. Leave this disabled unless you explicitly want summary audits and one-shot regeneration on failed checks.",
"agents.defaults.compaction.qualityGuard.enabled":

View File

@@ -452,6 +452,7 @@ export const FIELD_LABELS: Record<string, string> = {
"agents.defaults.compaction.maxHistoryShare": "Compaction Max History Share",
"agents.defaults.compaction.identifierPolicy": "Compaction Identifier Policy",
"agents.defaults.compaction.identifierInstructions": "Compaction Identifier Instructions",
"agents.defaults.compaction.recentTurnsPreserve": "Compaction Preserve Recent Turns",
"agents.defaults.compaction.qualityGuard": "Compaction Quality Guard",
"agents.defaults.compaction.qualityGuard.enabled": "Compaction Quality Guard Enabled",
"agents.defaults.compaction.qualityGuard.maxRetries": "Compaction Quality Guard Max Retries",

View File

@@ -306,6 +306,8 @@ export type AgentCompactionConfig = {
reserveTokensFloor?: number;
/** Max share of context window for history during safeguard pruning (0.10.9, default 0.5). */
maxHistoryShare?: number;
/** Preserve this many most-recent user/assistant turns verbatim in compaction summary context. */
recentTurnsPreserve?: number;
/** Identifier-preservation instruction policy for compaction summaries. */
identifierPolicy?: AgentCompactionIdentifierPolicy;
/** Custom identifier-preservation instructions used when identifierPolicy is "custom". */

View File

@@ -95,6 +95,7 @@ export const AgentDefaultsSchema = z
.union([z.literal("strict"), z.literal("off"), z.literal("custom")])
.optional(),
identifierInstructions: z.string().optional(),
recentTurnsPreserve: z.number().int().min(0).max(12).optional(),
qualityGuard: z
.object({
enabled: z.boolean().optional(),