fix(agents): skip bootstrap files with undefined path (#22698)

* fix(agents): skip bootstrap files with undefined path

buildBootstrapContextFiles() called file.path.replace() without checking
that path was defined. If a hook pushed a bootstrap file using 'filePath'
instead of 'path', the function threw TypeError and crashed every agent
session — not just the misconfigured hook.

Fix: add a null-guard before the path.replace() call. Files with undefined
path are skipped with a warning so one bad hook can't take down all agents.

Also adds a test covering the undefined-path case.

Fixes #22693

* fix: harden bootstrap path validation and report guards (#22698) (thanks @arosstale)

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
Artale
2026-02-22 13:17:07 +01:00
committed by GitHub
parent 45d7776697
commit 51e9c54f09
7 changed files with 141 additions and 8 deletions

View File

@@ -40,26 +40,34 @@ function buildInjectedWorkspaceFiles(params: {
bootstrapFiles: WorkspaceBootstrapFile[];
injectedFiles: EmbeddedContextFile[];
}): SessionSystemPromptReport["injectedWorkspaceFiles"] {
const injectedByPath = new Map(params.injectedFiles.map((f) => [f.path, f.content]));
const injectedByPath = new Map<string, string>();
const injectedByBaseName = new Map<string, string>();
for (const file of params.injectedFiles) {
const normalizedPath = file.path.replace(/\\/g, "/");
const pathValue = typeof file.path === "string" ? file.path.trim() : "";
if (!pathValue) {
continue;
}
if (!injectedByPath.has(pathValue)) {
injectedByPath.set(pathValue, file.content);
}
const normalizedPath = pathValue.replace(/\\/g, "/");
const baseName = path.posix.basename(normalizedPath);
if (!injectedByBaseName.has(baseName)) {
injectedByBaseName.set(baseName, file.content);
}
}
return params.bootstrapFiles.map((file) => {
const pathValue = typeof file.path === "string" ? file.path.trim() : "";
const rawChars = file.missing ? 0 : (file.content ?? "").trimEnd().length;
const injected =
injectedByPath.get(file.path) ??
(pathValue ? injectedByPath.get(pathValue) : undefined) ??
injectedByPath.get(file.name) ??
injectedByBaseName.get(file.name);
const injectedChars = injected ? injected.length : 0;
const truncated = !file.missing && injectedChars < rawChars;
return {
name: file.name,
path: file.path,
path: pathValue || file.name,
missing: file.missing,
rawChars,
injectedChars,