From 565a6cf9712d8cd2c37b9adbfda9b02c2b9c5e2d Mon Sep 17 00:00:00 2001 From: roboomp Date: Fri, 15 May 2026 01:19:48 +0000 Subject: [PATCH] fix(web-search): thread AbortSignal to fetch() in anthropic, exa, jina, zai, gemini providers - anthropic: add signal to AnthropicSearchParams, callSearch(), and AnthropicProvider.search() - exa: add signal to ExaSearchParams, callExaSearch(), and ExaProvider.search() - jina: add signal to JinaSearchParams, callJinaSearch(), and JinaProvider.search() - zai: add signal to ZaiSearchParams, callZaiTool(), and ZaiProvider.search() - gemini: add signal to GeminiSearchParams, callGeminiSearch() and buildInit() so both fetchWithRetry calls (initial + auth-refresh retry) carry the signal The five providers listed above never forwarded SearchParams.signal to the underlying HTTP layer, so pressing Esc during a web_search call had no effect and the session froze until the request resolved or Ctrl+C was pressed. brave, kimi, perplexity, searxng, tavily, synthetic, codex, kagi, and parallel already thread the signal correctly and are unchanged. Fixes #1044 --- .../coding-agent/src/web/search/providers/anthropic.ts | 5 +++++ packages/coding-agent/src/web/search/providers/exa.ts | 3 +++ packages/coding-agent/src/web/search/providers/gemini.ts | 5 +++++ packages/coding-agent/src/web/search/providers/jina.ts | 7 +++++-- packages/coding-agent/src/web/search/providers/zai.ts | 7 +++++-- 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/packages/coding-agent/src/web/search/providers/anthropic.ts b/packages/coding-agent/src/web/search/providers/anthropic.ts index c24b0e0af..b0aa9dfb3 100644 --- a/packages/coding-agent/src/web/search/providers/anthropic.ts +++ b/packages/coding-agent/src/web/search/providers/anthropic.ts @@ -38,6 +38,7 @@ export interface AnthropicSearchParams { max_tokens?: number; /** Sampling temperature (0–1). Lower = more focused/factual. */ temperature?: number; + signal?: AbortSignal; } /** @@ -86,6 +87,7 @@ async function callSearch( systemPrompt?: string, maxTokens?: number, temperature?: number, + signal?: AbortSignal, ): Promise { const url = buildAnthropicUrl(auth); const headers = buildAnthropicSearchHeaders(auth); @@ -116,6 +118,7 @@ async function callSearch( method: "POST", headers, body: JSON.stringify(body), + signal, }); if (!response.ok) { @@ -253,6 +256,7 @@ export async function searchAnthropic(params: AnthropicSearchParams): Promise>> { @@ -235,6 +236,7 @@ async function callGeminiSearch( maxOutputTokens?: number, temperature?: number, toolParams: GeminiToolParams = {}, + signal?: AbortSignal, ): Promise<{ answer: string; sources: SearchSource[]; @@ -308,6 +310,7 @@ async function callGeminiSearch( ...headers, }, body: JSON.stringify(requestBody), + signal, }); const urlFor = (attempt: number) => `${endpoints[Math.min(attempt, endpoints.length - 1)]}/v1internal:streamGenerateContent?alt=sse`; @@ -500,6 +503,7 @@ export async function searchGemini(params: GeminiSearchParams): Promise { +async function callJinaSearch(apiKey: string, query: string, signal?: AbortSignal): Promise { const requestUrl = `${JINA_SEARCH_URL}/${encodeURIComponent(query)}`; const response = await fetch(requestUrl, { headers: { Accept: "application/json", Authorization: `Bearer ${apiKey}`, }, + signal, }); if (!response.ok) { @@ -58,7 +60,7 @@ export async function searchJina(params: JinaSearchParams): Promise { return findCredential(getEnvApiKey("zai"), "zai"); } -async function callZaiTool(apiKey: string, args: Record): Promise { +async function callZaiTool(apiKey: string, args: Record, signal?: AbortSignal): Promise { const response = await fetch(ZAI_MCP_URL, { method: "POST", headers: { @@ -72,6 +73,7 @@ async function callZaiTool(apiKey: string, args: Record): Promi arguments: args, }, }), + signal, }); if (!response.ok) { @@ -157,7 +159,7 @@ async function callZaiSearch(apiKey: string, params: ZaiSearchParams): Promise