diff options
| author | Igor Soarez <igor@soarez.org> | 2026-11-06 03:33:30 +0000 |
|---|---|---|
| committer | Igor Soarez <igor@soarez.org> | 2026-11-06 03:33:30 +0000 |
| commit | 561f0ee076c684a79fd5671af1bad0894f33e4de (patch) | |
| tree | 7849677412a8b5f80835fa812f2e255f222a2c82 /castle-search/src/index.ts | |
| parent | 147b3b1507ebcdf5912fbd4c474c9dfb35265148 (diff) | |
The Node-side resolver sent one curl-like request per result with no
cookies, JS or browser; Google answers that with its 'Verifying your
request' countdown after a handful, and the testing itself triggered it.
A click or direct navigation in Chrome redirects at once (measured).
Search now leaves Google links wrapped and prints the breadcrumb Google
shows for each. castle_cdp_resolve follows one wrapped link in a Chrome tab
when the agent picks it, accepts only google.com/goto?url=<token>, and
reports the verification page instead of waiting it out.
Diffstat (limited to 'castle-search/src/index.ts')
| -rw-r--r-- | castle-search/src/index.ts | 37 |
1 files changed, 36 insertions, 1 deletions
diff --git a/castle-search/src/index.ts b/castle-search/src/index.ts index 29cdaaf..9b7c143 100644 --- a/castle-search/src/index.ts +++ b/castle-search/src/index.ts @@ -43,6 +43,7 @@ import { describeConfiguredEndpoint, describeConfiguredEngine, disconnect, + resolveLink, search, } from "./search.ts"; @@ -202,6 +203,38 @@ export default function (pi: ExtensionAPI): void { }); pi.registerTool({ + name: "castle_cdp_resolve", + label: "Castle Resolve", + description: + "Turn one Google-wrapped result link (https://www.google.com/goto?url=…, as printed by castle_cdp_search " + + "with engine google) into the real destination URL, by following it in the same Chrome. Opens one tab, " + + "reads where it lands, closes it. It does not read the page. Resolve only the results you mean to open: " + + "Google answers a burst of links with a \"Verifying your request\" countdown, which this tool reports " + + "instead of waiting out. Other engines already return real URLs and need no resolving.", + promptSnippet: "Resolve one Google-wrapped result link to its real URL (castle_cdp_resolve)", + promptGuidelines: [ + "castle_cdp_search on Google returns wrapped links. Read the titles, snippets and the breadcrumb Google " + + "shows for each, decide which result you actually need, and resolve only that one with castle_cdp_resolve. " + + "Do not resolve every result: that burst is what triggers Google's verification page.", + ], + parameters: Type.Object({ + url: Type.String({ + description: "A https://www.google.com/goto?url=… link exactly as castle_cdp_search printed it.", + minLength: 1, + }), + }), + async execute(_toolCallId, params, signal, _onUpdate, _ctx) { + const timeout = AbortSignal.timeout(SEARCH_TIMEOUT_MS); + const combined = signal ? AbortSignal.any([signal, timeout]) : timeout; + const out = await resolveLink(params.url.trim(), combined); + return { + content: [{ type: "text", text: `${out.to}\n(resolved via Chrome at ${out.endpoint})` }], + details: { from: out.from, to: out.to, endpoint: out.endpoint }, + }; + }, + }); + + pi.registerTool({ name: "castle_cdp_search", label: "Castle Search", description: @@ -209,7 +242,9 @@ export default function (pi: ExtensionAPI): void { "DevTools Protocol. Returns ranked results with titles, URLs and snippets. Because it uses a real " + "logged-in browser rather than a search API, it reaches pages that block datacentre traffic. It " + "returns search results only — it does not fetch or read the linked pages, so follow up with a " + - "fetch/read tool for full page content. Supports four engines with independent indexes and " + + "fetch/read tool for full page content. Google results come back as wrapped links with the " + + "breadcrumb Google shows; resolve the ones you need with castle_cdp_resolve. " + + "Supports four engines with independent indexes and " + `independent rate limits (${ENGINE_IDS.join(", ")}); pass engine to switch when one is blocked. ` + `Output is truncated at ${formatSize(DEFAULT_MAX_BYTES)} or ${DEFAULT_MAX_LINES} lines.`, promptSnippet: |
