From 1a43e5fe87e31abe062a006742df6c421f98f777 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Thu, 15 Jan 2026 20:50:18 -0600 Subject: [PATCH] fix: adjust websearch tool to emphasize that it ISNT 2024, give more info as to current date --- packages/opencode/src/tool/websearch.ts | 188 ++++++++++++----------- packages/opencode/src/tool/websearch.txt | 3 + 2 files changed, 100 insertions(+), 91 deletions(-) diff --git a/packages/opencode/src/tool/websearch.ts b/packages/opencode/src/tool/websearch.ts index f6df36f10..9cc6af72d 100644 --- a/packages/opencode/src/tool/websearch.ts +++ b/packages/opencode/src/tool/websearch.ts @@ -36,109 +36,115 @@ interface McpSearchResponse { } } -export const WebSearchTool = Tool.define("websearch", { - description: DESCRIPTION, - parameters: z.object({ - query: z.string().describe("Websearch query"), - numResults: z.number().optional().describe("Number of search results to return (default: 8)"), - livecrawl: z - .enum(["fallback", "preferred"]) - .optional() - .describe( - "Live crawl mode - 'fallback': use live crawling as backup if cached content unavailable, 'preferred': prioritize live crawling (default: 'fallback')", - ), - type: z - .enum(["auto", "fast", "deep"]) - .optional() - .describe("Search type - 'auto': balanced search (default), 'fast': quick results, 'deep': comprehensive search"), - contextMaxCharacters: z - .number() - .optional() - .describe("Maximum characters for context string optimized for LLMs (default: 10000)"), - }), - async execute(params, ctx) { - await ctx.ask({ - permission: "websearch", - patterns: [params.query], - always: ["*"], - metadata: { - query: params.query, - numResults: params.numResults, - livecrawl: params.livecrawl, - type: params.type, - contextMaxCharacters: params.contextMaxCharacters, - }, - }) - - const searchRequest: McpSearchRequest = { - jsonrpc: "2.0", - id: 1, - method: "tools/call", - params: { - name: "web_search_exa", - arguments: { +export const WebSearchTool = Tool.define("websearch", async () => { + return { + get description() { + return DESCRIPTION.replace("{{date}}", new Date().toISOString().slice(0, 10)) + }, + parameters: z.object({ + query: z.string().describe("Websearch query"), + numResults: z.number().optional().describe("Number of search results to return (default: 8)"), + livecrawl: z + .enum(["fallback", "preferred"]) + .optional() + .describe( + "Live crawl mode - 'fallback': use live crawling as backup if cached content unavailable, 'preferred': prioritize live crawling (default: 'fallback')", + ), + type: z + .enum(["auto", "fast", "deep"]) + .optional() + .describe( + "Search type - 'auto': balanced search (default), 'fast': quick results, 'deep': comprehensive search", + ), + contextMaxCharacters: z + .number() + .optional() + .describe("Maximum characters for context string optimized for LLMs (default: 10000)"), + }), + async execute(params, ctx) { + await ctx.ask({ + permission: "websearch", + patterns: [params.query], + always: ["*"], + metadata: { query: params.query, - type: params.type || "auto", - numResults: params.numResults || API_CONFIG.DEFAULT_NUM_RESULTS, - livecrawl: params.livecrawl || "fallback", + numResults: params.numResults, + livecrawl: params.livecrawl, + type: params.type, contextMaxCharacters: params.contextMaxCharacters, }, - }, - } - - const controller = new AbortController() - const timeoutId = setTimeout(() => controller.abort(), 25000) - - try { - const headers: Record = { - accept: "application/json, text/event-stream", - "content-type": "application/json", - } - - const response = await fetch(`${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.SEARCH}`, { - method: "POST", - headers, - body: JSON.stringify(searchRequest), - signal: AbortSignal.any([controller.signal, ctx.abort]), }) - clearTimeout(timeoutId) - - if (!response.ok) { - const errorText = await response.text() - throw new Error(`Search error (${response.status}): ${errorText}`) + const searchRequest: McpSearchRequest = { + jsonrpc: "2.0", + id: 1, + method: "tools/call", + params: { + name: "web_search_exa", + arguments: { + query: params.query, + type: params.type || "auto", + numResults: params.numResults || API_CONFIG.DEFAULT_NUM_RESULTS, + livecrawl: params.livecrawl || "fallback", + contextMaxCharacters: params.contextMaxCharacters, + }, + }, } - const responseText = await response.text() + const controller = new AbortController() + const timeoutId = setTimeout(() => controller.abort(), 25000) - // Parse SSE response - const lines = responseText.split("\n") - for (const line of lines) { - if (line.startsWith("data: ")) { - const data: McpSearchResponse = JSON.parse(line.substring(6)) - if (data.result && data.result.content && data.result.content.length > 0) { - return { - output: data.result.content[0].text, - title: `Web search: ${params.query}`, - metadata: {}, + try { + const headers: Record = { + accept: "application/json, text/event-stream", + "content-type": "application/json", + } + + const response = await fetch(`${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.SEARCH}`, { + method: "POST", + headers, + body: JSON.stringify(searchRequest), + signal: AbortSignal.any([controller.signal, ctx.abort]), + }) + + clearTimeout(timeoutId) + + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Search error (${response.status}): ${errorText}`) + } + + const responseText = await response.text() + + // Parse SSE response + const lines = responseText.split("\n") + for (const line of lines) { + if (line.startsWith("data: ")) { + const data: McpSearchResponse = JSON.parse(line.substring(6)) + if (data.result && data.result.content && data.result.content.length > 0) { + return { + output: data.result.content[0].text, + title: `Web search: ${params.query}`, + metadata: {}, + } } } } - } - return { - output: "No search results found. Please try a different query.", - title: `Web search: ${params.query}`, - metadata: {}, - } - } catch (error) { - clearTimeout(timeoutId) + return { + output: "No search results found. Please try a different query.", + title: `Web search: ${params.query}`, + metadata: {}, + } + } catch (error) { + clearTimeout(timeoutId) - if (error instanceof Error && error.name === "AbortError") { - throw new Error("Search request timed out") - } + if (error instanceof Error && error.name === "AbortError") { + throw new Error("Search request timed out") + } - throw error - } - }, + throw error + } + }, + } }) diff --git a/packages/opencode/src/tool/websearch.txt b/packages/opencode/src/tool/websearch.txt index 22427e246..657ac7458 100644 --- a/packages/opencode/src/tool/websearch.txt +++ b/packages/opencode/src/tool/websearch.txt @@ -9,3 +9,6 @@ Usage notes: - Search types: 'auto' (balanced), 'fast' (quick results), 'deep' (comprehensive search) - Configurable context length for optimal LLM integration - Domain filtering and advanced search options available + +Today's date is {{date}}. You MUST use this year when searching for recent information or current events +- Example: If today is 2025-07-15 and the user asks for "latest AI news", search for "AI news 2025", NOT "AI news 2024"