fix: keep safeguard quality guard opt-in (openclaw#25556) thanks @rodrigouroz

This commit is contained in:
Josh Lehman
2026-03-05 12:56:25 -08:00
parent 566a795c88
commit be473efd16
9 changed files with 73 additions and 4 deletions

View File

@@ -165,6 +165,7 @@ Docs: https://docs.openclaw.ai
- Plugin runtime/system: expose `runtime.system.requestHeartbeatNow(...)` so extensions can wake targeted sessions immediately after enqueueing system events. (#19464) Thanks @AustinEral.
- Plugin runtime/events: expose `runtime.events.onAgentEvent` and `runtime.events.onSessionTranscriptUpdate` for extension-side subscriptions, and isolate transcript-listener failures so one faulty listener cannot break the entire update fanout. (#16044) Thanks @scifantastic.
- CLI/Banner taglines: add `cli.banner.taglineMode` (`random` | `default` | `off`) to control funny tagline behavior in startup output, with docs + FAQ guidance and regression tests for config override behavior.
- Agents/compaction safeguard quality-audit rollout: keep summary quality audits disabled by default unless `agents.defaults.compaction.qualityGuard` is explicitly enabled, and add config plumbing for bounded retry control. (#25556) thanks @rodrigouroz.
### Breaking

View File

@@ -7,7 +7,7 @@ import compactionSafeguardExtension from "../pi-extensions/compaction-safeguard.
import { buildEmbeddedExtensionFactories } from "./extensions.js";
describe("buildEmbeddedExtensionFactories", () => {
it("wires safeguard quality-guard runtime flags", () => {
it("does not opt safeguard mode into quality-guard retries", () => {
const sessionManager = {} as SessionManager;
const model = {
id: "claude-sonnet-4-20250514",
@@ -31,10 +31,44 @@ describe("buildEmbeddedExtensionFactories", () => {
model,
});
expect(factories).toContain(compactionSafeguardExtension);
expect(getCompactionSafeguardRuntime(sessionManager)).toMatchObject({
qualityGuardEnabled: false,
});
});
it("wires explicit safeguard quality-guard runtime flags", () => {
const sessionManager = {} as SessionManager;
const model = {
id: "claude-sonnet-4-20250514",
contextWindow: 200_000,
} as Model<Api>;
const cfg = {
agents: {
defaults: {
compaction: {
mode: "safeguard",
qualityGuard: {
enabled: true,
maxRetries: 2,
},
},
},
},
} as OpenClawConfig;
const factories = buildEmbeddedExtensionFactories({
cfg,
sessionManager,
provider: "anthropic",
modelId: "claude-sonnet-4-20250514",
model,
});
expect(factories).toContain(compactionSafeguardExtension);
expect(getCompactionSafeguardRuntime(sessionManager)).toMatchObject({
qualityGuardEnabled: true,
qualityGuardMaxRetries: 1,
qualityGuardMaxRetries: 2,
});
});
});

View File

@@ -71,6 +71,7 @@ export function buildEmbeddedExtensionFactories(params: {
const factories: ExtensionFactory[] = [];
if (resolveCompactionMode(params.cfg) === "safeguard") {
const compactionCfg = params.cfg?.agents?.defaults?.compaction;
const qualityGuardCfg = compactionCfg?.qualityGuard;
const contextWindowInfo = resolveContextWindowInfo({
cfg: params.cfg,
provider: params.provider,
@@ -83,8 +84,8 @@ export function buildEmbeddedExtensionFactories(params: {
contextWindowTokens: contextWindowInfo.tokens,
identifierPolicy: compactionCfg?.identifierPolicy,
identifierInstructions: compactionCfg?.identifierInstructions,
qualityGuardEnabled: true,
qualityGuardMaxRetries: 1,
qualityGuardEnabled: qualityGuardCfg?.enabled ?? false,
qualityGuardMaxRetries: qualityGuardCfg?.maxRetries,
model: params.model,
});
factories.push(compactionSafeguardExtension);

View File

@@ -13,6 +13,10 @@ describe("config compaction settings", () => {
reserveTokensFloor: 12_345,
identifierPolicy: "custom",
identifierInstructions: "Keep ticket IDs unchanged.",
qualityGuard: {
enabled: true,
maxRetries: 2,
},
memoryFlush: {
enabled: false,
softThresholdTokens: 1234,
@@ -34,6 +38,8 @@ describe("config compaction settings", () => {
expect(cfg.agents?.defaults?.compaction?.identifierInstructions).toBe(
"Keep ticket IDs unchanged.",
);
expect(cfg.agents?.defaults?.compaction?.qualityGuard?.enabled).toBe(true);
expect(cfg.agents?.defaults?.compaction?.qualityGuard?.maxRetries).toBe(2);
expect(cfg.agents?.defaults?.compaction?.memoryFlush?.enabled).toBe(false);
expect(cfg.agents?.defaults?.compaction?.memoryFlush?.softThresholdTokens).toBe(1234);
expect(cfg.agents?.defaults?.compaction?.memoryFlush?.prompt).toBe("Write notes.");

View File

@@ -370,6 +370,9 @@ const TARGET_KEYS = [
"agents.defaults.compaction.maxHistoryShare",
"agents.defaults.compaction.identifierPolicy",
"agents.defaults.compaction.identifierInstructions",
"agents.defaults.compaction.qualityGuard",
"agents.defaults.compaction.qualityGuard.enabled",
"agents.defaults.compaction.qualityGuard.maxRetries",
"agents.defaults.compaction.memoryFlush",
"agents.defaults.compaction.memoryFlush.enabled",
"agents.defaults.compaction.memoryFlush.softThresholdTokens",

View File

@@ -967,6 +967,12 @@ 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.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":
"Enables summary quality audits and regeneration retries for safeguard compaction. Default: false, so safeguard mode alone does not turn on retry behavior.",
"agents.defaults.compaction.qualityGuard.maxRetries":
"Maximum number of regeneration retries after a failed safeguard summary quality audit. Use small values to bound extra latency and token cost.",
"agents.defaults.compaction.memoryFlush":
"Pre-compaction memory flush settings that run an agentic memory write before heavy compaction. Keep enabled for long sessions so salient context is persisted before aggressive trimming.",
"agents.defaults.compaction.memoryFlush.enabled":

View File

@@ -434,6 +434,9 @@ 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.qualityGuard": "Compaction Quality Guard",
"agents.defaults.compaction.qualityGuard.enabled": "Compaction Quality Guard Enabled",
"agents.defaults.compaction.qualityGuard.maxRetries": "Compaction Quality Guard Max Retries",
"agents.defaults.compaction.memoryFlush": "Compaction Memory Flush",
"agents.defaults.compaction.memoryFlush.enabled": "Compaction Memory Flush Enabled",
"agents.defaults.compaction.memoryFlush.softThresholdTokens":

View File

@@ -288,6 +288,12 @@ export type AgentDefaultsConfig = {
export type AgentCompactionMode = "default" | "safeguard";
export type AgentCompactionIdentifierPolicy = "strict" | "off" | "custom";
export type AgentCompactionQualityGuardConfig = {
/** Enable compaction summary quality audits and regeneration retries. Default: false. */
enabled?: boolean;
/** Maximum regeneration retries after a failed quality audit. Default: 1 when enabled. */
maxRetries?: number;
};
export type AgentCompactionConfig = {
/** Compaction summarization mode. */
@@ -304,6 +310,8 @@ export type AgentCompactionConfig = {
identifierPolicy?: AgentCompactionIdentifierPolicy;
/** Custom identifier-preservation instructions used when identifierPolicy is "custom". */
identifierInstructions?: string;
/** Optional quality-audit retries for safeguard compaction summaries. */
qualityGuard?: AgentCompactionQualityGuardConfig;
/** Pre-compaction memory flush (agentic turn). Default: enabled. */
memoryFlush?: AgentCompactionMemoryFlushConfig;
};

View File

@@ -95,6 +95,13 @@ export const AgentDefaultsSchema = z
.union([z.literal("strict"), z.literal("off"), z.literal("custom")])
.optional(),
identifierInstructions: z.string().optional(),
qualityGuard: z
.object({
enabled: z.boolean().optional(),
maxRetries: z.number().int().nonnegative().optional(),
})
.strict()
.optional(),
memoryFlush: z
.object({
enabled: z.boolean().optional(),