From f15b8e29bb9c43c3d92ede4874966ef325341bd7 Mon Sep 17 00:00:00 2001 From: Charlie Greenman Date: Sat, 14 Feb 2026 16:25:13 -0500 Subject: [PATCH] config: make bootstrap total context cap configurable --- src/agents/bootstrap-files.ts | 7 +++++- ...lpers.resolvebootstrapmaxchars.e2e.test.ts | 25 ++++++++++++++++++- src/agents/pi-embedded-helpers.ts | 1 + src/agents/pi-embedded-helpers/bootstrap.ts | 8 ++++++ src/config/schema.help.ts | 2 ++ src/config/schema.labels.ts | 1 + src/config/types.agent-defaults.ts | 2 ++ src/config/zod-schema.agent-defaults.ts | 1 + 8 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/agents/bootstrap-files.ts b/src/agents/bootstrap-files.ts index 0954cd40e15..50df5dfdd94 100644 --- a/src/agents/bootstrap-files.ts +++ b/src/agents/bootstrap-files.ts @@ -1,7 +1,11 @@ import type { OpenClawConfig } from "../config/config.js"; import type { EmbeddedContextFile } from "./pi-embedded-helpers.js"; import { applyBootstrapHookOverrides } from "./bootstrap-hooks.js"; -import { buildBootstrapContextFiles, resolveBootstrapMaxChars } from "./pi-embedded-helpers.js"; +import { + buildBootstrapContextFiles, + resolveBootstrapMaxChars, + resolveBootstrapTotalMaxChars, +} from "./pi-embedded-helpers.js"; import { filterBootstrapFilesForSession, loadWorkspaceBootstrapFiles, @@ -55,6 +59,7 @@ export async function resolveBootstrapContextForRun(params: { const bootstrapFiles = await resolveBootstrapFilesForRun(params); const contextFiles = buildBootstrapContextFiles(bootstrapFiles, { maxChars: resolveBootstrapMaxChars(params.config), + totalMaxChars: resolveBootstrapTotalMaxChars(params.config), warn: params.warn, }); return { bootstrapFiles, contextFiles }; diff --git a/src/agents/pi-embedded-helpers.resolvebootstrapmaxchars.e2e.test.ts b/src/agents/pi-embedded-helpers.resolvebootstrapmaxchars.e2e.test.ts index 021da973420..c4a0e7471c2 100644 --- a/src/agents/pi-embedded-helpers.resolvebootstrapmaxchars.e2e.test.ts +++ b/src/agents/pi-embedded-helpers.resolvebootstrapmaxchars.e2e.test.ts @@ -1,6 +1,11 @@ import { describe, expect, it } from "vitest"; import type { OpenClawConfig } from "../config/config.js"; -import { DEFAULT_BOOTSTRAP_MAX_CHARS, resolveBootstrapMaxChars } from "./pi-embedded-helpers.js"; +import { + DEFAULT_BOOTSTRAP_MAX_CHARS, + DEFAULT_BOOTSTRAP_TOTAL_MAX_CHARS, + resolveBootstrapMaxChars, + resolveBootstrapTotalMaxChars, +} from "./pi-embedded-helpers.js"; import { DEFAULT_AGENTS_FILENAME } from "./workspace.js"; const _makeFile = (overrides: Partial): WorkspaceBootstrapFile => ({ @@ -27,3 +32,21 @@ describe("resolveBootstrapMaxChars", () => { expect(resolveBootstrapMaxChars(cfg)).toBe(DEFAULT_BOOTSTRAP_MAX_CHARS); }); }); + +describe("resolveBootstrapTotalMaxChars", () => { + it("returns default when unset", () => { + expect(resolveBootstrapTotalMaxChars()).toBe(DEFAULT_BOOTSTRAP_TOTAL_MAX_CHARS); + }); + it("uses configured value when valid", () => { + const cfg = { + agents: { defaults: { bootstrapTotalMaxChars: 12345 } }, + } as OpenClawConfig; + expect(resolveBootstrapTotalMaxChars(cfg)).toBe(12345); + }); + it("falls back when invalid", () => { + const cfg = { + agents: { defaults: { bootstrapTotalMaxChars: -1 } }, + } as OpenClawConfig; + expect(resolveBootstrapTotalMaxChars(cfg)).toBe(DEFAULT_BOOTSTRAP_TOTAL_MAX_CHARS); + }); +}); diff --git a/src/agents/pi-embedded-helpers.ts b/src/agents/pi-embedded-helpers.ts index 8c1598e36af..5c45fb05093 100644 --- a/src/agents/pi-embedded-helpers.ts +++ b/src/agents/pi-embedded-helpers.ts @@ -4,6 +4,7 @@ export { DEFAULT_BOOTSTRAP_TOTAL_MAX_CHARS, ensureSessionHeader, resolveBootstrapMaxChars, + resolveBootstrapTotalMaxChars, stripThoughtSignatures, } from "./pi-embedded-helpers/bootstrap.js"; export { diff --git a/src/agents/pi-embedded-helpers/bootstrap.ts b/src/agents/pi-embedded-helpers/bootstrap.ts index 979e9a3cd18..90c1d2b102f 100644 --- a/src/agents/pi-embedded-helpers/bootstrap.ts +++ b/src/agents/pi-embedded-helpers/bootstrap.ts @@ -101,6 +101,14 @@ export function resolveBootstrapMaxChars(cfg?: OpenClawConfig): number { return DEFAULT_BOOTSTRAP_MAX_CHARS; } +export function resolveBootstrapTotalMaxChars(cfg?: OpenClawConfig): number { + const raw = cfg?.agents?.defaults?.bootstrapTotalMaxChars; + if (typeof raw === "number" && Number.isFinite(raw) && raw > 0) { + return Math.floor(raw); + } + return DEFAULT_BOOTSTRAP_TOTAL_MAX_CHARS; +} + function trimBootstrapContent( content: string, fileName: string, diff --git a/src/config/schema.help.ts b/src/config/schema.help.ts index 9c07993a00c..97e43e5b187 100644 --- a/src/config/schema.help.ts +++ b/src/config/schema.help.ts @@ -142,6 +142,8 @@ export const FIELD_HELP: Record = { "auth.cooldowns.failureWindowHours": "Failure window (hours) for backoff counters (default: 24).", "agents.defaults.bootstrapMaxChars": "Max characters of each workspace bootstrap file injected into the system prompt before truncation (default: 20000).", + "agents.defaults.bootstrapTotalMaxChars": + "Max total characters across all injected workspace bootstrap files (default: 24000).", "agents.defaults.repoRoot": "Optional repository root shown in the system prompt runtime line (overrides auto-detect).", "agents.defaults.envelopeTimezone": diff --git a/src/config/schema.labels.ts b/src/config/schema.labels.ts index 2c5e090642d..60a467fd02c 100644 --- a/src/config/schema.labels.ts +++ b/src/config/schema.labels.ts @@ -121,6 +121,7 @@ export const FIELD_LABELS: Record = { "agents.defaults.workspace": "Workspace", "agents.defaults.repoRoot": "Repo Root", "agents.defaults.bootstrapMaxChars": "Bootstrap Max Chars", + "agents.defaults.bootstrapTotalMaxChars": "Bootstrap Total Max Chars", "agents.defaults.envelopeTimezone": "Envelope Timezone", "agents.defaults.envelopeTimestamp": "Envelope Timestamp", "agents.defaults.envelopeElapsed": "Envelope Elapsed", diff --git a/src/config/types.agent-defaults.ts b/src/config/types.agent-defaults.ts index 217e8f12559..72d49e1b285 100644 --- a/src/config/types.agent-defaults.ts +++ b/src/config/types.agent-defaults.ts @@ -108,6 +108,8 @@ export type AgentDefaultsConfig = { skipBootstrap?: boolean; /** Max chars for injected bootstrap files before truncation (default: 20000). */ bootstrapMaxChars?: number; + /** Max total chars across all injected bootstrap files (default: 24000). */ + bootstrapTotalMaxChars?: number; /** Optional IANA timezone for the user (used in system prompt; defaults to host timezone). */ userTimezone?: string; /** Time format in system prompt: auto (OS preference), 12-hour, or 24-hour. */ diff --git a/src/config/zod-schema.agent-defaults.ts b/src/config/zod-schema.agent-defaults.ts index 8aa43933c55..31dd1f672cd 100644 --- a/src/config/zod-schema.agent-defaults.ts +++ b/src/config/zod-schema.agent-defaults.ts @@ -47,6 +47,7 @@ export const AgentDefaultsSchema = z repoRoot: z.string().optional(), skipBootstrap: z.boolean().optional(), bootstrapMaxChars: z.number().int().positive().optional(), + bootstrapTotalMaxChars: z.number().int().positive().optional(), userTimezone: z.string().optional(), timeFormat: z.union([z.literal("auto"), z.literal("12"), z.literal("24")]).optional(), envelopeTimezone: z.string().optional(),