mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 17:31:06 +00:00
fix: dedupe repeated bootstrap truncation warnings (#67906) (thanks @rubencu)
* Agents: dedupe bootstrap truncation warnings * Agents: normalize bootstrap warning cache bookkeeping * fix(agents): scope bootstrap warning dedupe by workspace * refactor(agents): simplify bootstrap warning wrapper --------- Co-authored-by: Ayaan Zaidi <hi@obviy.us>
This commit is contained in:
@@ -8,8 +8,10 @@ import {
|
||||
} from "../hooks/internal-hooks.js";
|
||||
import { makeTempWorkspace } from "../test-helpers/workspace.js";
|
||||
import {
|
||||
_resetBootstrapWarningCacheForTest,
|
||||
FULL_BOOTSTRAP_COMPLETED_CUSTOM_TYPE,
|
||||
hasCompletedBootstrapTurn,
|
||||
makeBootstrapWarn,
|
||||
resolveBootstrapContextForRun,
|
||||
resolveBootstrapFilesForRun,
|
||||
resolveContextInjectionMode,
|
||||
@@ -362,6 +364,69 @@ describe("hasCompletedBootstrapTurn", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("makeBootstrapWarn", () => {
|
||||
afterEach(() => {
|
||||
_resetBootstrapWarningCacheForTest();
|
||||
});
|
||||
|
||||
it("deduplicates repeated warnings for the same session and message", () => {
|
||||
const warnings: string[] = [];
|
||||
const warn = makeBootstrapWarn({
|
||||
sessionLabel: "agent:main:test-session",
|
||||
warn: (message) => warnings.push(message),
|
||||
});
|
||||
|
||||
warn?.("workspace bootstrap file MEMORY.md is 36697 chars (limit 12000); truncating");
|
||||
warn?.("workspace bootstrap file MEMORY.md is 36697 chars (limit 12000); truncating");
|
||||
|
||||
expect(warnings).toEqual([
|
||||
"workspace bootstrap file MEMORY.md is 36697 chars (limit 12000); truncating (sessionKey=agent:main:test-session)",
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps warnings distinct across sessions", () => {
|
||||
const warnings: string[] = [];
|
||||
const first = makeBootstrapWarn({
|
||||
sessionLabel: "agent:main:first-session",
|
||||
warn: (message) => warnings.push(message),
|
||||
});
|
||||
const second = makeBootstrapWarn({
|
||||
sessionLabel: "agent:main:second-session",
|
||||
warn: (message) => warnings.push(message),
|
||||
});
|
||||
|
||||
first?.("workspace bootstrap file MEMORY.md is 36697 chars (limit 12000); truncating");
|
||||
second?.("workspace bootstrap file MEMORY.md is 36697 chars (limit 12000); truncating");
|
||||
|
||||
expect(warnings).toEqual([
|
||||
"workspace bootstrap file MEMORY.md is 36697 chars (limit 12000); truncating (sessionKey=agent:main:first-session)",
|
||||
"workspace bootstrap file MEMORY.md is 36697 chars (limit 12000); truncating (sessionKey=agent:main:second-session)",
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps warnings distinct across workspaces with the same session", () => {
|
||||
const warnings: string[] = [];
|
||||
const first = makeBootstrapWarn({
|
||||
sessionLabel: "agent:main:shared-session",
|
||||
workspaceDir: "/tmp/workspace-a",
|
||||
warn: (message) => warnings.push(message),
|
||||
});
|
||||
const second = makeBootstrapWarn({
|
||||
sessionLabel: "agent:main:shared-session",
|
||||
workspaceDir: "/tmp/workspace-b",
|
||||
warn: (message) => warnings.push(message),
|
||||
});
|
||||
|
||||
first?.("workspace bootstrap file MEMORY.md is 36697 chars (limit 12000); truncating");
|
||||
second?.("workspace bootstrap file MEMORY.md is 36697 chars (limit 12000); truncating");
|
||||
|
||||
expect(warnings).toEqual([
|
||||
"workspace bootstrap file MEMORY.md is 36697 chars (limit 12000); truncating (sessionKey=agent:main:shared-session)",
|
||||
"workspace bootstrap file MEMORY.md is 36697 chars (limit 12000); truncating (sessionKey=agent:main:shared-session)",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveContextInjectionMode", () => {
|
||||
it("defaults to always when config is missing", () => {
|
||||
expect(resolveContextInjectionMode(undefined)).toBe("always");
|
||||
|
||||
@@ -25,6 +25,29 @@ export type BootstrapContextRunKind = "default" | "heartbeat" | "cron";
|
||||
const CONTINUATION_SCAN_MAX_TAIL_BYTES = 256 * 1024;
|
||||
const CONTINUATION_SCAN_MAX_RECORDS = 500;
|
||||
export const FULL_BOOTSTRAP_COMPLETED_CUSTOM_TYPE = "openclaw:bootstrap-context:full";
|
||||
const BOOTSTRAP_WARNING_DEDUPE_LIMIT = 1024;
|
||||
const seenBootstrapWarnings = new Set<string>();
|
||||
const bootstrapWarningOrder: string[] = [];
|
||||
|
||||
function rememberBootstrapWarning(key: string): boolean {
|
||||
if (seenBootstrapWarnings.has(key)) {
|
||||
return false;
|
||||
}
|
||||
if (seenBootstrapWarnings.size >= BOOTSTRAP_WARNING_DEDUPE_LIMIT) {
|
||||
const oldest = bootstrapWarningOrder.shift();
|
||||
if (oldest) {
|
||||
seenBootstrapWarnings.delete(oldest);
|
||||
}
|
||||
}
|
||||
seenBootstrapWarnings.add(key);
|
||||
bootstrapWarningOrder.push(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function _resetBootstrapWarningCacheForTest(): void {
|
||||
seenBootstrapWarnings.clear();
|
||||
bootstrapWarningOrder.length = 0;
|
||||
}
|
||||
|
||||
export function resolveContextInjectionMode(config?: OpenClawConfig): AgentContextInjection {
|
||||
return config?.agents?.defaults?.contextInjection ?? "always";
|
||||
@@ -103,12 +126,21 @@ export async function hasCompletedBootstrapTurn(sessionFile: string): Promise<bo
|
||||
|
||||
export function makeBootstrapWarn(params: {
|
||||
sessionLabel: string;
|
||||
workspaceDir?: string;
|
||||
warn?: (message: string) => void;
|
||||
}): ((message: string) => void) | undefined {
|
||||
if (!params.warn) {
|
||||
const warn = params.warn;
|
||||
if (!warn) {
|
||||
return undefined;
|
||||
}
|
||||
return (message: string) => params.warn?.(`${message} (sessionKey=${params.sessionLabel})`);
|
||||
const workspacePrefix = params.workspaceDir ?? "";
|
||||
return (message: string) => {
|
||||
const key = `${workspacePrefix}\u0000${params.sessionLabel}\u0000${message}`;
|
||||
if (!rememberBootstrapWarning(key)) {
|
||||
return;
|
||||
}
|
||||
warn(`${message} (sessionKey=${params.sessionLabel})`);
|
||||
};
|
||||
}
|
||||
|
||||
function sanitizeBootstrapFiles(
|
||||
|
||||
@@ -91,6 +91,7 @@ export async function prepareCliRunContext(
|
||||
sessionId: params.sessionId,
|
||||
warn: prepareDeps.makeBootstrapWarn({
|
||||
sessionLabel,
|
||||
workspaceDir,
|
||||
warn: (message) => cliBackendLog.warn(message),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -465,6 +465,7 @@ export async function compactEmbeddedPiSessionDirect(
|
||||
sessionId: params.sessionId,
|
||||
warn: makeBootstrapWarn({
|
||||
sessionLabel,
|
||||
workspaceDir: effectiveWorkspace,
|
||||
warn: (message) => log.warn(message),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -464,7 +464,11 @@ export async function runEmbeddedAttempt(
|
||||
config: params.config,
|
||||
sessionKey: params.sessionKey,
|
||||
sessionId: params.sessionId,
|
||||
warn: makeBootstrapWarn({ sessionLabel, warn: (message) => log.warn(message) }),
|
||||
warn: makeBootstrapWarn({
|
||||
sessionLabel,
|
||||
workspaceDir: effectiveWorkspace,
|
||||
warn: (message) => log.warn(message),
|
||||
}),
|
||||
contextMode: params.bootstrapContextMode,
|
||||
runKind: params.bootstrapContextRunKind,
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user