From db69207c06e8d5233bff4996e3ef5b43332d533f Mon Sep 17 00:00:00 2001 From: Igor Soarez Date: Mon, 3 Aug 2026 21:43:56 +0100 Subject: Support DuckDuckGo, Bing and Brave, switchable like the CDP host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Engine resolution mirrors the browser target: CCS_SEARCH_ENGINE, then a /search-engine choice persisted machine-wide, then Google. A per-call `engine` parameter sits above both so the agent can fall back when one engine starts serving captchas — the one case where the model, not the operator, has to make the call. Unlike the CDP target there is no safety argument for the environment winning: driving the wrong browser means automating someone's signed-in Chrome, choosing a different index does not. An unsupported recency window is refused, naming the engines that support it, rather than dropped. Silently returning unfiltered results is indistinguishable from success, which is the failure this whole design is trying to avoid. Extraction grows a second mode. DuckDuckGo, Bing and Brave have clean per-result containers; Google does not, so its heading-walk stays as its own path rather than being bent into the item shape. DuckDuckGo and Bing route links through redirectors, unwrapped in the page. Third silent-failure trap found, alongside Google's two: on Bing, `count` cancels `filters`. With ex1:"ez1" alone every result is hours old; add count in either order and months-old results return, looking perfectly ordinary. Bing now drops count whenever a date filter is present. Challenge detection widened to Brave's "Verifying you're not a bot" and "Quick check before you continue searching", which the previous Google- shaped matcher missed entirely — found by tripping it. --- test/format.test.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'test/format.test.ts') diff --git a/test/format.test.ts b/test/format.test.ts index 3060a62..74e37e1 100644 --- a/test/format.test.ts +++ b/test/format.test.ts @@ -15,9 +15,16 @@ test("results are numbered, with the URL on its own line", () => { searchUrl: "https://www.google.com/search?q=q", finalUrl: "https://www.google.com/search?q=q", endpoint: "10.88.0.25:9223 (built-in default)", + engine: "Google", + engineSource: "built-in default", results, }); - assert.match(text, /^Search results for "q" \(2 hits, via Chrome at 10\.88\.0\.25:9223 \(built-in default\)\)/); + // The engine has to be named on every result set: the agent may have switched + // engines to get around a block, and two indexes are not interchangeable. + assert.match( + text, + /^Search results for "q" — 2 hits from Google \(built-in default\), via Chrome at 10\.88\.0\.25:9223 \(built-in default\)$/m, + ); assert.match(text, /^1\. First$/m); assert.match(text, /^ {3}https:\/\/example\.com\/1$/m); assert.match(text, /^2\. Second$/m); @@ -31,6 +38,8 @@ test("a redirect is reported, because the query URL is then not where we looked" searchUrl: "https://www.google.com/search?q=q", finalUrl: "https://www.google.com/search?q=q&sei=abc", endpoint: "e", + engine: "Bing", + engineSource: "/search-engine", results, }); assert.match(text, /^Landed on: https:\/\/www\.google\.com\/search\?q=q&sei=abc$/m); @@ -41,6 +50,8 @@ test("the challenge error tells the agent to hand off to a human, with the URL", challenge: "captcha-widget", pageUrl: "https://www.google.com/sorry/index?continue=x", endpoint: "10.88.0.25:9223 (built-in default)", + engine: "Google", + alternatives: ["duckduckgo", "bing", "brave"], detail: "Our systems have detected unusual traffic", }); assert.equal(err.name, "SearchChallengeError"); @@ -49,4 +60,11 @@ test("the challenge error tells the agent to hand off to a human, with the URL", assert.match(err.message, /https:\/\/www\.google\.com\/sorry\/index\?continue=x/); assert.match(err.message, /Ask the user/); assert.match(err.message, /10\.88\.0\.25:9223/); + // Switching engines is the agent's own move and must be offered before the + // human handoff, since a block on one engine says nothing about the others. + for (const alt of ["duckduckgo", "bing", "brave"]) assert.match(err.message, new RegExp(alt)); + assert.ok( + err.message.indexOf("retry the same query with engine") < err.message.indexOf("Ask the user"), + "the self-service fallback must come before the human handoff", + ); }); -- cgit v1.3.1