diff --git a/src/agents/bootstrap-budget.test.ts b/src/agents/bootstrap-budget.test.ts index d12326b41813..a74c077ec345 100644 --- a/src/agents/bootstrap-budget.test.ts +++ b/src/agents/bootstrap-budget.test.ts @@ -103,6 +103,29 @@ describe("analyzeBootstrapBudget", () => { }); describe("bootstrap prompt warnings", () => { + it("handles malformed truncation entries without names", () => { + const analysis = analyzeBootstrapBudget({ + files: [ + { + name: "TEMP.md", + path: "/tmp/unknown", + missing: false, + rawChars: 10, + injectedChars: 1, + truncated: true, + }, + ], + bootstrapMaxChars: 5, + bootstrapTotalMaxChars: 5, + }); + (analysis.truncatedFiles[0] as { name?: string }).name = undefined; + + const lines = formatBootstrapTruncationWarningLines({ + analysis, + }); + expect(lines.join("\n")).toContain("10 raw -> 1 injected"); + }); + it("appends warning details to the turn prompt instead of mutating the system prompt", () => { const prompt = appendBootstrapPromptWarning("Please continue.", [ "AGENTS.md: 200 raw -> 0 injected", diff --git a/src/agents/bootstrap-budget.ts b/src/agents/bootstrap-budget.ts index a493d68d9b17..85c43173301e 100644 --- a/src/agents/bootstrap-budget.ts +++ b/src/agents/bootstrap-budget.ts @@ -68,8 +68,8 @@ function formatWarningCause(cause: BootstrapTruncationCause): string { return cause === "per-file-limit" ? "max/file" : "max/total"; } -function isAgentsBootstrapName(name: string): boolean { - return name.toLowerCase() === "agents.md"; +function isAgentsBootstrapName(name: string | undefined): boolean { + return name?.toLowerCase() === "agents.md"; } function normalizeSeenSignatures(signatures?: string[]): string[] { diff --git a/src/agents/pi-embedded-helpers.buildbootstrapcontextfiles.test.ts b/src/agents/pi-embedded-helpers.buildbootstrapcontextfiles.test.ts index f2644ab148be..0042ffbee406 100644 --- a/src/agents/pi-embedded-helpers.buildbootstrapcontextfiles.test.ts +++ b/src/agents/pi-embedded-helpers.buildbootstrapcontextfiles.test.ts @@ -241,6 +241,21 @@ describe("buildBootstrapContextFiles", () => { warnings.filter((warning) => !warning.includes('missing or invalid "path" field')), ).toStrictEqual([]); }); + + it("handles undefined file names without crashing", () => { + const fileWithUndefinedName = { + name: undefined, + path: "/tmp/test.md", + content: "content", + missing: false, + } as unknown as WorkspaceBootstrapFile; + const warnings: string[] = []; + const result = buildBootstrapContextFiles([fileWithUndefinedName], { + warn: (msg) => warnings.push(msg), + }); + expect(result).toBeDefined(); + expect(Array.isArray(result)).toBe(true); + }); }); type BootstrapLimitResolverCase = { diff --git a/src/agents/pi-embedded-helpers/bootstrap.ts b/src/agents/pi-embedded-helpers/bootstrap.ts index bd24fc866e93..80f24537b349 100644 --- a/src/agents/pi-embedded-helpers/bootstrap.ts +++ b/src/agents/pi-embedded-helpers/bootstrap.ts @@ -151,8 +151,8 @@ export function resolveBootstrapPromptTruncationWarningMode( return DEFAULT_BOOTSTRAP_PROMPT_TRUNCATION_WARNING_MODE; } -function isAgentsBootstrapFile(fileName: string): boolean { - return fileName.toLowerCase() === AGENTS_BOOTSTRAP_FILENAME.toLowerCase(); +function isAgentsBootstrapFile(fileName: string | undefined): boolean { + return fileName?.toLowerCase() === AGENTS_BOOTSTRAP_FILENAME.toLowerCase(); } function isPolicyDigestCandidate(line: string): boolean {