From 1112c0f966dbe4dd4b296ba4765aae74ffe7010e Mon Sep 17 00:00:00 2001 From: Alisa Novikova <62909685+alisa-alisa@users.noreply.github.com> Date: Fri, 6 Mar 2026 04:51:25 -0800 Subject: [PATCH] refactor(a2a): standardize safeFetch signature and simplify usage in registry and client manager --- packages/core/src/agents/a2a-client-manager.ts | 2 +- packages/core/src/agents/registry.test.ts | 8 ++------ packages/core/src/agents/registry.ts | 9 +-------- packages/core/src/utils/fetch.ts | 12 +++++++++--- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/packages/core/src/agents/a2a-client-manager.ts b/packages/core/src/agents/a2a-client-manager.ts index a3f5cbd763..5632b91224 100644 --- a/packages/core/src/agents/a2a-client-manager.ts +++ b/packages/core/src/agents/a2a-client-manager.ts @@ -66,7 +66,7 @@ const a2aDispatcher = new UndiciAgent({ }); const a2aFetch: typeof fetch = (input, init) => { const nodeInit: NodeFetchInit = { ...init, dispatcher: a2aDispatcher }; - return fetch(input, nodeInit); + return fetch(input, nodeInit as RequestInit); }; /** diff --git a/packages/core/src/agents/registry.test.ts b/packages/core/src/agents/registry.test.ts index d1dc62d1f5..24a8cf65c3 100644 --- a/packages/core/src/agents/registry.test.ts +++ b/packages/core/src/agents/registry.test.ts @@ -531,14 +531,10 @@ describe('AgentRegistry', () => { }); expect(call).toBeDefined(); - // Verify that the wrapper delegates to safeFetch const options = call?.[0] as { fetchImpl?: typeof fetch }; - // We can't easily spy on safeFetch because it's an exported function, - // but we've verified it is provided via options. - expect(typeof options?.fetchImpl).toBe('function'); - // Use safeFetch to satisfy the unused import check. - expect(safeFetch).toBeDefined(); + // We passed safeFetch directly + expect(options?.fetchImpl).toBe(safeFetch); }); }); diff --git a/packages/core/src/agents/registry.ts b/packages/core/src/agents/registry.ts index 8db453bf07..3b33539f2d 100644 --- a/packages/core/src/agents/registry.ts +++ b/packages/core/src/agents/registry.ts @@ -166,15 +166,8 @@ export class AgentRegistry { // We use a dedicated resolver here to fetch the card for hashing. // This is separate from loadAgent to keep hashing logic isolated. // We provide safeFetch to ensure SSRF and DNS rebinding protection. - // We wrap it to match the signature expected by the SDK. - const fetchImpl: typeof fetch = (input, init) => { - if (input instanceof Request) { - return safeFetch(input.url, init); - } - return safeFetch(input, init); - }; const resolver = new DefaultAgentCardResolver({ - fetchImpl, + fetchImpl: safeFetch, }); const { baseUrl, path } = splitAgentCardUrl(agent.agentCardUrl); const rawCard = await resolver.resolve(baseUrl, path); diff --git a/packages/core/src/utils/fetch.ts b/packages/core/src/utils/fetch.ts index ca8fa258b7..07b416b988 100644 --- a/packages/core/src/utils/fetch.ts +++ b/packages/core/src/utils/fetch.ts @@ -191,7 +191,7 @@ export function isAddressPrivate(address: string): boolean { * Prevents access to private/internal networks at the connection level. */ export async function safeFetch( - url: string | URL, + input: RequestInfo | URL, init?: RequestInit, ): Promise { const nodeInit: NodeFetchInit = { @@ -200,13 +200,19 @@ export async function safeFetch( }; try { - return await fetch(url, nodeInit); + return await fetch(input, nodeInit); } catch (error) { if (error instanceof Error) { // Re-map refusing to connect errors to standard FetchError if (error.message.includes('Refusing to connect to private IP address')) { + const urlString = + input instanceof Request + ? input.url + : typeof input === 'string' + ? input + : input.toString(); throw new FetchError( - `Access to private network is blocked: ${url.toString()}`, + `Access to private network is blocked: ${urlString}`, 'ERR_PRIVATE_NETWORK', { cause: error }, );