diff --git a/src/agents/system-prompt-report.test.ts b/src/agents/system-prompt-report.test.ts index 4f5095c6503d..f0f892f576ab 100644 --- a/src/agents/system-prompt-report.test.ts +++ b/src/agents/system-prompt-report.test.ts @@ -216,4 +216,83 @@ describe("buildSystemPromptReport", () => { }); expect(report.tools.entries[0]?.schemaHash).toMatch(/^[a-f0-9]{64}$/u); }); + + it("keeps reporting when a tool schema getter throws", () => { + const file = makeBootstrapFile({ path: "/tmp/workspace/AGENTS.md" }); + const brokenTool = { + name: "broken", + description: "Broken schema", + }; + Object.defineProperty(brokenTool, "parameters", { + get() { + throw new Error("schema getter exploded"); + }, + }); + + const report = buildSystemPromptReport({ + source: "run", + generatedAt: 0, + bootstrapMaxChars: 20_000, + systemPrompt: "system", + bootstrapFiles: [file], + injectedFiles: [], + skillsPrompt: "", + tools: [ + brokenTool, + { + name: "healthy", + description: "Healthy schema", + parameters: { + type: "object", + properties: { path: { type: "string" } }, + }, + }, + ] as never, + }); + + expect(report.tools.entries).toHaveLength(2); + expect(report.tools.entries[0]).toMatchObject({ + name: "broken", + schemaChars: 0, + propertiesCount: null, + }); + expect(report.tools.entries[1]).toMatchObject({ + name: "healthy", + propertiesCount: 1, + }); + }); + + it("keeps reporting when a tool schema properties getter throws", () => { + const file = makeBootstrapFile({ path: "/tmp/workspace/AGENTS.md" }); + const schema = { type: "object" }; + Object.defineProperty(schema, "properties", { + get() { + throw new Error("properties getter exploded"); + }, + enumerable: true, + }); + + const report = buildSystemPromptReport({ + source: "run", + generatedAt: 0, + bootstrapMaxChars: 20_000, + systemPrompt: "system", + bootstrapFiles: [file], + injectedFiles: [], + skillsPrompt: "", + tools: [ + { + name: "broken", + description: "Broken schema", + parameters: schema, + }, + ] as never, + }); + + expect(report.tools.entries[0]).toMatchObject({ + name: "broken", + schemaChars: 0, + propertiesCount: null, + }); + }); }); diff --git a/src/agents/system-prompt-report.ts b/src/agents/system-prompt-report.ts index d258f3735606..5b7b686937f1 100644 --- a/src/agents/system-prompt-report.ts +++ b/src/agents/system-prompt-report.ts @@ -49,7 +49,7 @@ function parseSkillBlocks(skillsPrompt: string): Array<{ name: string; blockChar } function buildToolSchemaStats( - parameters: AgentTool["parameters"], + parameters: AgentTool["parameters"] | undefined, ): Pick { if (!parameters || typeof parameters !== "object") { return { schemaChars: 0, schemaHash: sha256(""), propertiesCount: null }; @@ -67,14 +67,7 @@ function buildToolSchemaStats( const stats = { schemaChars: schemaJson.length, schemaHash: sha256(schemaJson), - propertiesCount: (() => { - const schema = parameters as Record; - const props = typeof schema.properties === "object" ? schema.properties : null; - if (!props || typeof props !== "object") { - return null; - } - return Object.keys(props as Record).length; - })(), + propertiesCount: countToolSchemaProperties(parameters), }; // Tool parameter objects are reused across runs; cache their stable size/hash // so report generation stays cheap during frequent prompt rebuilds. @@ -82,6 +75,27 @@ function buildToolSchemaStats( return stats; } +function countToolSchemaProperties(parameters: object): number | null { + try { + const schema = parameters as Record; + const props = typeof schema.properties === "object" ? schema.properties : null; + if (!props || typeof props !== "object") { + return null; + } + return Object.keys(props as Record).length; + } catch { + return null; + } +} + +function readToolParameters(tool: AgentTool): AgentTool["parameters"] | undefined { + try { + return tool.parameters; + } catch { + return undefined; + } +} + function buildToolsEntries(tools: AgentTool[]): SessionSystemPromptReport["tools"]["entries"] { return tools.map((tool) => { const cached = toolReportEntryCache.get(tool); @@ -91,7 +105,7 @@ function buildToolsEntries(tools: AgentTool[]): SessionSystemPromptReport["tools const name = tool.name; const summary = tool.description?.trim() || tool.label?.trim() || ""; const summaryChars = summary.length; - const schemaStats = buildToolSchemaStats(tool.parameters); + const schemaStats = buildToolSchemaStats(readToolParameters(tool)); const entry = { name, summaryChars, summaryHash: sha256(summary), ...schemaStats }; toolReportEntryCache.set(tool, entry); return entry;