mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-22 11:04:42 -07:00
fix(a2a): fix card resolver bug, simplify normalizeAgentCard, use correct gRPC URL
- Pass empty string to resolver.resolve() to prevent SDK from appending /.well-known/agent-card.json to direct card URLs - Simplify normalizeAgentCard to only handle proto field name aliases (supportedInterfaces → additionalInterfaces, protocolBinding → transport) - Use gRPC-specific URL from additionalInterfaces for credentials - Remove dead helper functions and unnecessary behaviors - Add shallow copy to prevent SDK object mutation
This commit is contained in:
committed by
Alisa Novikova
parent
bdda2f4d59
commit
72e0cd5112
@@ -212,90 +212,45 @@ function extractPartText(part: Part): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes an agent card by ensuring it has the required properties
|
||||
* and resolving any inconsistencies between protocol versions.
|
||||
* Normalizes proto field name aliases that the SDK doesn't handle yet.
|
||||
* The A2A proto spec uses `supported_interfaces` and `protocol_binding`,
|
||||
* while the SDK expects `additionalInterfaces` and `transport`.
|
||||
* TODO: Remove once @a2a-js/sdk handles these aliases natively.
|
||||
*/
|
||||
export function normalizeAgentCard(card: unknown): AgentCard {
|
||||
if (!isObject(card)) {
|
||||
throw new Error('Agent card is missing.');
|
||||
}
|
||||
|
||||
// Narrowing to AgentCard interface.
|
||||
// Shallow-copy to avoid mutating the SDK's cached object.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const result = card as unknown as AgentCard;
|
||||
const result = { ...card } as unknown as AgentCard;
|
||||
|
||||
// 1. Normalize and Sync Interfaces
|
||||
const interfaces = normalizeInterfaces(card);
|
||||
result.additionalInterfaces = interfaces;
|
||||
|
||||
// Sync supportedInterfaces for backward compatibility.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const legacyResult = result as unknown as Record<string, AgentInterface[]>;
|
||||
legacyResult['supportedInterfaces'] = interfaces;
|
||||
|
||||
// 2. Fallback preferredTransport: If not specified, default to GRPC if available.
|
||||
if (
|
||||
!result.preferredTransport &&
|
||||
interfaces.some((i) => i.transport === 'GRPC')
|
||||
) {
|
||||
result.preferredTransport = 'GRPC';
|
||||
// Map supportedInterfaces → additionalInterfaces if needed
|
||||
if (!result.additionalInterfaces) {
|
||||
const raw = card;
|
||||
if (Array.isArray(raw['supportedInterfaces'])) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
result.additionalInterfaces = raw[
|
||||
'supportedInterfaces'
|
||||
] as AgentInterface[];
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Fallback: If top-level URL is missing, use the first interface's URL.
|
||||
if ((!result.url || result.url === '') && interfaces.length > 0) {
|
||||
result.url = interfaces[0].url;
|
||||
// Map protocolBinding → transport on each interface
|
||||
for (const intf of result.additionalInterfaces ?? []) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const raw = intf as unknown as Record<string, unknown>;
|
||||
const binding = raw['protocolBinding'];
|
||||
|
||||
if (!intf.transport && typeof binding === 'string') {
|
||||
intf.transport = binding;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts and normalizes interfaces from the card, handling protocol version fallbacks.
|
||||
*/
|
||||
function normalizeInterfaces(card: Record<string, unknown>): AgentInterface[] {
|
||||
const additional = card['additionalInterfaces'];
|
||||
const supported = card['supportedInterfaces'];
|
||||
|
||||
let rawInterfaces: unknown[] = [];
|
||||
if (Array.isArray(additional)) {
|
||||
rawInterfaces = additional;
|
||||
} else if (Array.isArray(supported)) {
|
||||
rawInterfaces = supported;
|
||||
}
|
||||
|
||||
return rawInterfaces.filter(isObject).map((i) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const intf = i as unknown as AgentInterface;
|
||||
normalizeInterface(intf);
|
||||
return intf;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a single AgentInterface.
|
||||
*/
|
||||
function normalizeInterface(intf: AgentInterface): void {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const raw = intf as unknown as Record<string, unknown>;
|
||||
|
||||
// Normalize 'transport' from 'protocolBinding' if missing.
|
||||
const protocolBinding = raw['protocolBinding'];
|
||||
if (!intf.transport && isString(protocolBinding)) {
|
||||
intf.transport = protocolBinding;
|
||||
}
|
||||
|
||||
// Robust URL: Ensure the URL has a scheme (except for gRPC).
|
||||
if (
|
||||
intf.url &&
|
||||
!intf.url.includes('://') &&
|
||||
!intf.url.startsWith('/') &&
|
||||
intf.transport !== 'GRPC'
|
||||
) {
|
||||
// Default to http:// for insecure REST/JSON-RPC if scheme is missing.
|
||||
intf.url = `http://${intf.url}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns gRPC channel credentials based on the URL scheme.
|
||||
*/
|
||||
@@ -376,13 +331,6 @@ export function isTerminalState(state: TaskState | undefined): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if a value is a string.
|
||||
*/
|
||||
function isString(val: unknown): val is string {
|
||||
return typeof val === 'string';
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if a value is a non-array object.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user