mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:10:44 +00:00
fix(compaction): honor manual keepRecentTokens
This commit is contained in:
@@ -67,6 +67,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Compaction: honor explicit `agents.defaults.compaction.keepRecentTokens` for manual `/compact`, re-distill safeguard summaries instead of snowballing previous summaries, and enable safeguard summary quality checks by default. Fixes #71357. Thanks @WhiteGiverMa.
|
||||
- Sessions: honor configured `session.maintenance` settings during load-time maintenance instead of falling back to default entry caps. Fixes #71356. Thanks @comolago.
|
||||
- Browser/sandbox: pass the resolved `browser.ssrfPolicy` into sandbox browser bridges and refresh cached bridges when the effective policy changes, so sandboxed browser navigation honors private-network opt-ins. Fixes #45153 and #57055. Thanks @jzakirov, @zuoanCo, and @kybrcore.
|
||||
- Browser/proxy: keep Gateway/provider proxy environment variables from proxying the OpenClaw-managed browser, so `HTTP_PROXY` and `HTTPS_PROXY` no longer block ordinary browser navigation. Fixes #71358. Thanks @Sanjays2402.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
8f23e853ccde6cd021b84b32fe205f456f8516667683d16c9b56d6598f608989 config-baseline.json
|
||||
037bf4a873587adb8349f531c0ad79cd4f90e01712f5aa5d8b4387be73538a7f config-baseline.core.json
|
||||
71ef32b7723f64d4a84ac43bb6d41ff21e0d77a099b42e026d8b0d3d5301f917 config-baseline.json
|
||||
cfab1910132ed23777005e0c650a13f44626b0450963f733e9de56a13323ae2b config-baseline.core.json
|
||||
22d7cd6d8279146b2d79c9531a55b80b52a2c99c81338c508104729154fdd02d config-baseline.channel.json
|
||||
86f615b7d267b03888af0af7ccb3f8232a6b636f8a741d522ff425e46729ba81 config-baseline.plugin.json
|
||||
|
||||
@@ -113,6 +113,11 @@ the summary:
|
||||
/compact Focus on the API design decisions
|
||||
```
|
||||
|
||||
When `agents.defaults.compaction.keepRecentTokens` is set, manual compaction
|
||||
honors that Pi cut-point and keeps the recent tail in rebuilt context. Without
|
||||
an explicit keep budget, manual compaction behaves as a hard checkpoint and
|
||||
continues from the new summary alone.
|
||||
|
||||
## Using a different model
|
||||
|
||||
By default, compaction uses your agent's primary model. You can use a more
|
||||
|
||||
@@ -542,8 +542,10 @@ Periodic heartbeat runs.
|
||||
provider: "my-provider", // id of a registered compaction provider plugin (optional)
|
||||
timeoutSeconds: 900,
|
||||
reserveTokensFloor: 24000,
|
||||
keepRecentTokens: 50000,
|
||||
identifierPolicy: "strict", // strict | off | custom
|
||||
identifierInstructions: "Preserve deployment IDs, ticket IDs, and host:port pairs exactly.", // used when identifierPolicy=custom
|
||||
qualityGuard: { enabled: true, maxRetries: 1 },
|
||||
postCompactionSections: ["Session Startup", "Red Lines"], // [] disables reinjection
|
||||
model: "openrouter/anthropic/claude-sonnet-4-6", // optional compaction-only model override
|
||||
notifyUser: true, // send brief notices when compaction starts and completes (default: false)
|
||||
@@ -562,8 +564,10 @@ Periodic heartbeat runs.
|
||||
- `mode`: `default` or `safeguard` (chunked summarization for long histories). See [Compaction](/concepts/compaction).
|
||||
- `provider`: id of a registered compaction provider plugin. When set, the provider's `summarize()` is called instead of built-in LLM summarization. Falls back to built-in on failure. Setting a provider forces `mode: "safeguard"`. See [Compaction](/concepts/compaction).
|
||||
- `timeoutSeconds`: maximum seconds allowed for a single compaction operation before OpenClaw aborts it. Default: `900`.
|
||||
- `keepRecentTokens`: Pi cut-point budget for keeping the most recent transcript tail verbatim. Manual `/compact` honors this when explicitly set; otherwise manual compaction is a hard checkpoint.
|
||||
- `identifierPolicy`: `strict` (default), `off`, or `custom`. `strict` prepends built-in opaque identifier retention guidance during compaction summarization.
|
||||
- `identifierInstructions`: optional custom identifier-preservation text used when `identifierPolicy=custom`.
|
||||
- `qualityGuard`: retry-on-malformed-output checks for safeguard summaries. Enabled by default in safeguard mode; set `enabled: false` to skip the audit.
|
||||
- `postCompactionSections`: optional AGENTS.md H2/H3 section names to re-inject after compaction. Defaults to `["Session Startup", "Red Lines"]`; set `[]` to disable reinjection. When unset or explicitly set to that default pair, older `Every Session`/`Safety` headings are also accepted as a legacy fallback.
|
||||
- `model`: optional `provider/model-id` override for compaction summarization only. Use this when the main session should keep one model but compaction summaries should run on another; when unset, compaction uses the session's primary model.
|
||||
- `notifyUser`: when `true`, sends brief notices to the user when compaction starts and when it completes (for example, "Compacting context..." and "Compaction complete"). Disabled by default to keep compaction silent.
|
||||
|
||||
@@ -267,6 +267,10 @@ OpenClaw also enforces a safety floor for embedded runs:
|
||||
- Default floor is `20000` tokens.
|
||||
- Set `agents.defaults.compaction.reserveTokensFloor: 0` to disable the floor.
|
||||
- If it’s already higher, OpenClaw leaves it alone.
|
||||
- Manual `/compact` honors an explicit `agents.defaults.compaction.keepRecentTokens`
|
||||
and keeps Pi's recent-tail cut point. Without an explicit keep budget,
|
||||
manual compaction remains a hard checkpoint and rebuilt context starts from
|
||||
the new summary.
|
||||
|
||||
Why: leave enough headroom for multi-turn “housekeeping” (like memory writes) before compaction becomes unavoidable.
|
||||
|
||||
@@ -283,6 +287,10 @@ Plugins can register a compaction provider via `registerCompactionProvider()` on
|
||||
- Setting a `provider` forces `mode: "safeguard"`.
|
||||
- Providers receive the same compaction instructions and identifier-preservation policy as the built-in path.
|
||||
- The safeguard still preserves recent-turn and split-turn suffix context after provider output.
|
||||
- Built-in safeguard summarization re-distills prior summaries with new messages
|
||||
instead of preserving the full previous summary verbatim.
|
||||
- Safeguard mode enables summary quality audits by default; set
|
||||
`qualityGuard.enabled: false` to skip retry-on-malformed-output behavior.
|
||||
- If the provider fails or returns an empty result, OpenClaw falls back to built-in LLM summarization automatically.
|
||||
- Abort/timeout signals are re-thrown (not swallowed) to respect caller cancellation.
|
||||
|
||||
|
||||
@@ -1059,6 +1059,8 @@ export async function compactEmbeddedPiSessionDirect(
|
||||
try {
|
||||
const hardenedBoundary = await hardenManualCompactionBoundary({
|
||||
sessionFile: params.sessionFile,
|
||||
preserveRecentTail:
|
||||
typeof params.config?.agents?.defaults?.compaction?.keepRecentTokens === "number",
|
||||
});
|
||||
if (hardenedBoundary.applied) {
|
||||
effectiveFirstKeptEntryId =
|
||||
|
||||
@@ -45,7 +45,7 @@ function expectSafeguardRuntime(
|
||||
}
|
||||
|
||||
describe("buildEmbeddedExtensionFactories", () => {
|
||||
it("does not opt safeguard mode into quality-guard retries", () => {
|
||||
it("enables quality-guard retries by default in safeguard mode", () => {
|
||||
const cfg = {
|
||||
agents: {
|
||||
defaults: {
|
||||
@@ -55,6 +55,24 @@ describe("buildEmbeddedExtensionFactories", () => {
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
expectSafeguardRuntime(cfg, {
|
||||
qualityGuardEnabled: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("honors explicit safeguard quality-guard disablement", () => {
|
||||
const cfg = {
|
||||
agents: {
|
||||
defaults: {
|
||||
compaction: {
|
||||
mode: "safeguard",
|
||||
qualityGuard: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
expectSafeguardRuntime(cfg, {
|
||||
qualityGuardEnabled: false,
|
||||
});
|
||||
|
||||
@@ -162,7 +162,7 @@ export function buildEmbeddedExtensionFactories(params: {
|
||||
identifierPolicy: compactionCfg?.identifierPolicy,
|
||||
identifierInstructions: compactionCfg?.identifierInstructions,
|
||||
customInstructions: compactionCfg?.customInstructions,
|
||||
qualityGuardEnabled: qualityGuardCfg?.enabled ?? false,
|
||||
qualityGuardEnabled: qualityGuardCfg?.enabled ?? true,
|
||||
qualityGuardMaxRetries: qualityGuardCfg?.maxRetries,
|
||||
model: params.model,
|
||||
recentTurnsPreserve: compactionCfg?.recentTurnsPreserve,
|
||||
|
||||
@@ -118,6 +118,39 @@ describe("hardenManualCompactionBoundary", () => {
|
||||
expect(afterTexts.join("\n")).not.toContain("detailed new answer");
|
||||
});
|
||||
|
||||
it("keeps the upstream recent tail when requested", async () => {
|
||||
const dir = await makeTmpDir();
|
||||
const session = SessionManager.create(dir, dir);
|
||||
|
||||
session.appendMessage({ role: "user", content: "old question", timestamp: 1 });
|
||||
session.appendMessage(createAssistantTextMessage("old answer", 2));
|
||||
const keepId = session.getBranch().at(-1)?.id;
|
||||
expect(keepId).toBeTruthy();
|
||||
const latestCompactionId = session.appendCompaction("fresh summary", keepId!, 200);
|
||||
const sessionFile = session.getSessionFile();
|
||||
expect(sessionFile).toBeTruthy();
|
||||
|
||||
const hardened = await hardenManualCompactionBoundary({
|
||||
sessionFile: sessionFile!,
|
||||
preserveRecentTail: true,
|
||||
});
|
||||
expect(hardened.applied).toBe(false);
|
||||
expect(hardened.firstKeptEntryId).toBe(keepId);
|
||||
|
||||
const reopened = SessionManager.open(sessionFile!);
|
||||
const latest = reopened.getLeafEntry();
|
||||
expect(latest?.type).toBe("compaction");
|
||||
if (!latest || latest.type !== "compaction") {
|
||||
throw new Error("expected latest leaf to be a compaction entry");
|
||||
}
|
||||
expect(latest.id).toBe(latestCompactionId);
|
||||
expect(latest.firstKeptEntryId).toBe(keepId);
|
||||
expect(reopened.buildSessionContext().messages.map((message) => message.role)).toEqual([
|
||||
"compactionSummary",
|
||||
"assistant",
|
||||
]);
|
||||
});
|
||||
|
||||
it("is a no-op when the latest leaf is not a compaction entry", async () => {
|
||||
const dir = await makeTmpDir();
|
||||
const session = SessionManager.create(dir, dir);
|
||||
|
||||
@@ -40,6 +40,7 @@ function replaceLatestCompactionBoundary(params: {
|
||||
|
||||
export async function hardenManualCompactionBoundary(params: {
|
||||
sessionFile: string;
|
||||
preserveRecentTail?: boolean;
|
||||
}): Promise<HardenedManualCompactionBoundary> {
|
||||
const sessionManager = SessionManager.open(params.sessionFile) as Partial<SessionManagerLike>;
|
||||
if (
|
||||
@@ -68,6 +69,19 @@ export async function hardenManualCompactionBoundary(params: {
|
||||
};
|
||||
}
|
||||
|
||||
if (params.preserveRecentTail) {
|
||||
const sessionContext = sessionManager.buildSessionContext();
|
||||
return {
|
||||
applied: false,
|
||||
firstKeptEntryId: leaf.firstKeptEntryId,
|
||||
leafId:
|
||||
typeof sessionManager.getLeafId === "function"
|
||||
? (sessionManager.getLeafId() ?? undefined)
|
||||
: undefined,
|
||||
messages: sessionContext.messages,
|
||||
};
|
||||
}
|
||||
|
||||
if (leaf.firstKeptEntryId === leaf.id) {
|
||||
const sessionContext = sessionManager.buildSessionContext();
|
||||
return {
|
||||
|
||||
@@ -60,6 +60,7 @@ export function buildCompactionStructureInstructions(
|
||||
...REQUIRED_SUMMARY_SECTIONS,
|
||||
identifierSectionInstruction,
|
||||
"Do not omit unresolved asks from the user.",
|
||||
"When prior compaction summaries are present, re-distill them with new messages and remove stale duplicate detail.",
|
||||
].join("\n");
|
||||
const custom = customInstructions?.trim();
|
||||
if (!custom) {
|
||||
|
||||
@@ -38,6 +38,7 @@ const {
|
||||
formatPreservedTurnsSection,
|
||||
buildCompactionStructureInstructions,
|
||||
buildStructuredFallbackSummary,
|
||||
prependPreviousSummaryForRedistill,
|
||||
appendSummarySection,
|
||||
resolveRecentTurnsPreserve,
|
||||
resolveQualityGuardMaxRetries,
|
||||
@@ -1198,6 +1199,20 @@ describe("compaction-safeguard recent-turn preservation", () => {
|
||||
expect(buildStructuredFallbackSummary(structured)).toBe(structured);
|
||||
});
|
||||
|
||||
it("converts previous summaries into redistill input instead of update-prompt state", () => {
|
||||
const messages: AgentMessage[] = [{ role: "user", content: "new context", timestamp: 1 }];
|
||||
const redistillMessages = prependPreviousSummaryForRedistill({
|
||||
messages,
|
||||
previousSummary: "## Goal\nold duplicate summary",
|
||||
});
|
||||
|
||||
expect(redistillMessages).toHaveLength(2);
|
||||
expect(redistillMessages[0]?.role).toBe("user");
|
||||
expect(JSON.stringify(redistillMessages[0])).toContain("<previous-compaction-summary>");
|
||||
expect(JSON.stringify(redistillMessages[0])).toContain("Prune stale, duplicate");
|
||||
expect(redistillMessages[1]).toBe(messages[0]);
|
||||
});
|
||||
|
||||
it("restructures summaries with near-match headings instead of reusing them", () => {
|
||||
const nearMatch = [
|
||||
"## Decisions",
|
||||
@@ -1685,7 +1700,72 @@ describe("compaction-safeguard recent-turn preservation", () => {
|
||||
expect(summary).toContain("legacy summary without headings");
|
||||
});
|
||||
|
||||
it("re-distills prior summaries on the LLM path instead of preserving them verbatim", async () => {
|
||||
mockSummarizeInStages.mockReset();
|
||||
mockSummarizeInStages.mockResolvedValue(
|
||||
[
|
||||
"## Decisions",
|
||||
"Condensed prior context with latest status.",
|
||||
"## Open TODOs",
|
||||
"None.",
|
||||
"## Constraints/Rules",
|
||||
"Preserve identifiers.",
|
||||
"## Pending user asks",
|
||||
"latest ask status",
|
||||
"## Exact identifiers",
|
||||
"None.",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
const sessionManager = stubSessionManager();
|
||||
const model = createAnthropicModelFixture();
|
||||
setCompactionSafeguardRuntime(sessionManager, {
|
||||
model,
|
||||
recentTurnsPreserve: 0,
|
||||
qualityGuardEnabled: true,
|
||||
qualityGuardMaxRetries: 1,
|
||||
});
|
||||
|
||||
const compactionHandler = createCompactionHandler();
|
||||
const getApiKeyMock = vi.fn().mockResolvedValue("test-key");
|
||||
const mockContext = createCompactionContext({
|
||||
sessionManager,
|
||||
getApiKeyMock,
|
||||
});
|
||||
const event = {
|
||||
preparation: {
|
||||
messagesToSummarize: [{ role: "user", content: "latest ask status", timestamp: 1 }],
|
||||
turnPrefixMessages: [],
|
||||
firstKeptEntryId: "entry-1",
|
||||
tokensBefore: 1_500,
|
||||
fileOps: {
|
||||
read: [],
|
||||
edited: [],
|
||||
written: [],
|
||||
},
|
||||
settings: { reserveTokens: 4_000 },
|
||||
previousSummary: "## Goal\nOld duplicated section that should be re-distilled.",
|
||||
isSplitTurn: false,
|
||||
},
|
||||
customInstructions: "",
|
||||
signal: new AbortController().signal,
|
||||
};
|
||||
|
||||
const result = (await compactionHandler(event, mockContext)) as {
|
||||
cancel?: boolean;
|
||||
compaction?: { summary?: string };
|
||||
};
|
||||
|
||||
expect(result.cancel).not.toBe(true);
|
||||
expect(mockSummarizeInStages).toHaveBeenCalledTimes(1);
|
||||
const call = mockSummarizeInStages.mock.calls[0]?.[0];
|
||||
expect(call?.previousSummary).toBeUndefined();
|
||||
expect(JSON.stringify(call?.messages[0])).toContain("<previous-compaction-summary>");
|
||||
expect(JSON.stringify(call?.messages[0])).toContain("Old duplicated section");
|
||||
});
|
||||
|
||||
it("passes compaction instructions to providers and preserves suffix context", async () => {
|
||||
mockSummarizeInStages.mockReset();
|
||||
const providerSummarize = vi.fn().mockResolvedValue("provider summary body");
|
||||
registerCompactionProvider({
|
||||
id: "test-provider",
|
||||
|
||||
@@ -67,10 +67,37 @@ const DEFAULT_QUALITY_GUARD_MAX_RETRIES = 1;
|
||||
const MAX_RECENT_TURNS_PRESERVE = 12;
|
||||
const MAX_QUALITY_GUARD_MAX_RETRIES = 3;
|
||||
const MAX_RECENT_TURN_TEXT_CHARS = 600;
|
||||
const PREVIOUS_SUMMARY_REDISTILL_PREFIX =
|
||||
"Previous compaction summary to re-distill with the current conversation. " +
|
||||
"Prune stale, duplicate, or superseded details instead of preserving it verbatim.";
|
||||
const compactionSafeguardDeps = {
|
||||
summarizeInStages,
|
||||
};
|
||||
|
||||
function buildPreviousSummaryMessage(previousSummary: string): AgentMessage {
|
||||
return {
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `<previous-compaction-summary>\n${PREVIOUS_SUMMARY_REDISTILL_PREFIX}\n\n${previousSummary.trim()}\n</previous-compaction-summary>`,
|
||||
},
|
||||
],
|
||||
timestamp: 0,
|
||||
} as AgentMessage;
|
||||
}
|
||||
|
||||
function prependPreviousSummaryForRedistill(params: {
|
||||
messages: AgentMessage[];
|
||||
previousSummary?: string;
|
||||
}): AgentMessage[] {
|
||||
const previousSummary = params.previousSummary?.trim();
|
||||
if (!previousSummary) {
|
||||
return params.messages;
|
||||
}
|
||||
return [buildPreviousSummaryMessage(previousSummary), ...params.messages];
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt provider-based summarization. Returns the summary string on success,
|
||||
* or `undefined` when the caller should fall back to built-in LLM summarization.
|
||||
@@ -125,8 +152,12 @@ async function summarizeViaLLM(params: {
|
||||
summarizationInstructions?: Parameters<typeof summarizeInStages>[0]["summarizationInstructions"];
|
||||
previousSummary?: string;
|
||||
}): Promise<string> {
|
||||
return compactionSafeguardDeps.summarizeInStages({
|
||||
const messages = prependPreviousSummaryForRedistill({
|
||||
messages: params.messages,
|
||||
previousSummary: params.previousSummary,
|
||||
});
|
||||
return compactionSafeguardDeps.summarizeInStages({
|
||||
messages,
|
||||
model: params.model,
|
||||
apiKey: params.apiKey,
|
||||
headers: params.headers,
|
||||
@@ -136,7 +167,7 @@ async function summarizeViaLLM(params: {
|
||||
contextWindow: params.contextWindow,
|
||||
customInstructions: params.customInstructions,
|
||||
summarizationInstructions: params.summarizationInstructions,
|
||||
previousSummary: params.previousSummary,
|
||||
previousSummary: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1166,6 +1197,7 @@ export const __testing = {
|
||||
formatSplitTurnContextSection,
|
||||
buildCompactionStructureInstructions,
|
||||
buildStructuredFallbackSummary,
|
||||
prependPreviousSummaryForRedistill,
|
||||
appendSummarySection,
|
||||
resolveRecentTurnsPreserve,
|
||||
resolveQualityGuardMaxRetries,
|
||||
|
||||
@@ -4722,7 +4722,7 @@ export const GENERATED_BASE_CONFIG_SCHEMA: BaseConfigSchemaResponse = {
|
||||
type: "boolean",
|
||||
title: "Compaction Quality Guard Enabled",
|
||||
description:
|
||||
"Enables summary quality audits and regeneration retries for safeguard compaction. Default: false, so safeguard mode alone does not turn on retry behavior.",
|
||||
"Enables summary quality audits and regeneration retries for safeguard compaction. Default: true in safeguard mode.",
|
||||
},
|
||||
maxRetries: {
|
||||
type: "integer",
|
||||
@@ -4736,7 +4736,7 @@ export const GENERATED_BASE_CONFIG_SCHEMA: BaseConfigSchemaResponse = {
|
||||
additionalProperties: false,
|
||||
title: "Compaction Quality Guard",
|
||||
description:
|
||||
"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.",
|
||||
"Quality-audit retry settings for safeguard compaction summaries. Safeguard mode enables this by default; set enabled: false to skip summary audits and regeneration.",
|
||||
},
|
||||
postIndexSync: {
|
||||
type: "string",
|
||||
@@ -26066,12 +26066,12 @@ export const GENERATED_BASE_CONFIG_SCHEMA: BaseConfigSchemaResponse = {
|
||||
},
|
||||
"agents.defaults.compaction.qualityGuard": {
|
||||
label: "Compaction Quality Guard",
|
||||
help: "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.",
|
||||
help: "Quality-audit retry settings for safeguard compaction summaries. Safeguard mode enables this by default; set enabled: false to skip summary audits and regeneration.",
|
||||
tags: ["advanced"],
|
||||
},
|
||||
"agents.defaults.compaction.qualityGuard.enabled": {
|
||||
label: "Compaction Quality Guard Enabled",
|
||||
help: "Enables summary quality audits and regeneration retries for safeguard compaction. Default: false, so safeguard mode alone does not turn on retry behavior.",
|
||||
help: "Enables summary quality audits and regeneration retries for safeguard compaction. Default: true in safeguard mode.",
|
||||
tags: ["advanced"],
|
||||
},
|
||||
"agents.defaults.compaction.qualityGuard.maxRetries": {
|
||||
|
||||
@@ -1236,9 +1236,9 @@ export const FIELD_HELP: Record<string, string> = {
|
||||
"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.",
|
||||
"Quality-audit retry settings for safeguard compaction summaries. Safeguard mode enables this by default; set enabled: false to skip summary audits and regeneration.",
|
||||
"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.",
|
||||
"Enables summary quality audits and regeneration retries for safeguard compaction. Default: true in safeguard mode.",
|
||||
"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.postIndexSync":
|
||||
|
||||
Reference in New Issue
Block a user