mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 11:55:47 -06:00
fix(compaction): default maintenance reasoning to low (#126421)
* fix(compaction): default maintenance reasoning to low * test(compaction): cover explicit inherit fallback * test(compaction): preserve inherit literal type
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"core": 2333,
|
||||
"channel": 3582,
|
||||
"plugin": 4002
|
||||
"plugin": 3978
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ce0ade9436d094a159eaa16faa5ed92cc8b57d7dc1c5d5baa3a6f7c5147baf4b config-baseline.json
|
||||
0b84d1b0fabf0242f434f974bc7dd32ece2ac277f728d033e573e679dee042f3 config-baseline.core.json
|
||||
2b756ea29fdcb1cd3e7fc22b989ce27fe4213b9e0fe86c55dd80b3e88991a2c1 config-baseline.channel.json
|
||||
2eb56b8a47ab34006bd56d95f7379ac859661bed2f9335ae40b681793afaded4 config-baseline.plugin.json
|
||||
0442d66ded7bed6a7a1825888835034d55eb93d24e57693f762611b8f5ef48d8 config-baseline.json
|
||||
e3ec0a72fea5fbcc69b71a2687de77e8dcb58aab8ba2631d1de6fa5c0f43e9bf config-baseline.core.json
|
||||
c89feef2a5109dc979f5f2b6b32fbafdc93174d6eb1b6a7e525ab04beb93891c config-baseline.channel.json
|
||||
ed7c7e8dfe9d676ebbf60b5ee55f72d1f9e0286ffb37743f40f6c337816b166d config-baseline.plugin.json
|
||||
|
||||
@@ -600,7 +600,7 @@ An explicit request `agentId` always wins. Delegated consults with a requesting
|
||||
enabled: false, // disable embedded proactive auto-compaction (default: true)
|
||||
mode: "safeguard", // default | safeguard
|
||||
provider: "my-provider", // id of a registered compaction provider plugin (optional)
|
||||
thinkingLevel: "low", // optional compaction-only thinking override
|
||||
thinkingLevel: "low", // default; use "inherit" to reuse the session level
|
||||
timeoutSeconds: 180,
|
||||
keepRecentTokens: 50000,
|
||||
recentTurnsPreserve: 3,
|
||||
@@ -627,7 +627,7 @@ An explicit request `agentId` always wins. Delegated consults with a requesting
|
||||
- `enabled`: when `false`, disables threshold-driven auto-compaction inside the embedded agent runtime. OpenClaw's preflight and overflow-recovery compaction paths and manual `/compact` remain available. Default: `true`.
|
||||
- `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).
|
||||
- `thinkingLevel`: optional thinking level used only for embedded OpenClaw compaction summaries (`off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `adaptive`, `max`, or `ultra`). It overrides the session's current thinking level and is clamped to the selected compaction model/runtime. Leave unset to inherit the session level. Native Codex app-server compaction ignores this setting because the native compact request has no per-operation thinking override; OpenClaw logs a warning when configured.
|
||||
- `thinkingLevel`: thinking level used only for embedded OpenClaw compaction summaries (`off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `adaptive`, `max`, `ultra`, or `inherit`). It defaults to `low`; set `inherit` to reuse the session's current thinking level. The selected level is clamped to the compaction model/runtime. Native Codex app-server compaction ignores this setting because the native compact request has no per-operation thinking override; OpenClaw logs a warning when configured.
|
||||
- `timeoutSeconds`: maximum seconds allowed for a single compaction operation before OpenClaw aborts it. Default: `180`.
|
||||
- `keepRecentTokens`: agent cut-point budget for keeping the most recent transcript tail verbatim. Default: `20000`.
|
||||
- `recentTurnsPreserve`: number of most recent user/assistant turns kept verbatim outside safeguard summarization. Default: `3`.
|
||||
|
||||
@@ -197,7 +197,7 @@ More on limits: [/reference/token-use](/reference/token-use).
|
||||
|
||||
Compaction summarizes older conversation into a persisted `compaction` entry in the transcript and keeps recent messages intact. After compaction, future turns see the compaction summary plus messages after `firstKeptEntryId`. Compaction is **persistent**, unlike session pruning - see [/concepts/session-pruning](/concepts/session-pruning).
|
||||
|
||||
Embedded OpenClaw compaction inherits the session thinking level by default. Set `agents.defaults.compaction.thinkingLevel` to use a separate level for summary calls; the runtime clamps it to each concrete compaction model or fallback. Native Codex app-server compaction owns its compact request and cannot accept a per-compaction thinking override, so OpenClaw warns and leaves that setting to Codex.
|
||||
Embedded OpenClaw compaction uses `low` thinking by default. Set `agents.defaults.compaction.thinkingLevel: "inherit"` to reuse the session level, or choose another explicit level for summary calls; the runtime clamps it to each concrete compaction model or fallback. Native Codex app-server compaction owns its compact request and cannot accept a per-compaction thinking override, so OpenClaw warns and leaves that setting to Codex.
|
||||
|
||||
AGENTS.md section reinjection after compaction remains opt-in via `agents.defaults.compaction.postCompactionSections`. Plugins can add other prompt context through `before_prompt_build`.
|
||||
|
||||
|
||||
@@ -1606,6 +1606,7 @@ describe("compactEmbeddedAgentSessionDirect hooks", () => {
|
||||
config: {
|
||||
agents: {
|
||||
defaults: {
|
||||
compaction: { thinkingLevel: "inherit" as const },
|
||||
models: {
|
||||
"openai/gpt-5.6-sol": { agentRuntime: { id: "openclaw" } },
|
||||
},
|
||||
|
||||
@@ -67,25 +67,41 @@ describe("resolveEmbeddedCompactionThinkingLevel", () => {
|
||||
).toBe("high");
|
||||
});
|
||||
|
||||
it("inherits the session level and otherwise defaults to off", () => {
|
||||
it("defaults compaction to low without inheriting the session level", () => {
|
||||
expect(
|
||||
resolveEmbeddedCompactionThinkingLevel({
|
||||
provider: "demo",
|
||||
modelId: "demo-model",
|
||||
inheritedLevel: "medium",
|
||||
}),
|
||||
).toBe("medium");
|
||||
).toBe("low");
|
||||
expect(
|
||||
resolveEmbeddedCompactionThinkingLevel({
|
||||
provider: "demo",
|
||||
modelId: "demo-model",
|
||||
}),
|
||||
).toBe("off");
|
||||
).toBe("low");
|
||||
});
|
||||
|
||||
it("inherits the session level only when explicitly configured", () => {
|
||||
expect(
|
||||
resolveEmbeddedCompactionThinkingLevel({
|
||||
config: {
|
||||
agents: { defaults: { compaction: { thinkingLevel: "inherit" } } },
|
||||
} as unknown as OpenClawConfig,
|
||||
provider: "demo",
|
||||
modelId: "demo-model",
|
||||
inheritedLevel: "medium",
|
||||
}),
|
||||
).toBe("medium");
|
||||
});
|
||||
|
||||
it("preserves thinking when the resolved Ollama model reports reasoning support", () => {
|
||||
expect(
|
||||
resolveEmbeddedCompactionThinkingLevel({
|
||||
config: {
|
||||
agents: { defaults: { compaction: { thinkingLevel: "inherit" } } },
|
||||
},
|
||||
provider: "ollama",
|
||||
modelId: "qwen3.5:4b",
|
||||
inheritedLevel: "high",
|
||||
|
||||
@@ -71,8 +71,9 @@ export function resolveEmbeddedCompactionThinkingLevel(params: {
|
||||
sessionKey?: string;
|
||||
agentRuntime?: string | null;
|
||||
}): ThinkLevel {
|
||||
const configuredLevel = params.config?.agents?.defaults?.compaction?.thinkingLevel;
|
||||
const requestedLevel =
|
||||
params.config?.agents?.defaults?.compaction?.thinkingLevel ?? params.inheritedLevel;
|
||||
configuredLevel === "inherit" ? params.inheritedLevel : (configuredLevel ?? "low");
|
||||
if (!requestedLevel) {
|
||||
return "off";
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { SILENT_REPLY_TOKEN } from "../tokens.js";
|
||||
import { buildEmptyInteractiveReplyPayload } from "./agent-runner-failure-reply.js";
|
||||
import {
|
||||
buildEmptyInteractiveReplyPayload,
|
||||
buildPreflightCompactionFailureText,
|
||||
} from "./agent-runner-failure-reply.js";
|
||||
|
||||
const EMPTY_INTERACTIVE_REPLY_TEXT =
|
||||
"I finished the turn, but it did not produce a visible reply. Please try again, or start a new session if this keeps happening.";
|
||||
@@ -34,3 +37,16 @@ describe("buildEmptyInteractiveReplyPayload", () => {
|
||||
).toMatchObject({ text: EMPTY_INTERACTIVE_REPLY_TEXT, isError: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildPreflightCompactionFailureText", () => {
|
||||
it("identifies timeout failures without requiring verbose error details", () => {
|
||||
expect(
|
||||
buildPreflightCompactionFailureText(
|
||||
"Preflight compaction required but failed: Compaction timed out",
|
||||
),
|
||||
).toBe(
|
||||
"⚠️ Context is too large and auto-compaction timed out before it could finish. " +
|
||||
"Try again, use /compact, or use /new to start a fresh session.",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
} from "../../agents/auth-profiles/oauth-refresh-failure.js";
|
||||
import { sanitizeUserFacingText } from "../../agents/embedded-agent-helpers/sanitize-user-facing-text.js";
|
||||
import { renderUserFacingText } from "../../agents/embedded-agent-helpers/user-facing-text.js";
|
||||
import { classifyCompactionReason } from "../../agents/embedded-agent-runner/compact-reasons.js";
|
||||
import {
|
||||
describeFailoverError,
|
||||
findCliMaxTurnsError,
|
||||
@@ -158,11 +159,12 @@ export function buildPreflightCompactionFailureText(
|
||||
)
|
||||
.trim()
|
||||
.replace(/\s+/gu, " ");
|
||||
const reasonSuffix = options?.includeDetails && reason ? ` Reason: ${reason}.` : "";
|
||||
return (
|
||||
"⚠️ Context is too large and auto-compaction could not recover this turn." +
|
||||
`${reasonSuffix} Try again, use /compact, or use /new to start a fresh session.`
|
||||
);
|
||||
const isTimeout = classifyCompactionReason(reason) === "timeout";
|
||||
const reasonSuffix = options?.includeDetails && reason && !isTimeout ? ` Reason: ${reason}.` : "";
|
||||
const summary = isTimeout
|
||||
? "⚠️ Context is too large and auto-compaction timed out before it could finish."
|
||||
: "⚠️ Context is too large and auto-compaction could not recover this turn.";
|
||||
return `${summary}${reasonSuffix} Try again, use /compact, or use /new to start a fresh session.`;
|
||||
}
|
||||
|
||||
export function buildAuthProfileFailoverFailureText(error: unknown): string | null {
|
||||
|
||||
@@ -73,7 +73,7 @@ describe("config compaction settings", () => {
|
||||
expect(compaction?.qualityGuard?.maxRetries).toBe(99);
|
||||
});
|
||||
|
||||
it.each(["off", "low", "adaptive", "max", "ultra"] as const)(
|
||||
it.each(["off", "low", "adaptive", "max", "ultra", "inherit"] as const)(
|
||||
"preserves compaction thinkingLevel=%s during materialization",
|
||||
(thinkingLevel) => {
|
||||
expect(materializeCompactionConfig({ thinkingLevel })?.thinkingLevel).toBe(thinkingLevel);
|
||||
|
||||
@@ -129,7 +129,7 @@ export const AGENT_FIELD_HELP: Record<string, string> = {
|
||||
"agents.defaults.compaction.provider":
|
||||
"Id of a registered compaction provider plugin used for summarization. When set and the provider is registered, its summarize() method is called instead of the built-in summarizeInStages pipeline. Falls back to built-in on provider failure. Leave unset to use the default built-in summarization.",
|
||||
"agents.defaults.compaction.thinkingLevel":
|
||||
'Optional thinking level used only for embedded OpenClaw compaction summaries: "off", "minimal", "low", "medium", "high", "xhigh", "adaptive", "max", or "ultra". It overrides the session level and is clamped to the actual compaction model/runtime; leave unset to inherit the session level. Native Codex app-server compaction ignores this setting because its compact request has no per-operation thinking override, and OpenClaw logs a warning.',
|
||||
'Thinking level used only for embedded OpenClaw compaction summaries: "off", "minimal", "low", "medium", "high", "xhigh", "adaptive", "max", "ultra", or "inherit". The default is "low"; set "inherit" to reuse the session level. The selected level is clamped to the actual compaction model/runtime. Native Codex app-server compaction ignores this setting because its compact request has no per-operation thinking override, and OpenClaw logs a warning.',
|
||||
"agents.defaults.compaction.keepRecentTokens":
|
||||
"Minimum token budget preserved from the most recent conversation window during compaction. Use higher values to protect immediate context continuity and lower values to keep more long-tail history.",
|
||||
"agents.defaults.compaction.identifierPolicy":
|
||||
|
||||
@@ -385,6 +385,7 @@ export const ENUM_EXPECTATIONS: Record<string, string[]> = {
|
||||
'"adaptive"',
|
||||
'"max"',
|
||||
'"ultra"',
|
||||
'"inherit"',
|
||||
],
|
||||
"agents.defaults.compaction.identifierPolicy": ['"strict"', '"off"'],
|
||||
};
|
||||
|
||||
@@ -384,8 +384,8 @@ export type AgentCompactionConfig = {
|
||||
enabled?: boolean;
|
||||
/** Compaction summarization mode. */
|
||||
mode?: AgentCompactionMode;
|
||||
/** Override the session thinking level for embedded OpenClaw compaction summaries. */
|
||||
thinkingLevel?: AgentThinkingLevel;
|
||||
/** Thinking level for embedded OpenClaw compaction summaries. Default: low. */
|
||||
thinkingLevel?: AgentThinkingLevel | "inherit";
|
||||
/** Embedded OpenClaw keepRecentTokens budget used for cut-point selection. */
|
||||
keepRecentTokens?: number;
|
||||
/** Preserve this many most-recent user/assistant turns verbatim in compaction summary context. */
|
||||
|
||||
@@ -360,6 +360,7 @@ describe("agent defaults schema", () => {
|
||||
"adaptive",
|
||||
"max",
|
||||
"ultra",
|
||||
"inherit",
|
||||
] as const)("accepts compaction.thinkingLevel=%s", (thinkingLevel) => {
|
||||
const result = AgentDefaultsSchema.parse({ compaction: { thinkingLevel } })!;
|
||||
expect(result.compaction?.thinkingLevel).toBe(thinkingLevel);
|
||||
|
||||
@@ -141,7 +141,7 @@ export const AgentDefaultsSchema = z
|
||||
enabled: z.boolean().optional(),
|
||||
mode: z.union([z.literal("default"), z.literal("safeguard")]).optional(),
|
||||
provider: z.string().optional(),
|
||||
thinkingLevel: AgentThinkingLevelSchema.optional(),
|
||||
thinkingLevel: z.union([AgentThinkingLevelSchema, z.literal("inherit")]).optional(),
|
||||
keepRecentTokens: z.number().int().positive().optional(),
|
||||
identifierPolicy: z.union([z.literal("strict"), z.literal("off")]).optional(),
|
||||
recentTurnsPreserve: z.number().int().min(0).max(12).optional(),
|
||||
|
||||
Reference in New Issue
Block a user