fix(core): preserve display output for malformed tool results (#6925)

This commit is contained in:
morluto 2026-07-15 09:18:04 +08:00 committed by GitHub
parent 2a57e8b276
commit 3d06f962df
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 55 additions and 1 deletions

View file

@ -1226,6 +1226,60 @@ describe('CoreToolScheduler', () => {
expect(outputOfFirstCall(onAllToolCallsComplete)).toBe('small output');
});
it('preserves display output when a tool omits model-facing content', async () => {
const execute = vi.fn().mockResolvedValue({
llmContent: undefined,
returnDisplay: 'completed',
});
const toolsByName = new Map<string, MockTool>([
[
'malformedTool',
new MockTool({
name: 'malformedTool',
// SAFETY: This deliberately violates ToolResult to exercise the
// runtime boundary used by untyped custom tool adapters.
execute: execute as (
params: Record<string, unknown>,
) => Promise<ToolResult>,
}),
],
]);
const { scheduler, onAllToolCallsComplete } =
createSchedulerForLegacyToolTests({ toolsByName });
await scheduler.schedule(
[
{
callId: 'c-malformed',
name: 'malformedTool',
args: {},
isClientInitiated: false,
prompt_id: 'p-malformed',
},
],
new AbortController().signal,
);
const completedCall = (
onAllToolCallsComplete.mock.calls[0][0] as ToolCall[]
)[0];
expect(completedCall.status).toBe('success');
if (completedCall.status === 'success') {
expect(completedCall.response.responseParts).toEqual([
{
functionResponse: {
id: 'c-malformed',
name: 'malformedTool',
response: {
output: '(malformedTool completed with no output)',
},
},
},
]);
expect(completedCall.response.resultDisplay).toBe('completed');
}
});
it('applies the per-tool budget for a tool invoked via a legacy alias', async () => {
// Regression (C1): limitsTool read getTool(request.name) with the raw alias
// ('task'), which the registry stores only under the canonical name

View file

@ -3845,7 +3845,7 @@ export class CoreToolScheduler {
}
if (toolResult.error === undefined) {
let content = toolResult.llmContent;
let content = toolResult.llmContent ?? '';
let contentLength: number | undefined =
typeof content === 'string' ? content.length : undefined;