mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 11:08:37 +00:00
fix: keep safeguard quality guard opt-in (openclaw#25556) thanks @rodrigouroz
This commit is contained in:
@@ -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/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.
|
- 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.
|
- 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
|
### Breaking
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import compactionSafeguardExtension from "../pi-extensions/compaction-safeguard.
|
|||||||
import { buildEmbeddedExtensionFactories } from "./extensions.js";
|
import { buildEmbeddedExtensionFactories } from "./extensions.js";
|
||||||
|
|
||||||
describe("buildEmbeddedExtensionFactories", () => {
|
describe("buildEmbeddedExtensionFactories", () => {
|
||||||
it("wires safeguard quality-guard runtime flags", () => {
|
it("does not opt safeguard mode into quality-guard retries", () => {
|
||||||
const sessionManager = {} as SessionManager;
|
const sessionManager = {} as SessionManager;
|
||||||
const model = {
|
const model = {
|
||||||
id: "claude-sonnet-4-20250514",
|
id: "claude-sonnet-4-20250514",
|
||||||
@@ -31,10 +31,44 @@ describe("buildEmbeddedExtensionFactories", () => {
|
|||||||
model,
|
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(factories).toContain(compactionSafeguardExtension);
|
||||||
expect(getCompactionSafeguardRuntime(sessionManager)).toMatchObject({
|
expect(getCompactionSafeguardRuntime(sessionManager)).toMatchObject({
|
||||||
qualityGuardEnabled: true,
|
qualityGuardEnabled: true,
|
||||||
qualityGuardMaxRetries: 1,
|
qualityGuardMaxRetries: 2,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ export function buildEmbeddedExtensionFactories(params: {
|
|||||||
const factories: ExtensionFactory[] = [];
|
const factories: ExtensionFactory[] = [];
|
||||||
if (resolveCompactionMode(params.cfg) === "safeguard") {
|
if (resolveCompactionMode(params.cfg) === "safeguard") {
|
||||||
const compactionCfg = params.cfg?.agents?.defaults?.compaction;
|
const compactionCfg = params.cfg?.agents?.defaults?.compaction;
|
||||||
|
const qualityGuardCfg = compactionCfg?.qualityGuard;
|
||||||
const contextWindowInfo = resolveContextWindowInfo({
|
const contextWindowInfo = resolveContextWindowInfo({
|
||||||
cfg: params.cfg,
|
cfg: params.cfg,
|
||||||
provider: params.provider,
|
provider: params.provider,
|
||||||
@@ -83,8 +84,8 @@ export function buildEmbeddedExtensionFactories(params: {
|
|||||||
contextWindowTokens: contextWindowInfo.tokens,
|
contextWindowTokens: contextWindowInfo.tokens,
|
||||||
identifierPolicy: compactionCfg?.identifierPolicy,
|
identifierPolicy: compactionCfg?.identifierPolicy,
|
||||||
identifierInstructions: compactionCfg?.identifierInstructions,
|
identifierInstructions: compactionCfg?.identifierInstructions,
|
||||||
qualityGuardEnabled: true,
|
qualityGuardEnabled: qualityGuardCfg?.enabled ?? false,
|
||||||
qualityGuardMaxRetries: 1,
|
qualityGuardMaxRetries: qualityGuardCfg?.maxRetries,
|
||||||
model: params.model,
|
model: params.model,
|
||||||
});
|
});
|
||||||
factories.push(compactionSafeguardExtension);
|
factories.push(compactionSafeguardExtension);
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ describe("config compaction settings", () => {
|
|||||||
reserveTokensFloor: 12_345,
|
reserveTokensFloor: 12_345,
|
||||||
identifierPolicy: "custom",
|
identifierPolicy: "custom",
|
||||||
identifierInstructions: "Keep ticket IDs unchanged.",
|
identifierInstructions: "Keep ticket IDs unchanged.",
|
||||||
|
qualityGuard: {
|
||||||
|
enabled: true,
|
||||||
|
maxRetries: 2,
|
||||||
|
},
|
||||||
memoryFlush: {
|
memoryFlush: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
softThresholdTokens: 1234,
|
softThresholdTokens: 1234,
|
||||||
@@ -34,6 +38,8 @@ describe("config compaction settings", () => {
|
|||||||
expect(cfg.agents?.defaults?.compaction?.identifierInstructions).toBe(
|
expect(cfg.agents?.defaults?.compaction?.identifierInstructions).toBe(
|
||||||
"Keep ticket IDs unchanged.",
|
"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?.enabled).toBe(false);
|
||||||
expect(cfg.agents?.defaults?.compaction?.memoryFlush?.softThresholdTokens).toBe(1234);
|
expect(cfg.agents?.defaults?.compaction?.memoryFlush?.softThresholdTokens).toBe(1234);
|
||||||
expect(cfg.agents?.defaults?.compaction?.memoryFlush?.prompt).toBe("Write notes.");
|
expect(cfg.agents?.defaults?.compaction?.memoryFlush?.prompt).toBe("Write notes.");
|
||||||
|
|||||||
@@ -370,6 +370,9 @@ const TARGET_KEYS = [
|
|||||||
"agents.defaults.compaction.maxHistoryShare",
|
"agents.defaults.compaction.maxHistoryShare",
|
||||||
"agents.defaults.compaction.identifierPolicy",
|
"agents.defaults.compaction.identifierPolicy",
|
||||||
"agents.defaults.compaction.identifierInstructions",
|
"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",
|
||||||
"agents.defaults.compaction.memoryFlush.enabled",
|
"agents.defaults.compaction.memoryFlush.enabled",
|
||||||
"agents.defaults.compaction.memoryFlush.softThresholdTokens",
|
"agents.defaults.compaction.memoryFlush.softThresholdTokens",
|
||||||
|
|||||||
@@ -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.',
|
'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":
|
"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.',
|
'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":
|
"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.",
|
"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":
|
"agents.defaults.compaction.memoryFlush.enabled":
|
||||||
|
|||||||
@@ -434,6 +434,9 @@ export const FIELD_LABELS: Record<string, string> = {
|
|||||||
"agents.defaults.compaction.maxHistoryShare": "Compaction Max History Share",
|
"agents.defaults.compaction.maxHistoryShare": "Compaction Max History Share",
|
||||||
"agents.defaults.compaction.identifierPolicy": "Compaction Identifier Policy",
|
"agents.defaults.compaction.identifierPolicy": "Compaction Identifier Policy",
|
||||||
"agents.defaults.compaction.identifierInstructions": "Compaction Identifier Instructions",
|
"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": "Compaction Memory Flush",
|
||||||
"agents.defaults.compaction.memoryFlush.enabled": "Compaction Memory Flush Enabled",
|
"agents.defaults.compaction.memoryFlush.enabled": "Compaction Memory Flush Enabled",
|
||||||
"agents.defaults.compaction.memoryFlush.softThresholdTokens":
|
"agents.defaults.compaction.memoryFlush.softThresholdTokens":
|
||||||
|
|||||||
@@ -288,6 +288,12 @@ export type AgentDefaultsConfig = {
|
|||||||
|
|
||||||
export type AgentCompactionMode = "default" | "safeguard";
|
export type AgentCompactionMode = "default" | "safeguard";
|
||||||
export type AgentCompactionIdentifierPolicy = "strict" | "off" | "custom";
|
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 = {
|
export type AgentCompactionConfig = {
|
||||||
/** Compaction summarization mode. */
|
/** Compaction summarization mode. */
|
||||||
@@ -304,6 +310,8 @@ export type AgentCompactionConfig = {
|
|||||||
identifierPolicy?: AgentCompactionIdentifierPolicy;
|
identifierPolicy?: AgentCompactionIdentifierPolicy;
|
||||||
/** Custom identifier-preservation instructions used when identifierPolicy is "custom". */
|
/** Custom identifier-preservation instructions used when identifierPolicy is "custom". */
|
||||||
identifierInstructions?: string;
|
identifierInstructions?: string;
|
||||||
|
/** Optional quality-audit retries for safeguard compaction summaries. */
|
||||||
|
qualityGuard?: AgentCompactionQualityGuardConfig;
|
||||||
/** Pre-compaction memory flush (agentic turn). Default: enabled. */
|
/** Pre-compaction memory flush (agentic turn). Default: enabled. */
|
||||||
memoryFlush?: AgentCompactionMemoryFlushConfig;
|
memoryFlush?: AgentCompactionMemoryFlushConfig;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -95,6 +95,13 @@ export const AgentDefaultsSchema = z
|
|||||||
.union([z.literal("strict"), z.literal("off"), z.literal("custom")])
|
.union([z.literal("strict"), z.literal("off"), z.literal("custom")])
|
||||||
.optional(),
|
.optional(),
|
||||||
identifierInstructions: z.string().optional(),
|
identifierInstructions: z.string().optional(),
|
||||||
|
qualityGuard: z
|
||||||
|
.object({
|
||||||
|
enabled: z.boolean().optional(),
|
||||||
|
maxRetries: z.number().int().nonnegative().optional(),
|
||||||
|
})
|
||||||
|
.strict()
|
||||||
|
.optional(),
|
||||||
memoryFlush: z
|
memoryFlush: z
|
||||||
.object({
|
.object({
|
||||||
enabled: z.boolean().optional(),
|
enabled: z.boolean().optional(),
|
||||||
|
|||||||
Reference in New Issue
Block a user