Fix: A2A server - add liveOutput and response resultsDisplay to the serialized tool call result (closes #9520) (#9788)

Co-authored-by: anthony bushong <agmsb@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
Keith Schaab
2025-09-30 08:26:51 -07:00
committed by GitHub
parent 6ef78cbbe9
commit 1067df1872
+47 -33
View File
@@ -15,18 +15,17 @@ import {
isNodeError, isNodeError,
parseAndFormatApiError, parseAndFormatApiError,
safeLiteralReplace, safeLiteralReplace,
} from '@google/gemini-cli-core'; type AnyDeclarativeTool,
import type { type ToolCall,
ToolConfirmationPayload, type ToolConfirmationPayload,
CompletedToolCall, type CompletedToolCall,
ToolCall, type ToolCallRequestInfo,
ToolCallRequestInfo, type ServerGeminiErrorEvent,
ServerGeminiErrorEvent, type ServerGeminiStreamEvent,
ServerGeminiStreamEvent, type ToolCallConfirmationDetails,
ToolCallConfirmationDetails, type Config,
Config, type UserTierId,
UserTierId, type AnsiOutput,
AnsiOutput,
} from '@google/gemini-cli-core'; } from '@google/gemini-cli-core';
import type { RequestContext } from '@a2a-js/sdk/server'; import type { RequestContext } from '@a2a-js/sdk/server';
import { type ExecutionEventBus } from '@a2a-js/sdk/server'; import { type ExecutionEventBus } from '@a2a-js/sdk/server';
@@ -54,6 +53,8 @@ import type {
} from '../types.js'; } from '../types.js';
import type { PartUnion, Part as genAiPart } from '@google/genai'; import type { PartUnion, Part as genAiPart } from '@google/genai';
type UnionKeys<T> = T extends T ? keyof T : never;
export class Task { export class Task {
id: string; id: string;
contextId: string; contextId: string;
@@ -436,6 +437,19 @@ export class Task {
return scheduler; return scheduler;
} }
private _pickFields<
T extends ToolCall | AnyDeclarativeTool,
K extends UnionKeys<T>,
>(from: T, ...fields: K[]): Partial<T> {
const ret = {} as Pick<T, K>;
for (const field of fields) {
if (field in from) {
ret[field] = from[field];
}
}
return ret as Partial<T>;
}
private toolStatusMessage( private toolStatusMessage(
tc: ToolCall, tc: ToolCall,
taskId: string, taskId: string,
@@ -444,33 +458,33 @@ export class Task {
const messageParts: Part[] = []; const messageParts: Part[] = [];
// Create a serializable version of the ToolCall (pick necesssary // Create a serializable version of the ToolCall (pick necesssary
// properties/avoic methods causing circular reference errors) // properties/avoid methods causing circular reference errors)
const serializableToolCall: { [key: string]: unknown } = { const serializableToolCall: Partial<ToolCall> = this._pickFields(
request: tc.request, tc,
status: tc.status, 'request',
}; 'status',
'confirmationDetails',
// For WaitingToolCall type 'liveOutput',
if ('confirmationDetails' in tc) { 'response',
serializableToolCall['confirmationDetails'] = tc.confirmationDetails; );
}
if (tc.tool) { if (tc.tool) {
serializableToolCall['tool'] = { serializableToolCall.tool = this._pickFields(
name: tc.tool.name, tc.tool,
displayName: tc.tool.displayName, 'name',
description: tc.tool.description, 'displayName',
kind: tc.tool.kind, 'description',
isOutputMarkdown: tc.tool.isOutputMarkdown, 'kind',
canUpdateOutput: tc.tool.canUpdateOutput, 'isOutputMarkdown',
schema: tc.tool.schema, 'canUpdateOutput',
parameterSchema: tc.tool.parameterSchema, 'schema',
}; 'parameterSchema',
) as AnyDeclarativeTool;
} }
messageParts.push({ messageParts.push({
kind: 'data', kind: 'data',
data: serializableToolCall as ToolCall, data: serializableToolCall,
} as Part); } as Part);
return { return {