<feed xmlns='http://www.w3.org/2005/Atom'>
<title>pi-castle-cdp-search.git/src/index.ts, branch master</title>
<subtitle>Search castle's Chrome tabs and history over CDP</subtitle>
<id>https://git.soarez.net/sz/pi-castle-cdp-search.git/atom/src/index.ts?h=master</id>
<link rel='self' href='https://git.soarez.net/sz/pi-castle-cdp-search.git/atom/src/index.ts?h=master'/>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/pi-castle-cdp-search.git/'/>
<updated>2026-08-03T20:45:52Z</updated>
<entry>
<title>Make /search-engine speak in print mode</title>
<updated>2026-08-03T20:45:52Z</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-08-03T20:45:52Z</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/pi-castle-cdp-search.git/commit/?id=97b7804d02d0a8ffc9afdc64069069baf93ef392'/>
<id>urn:sha1:97b7804d02d0a8ffc9afdc64069069baf93ef392</id>
<content type='text'>
ctx.ui.notify is a no-op when hasUI is false, so `pi -p /search-engine`
changed the stored engine and printed nothing — the command looked broken.
Print mode now falls back to stdout. JSON mode deliberately does not: its
stdout is a structured event stream and a stray line would corrupt it.
</content>
</entry>
<entry>
<title>Support DuckDuckGo, Bing and Brave, switchable like the CDP host</title>
<updated>2026-08-03T20:43:56Z</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-08-03T20:43:56Z</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/pi-castle-cdp-search.git/commit/?id=db69207c06e8d5233bff4996e3ef5b43332d533f'/>
<id>urn:sha1:db69207c06e8d5233bff4996e3ef5b43332d533f</id>
<content type='text'>
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.
</content>
</entry>
<entry>
<title>Web search for pi through an existing Chrome over CDP</title>
<updated>2026-08-03T20:18:02Z</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-08-03T20:18:02Z</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/pi-castle-cdp-search.git/commit/?id=495de0d5283dd3e4a6ef715b596c4a2892e95915'/>
<id>urn:sha1:495de0d5283dd3e4a6ef715b596c4a2892e95915</id>
<content type='text'>
Attaches to a browser that is already running — never launches one —
using the same endpoint configuration as pi-browser-harness, so a single
/browser-target choice governs both packages.

One tool, castle_cdp_search, deliberately not named web_search so it
coexists with pi-web-access rather than shadowing it.

Notes from validating against castle's Chrome:

- tbs=qdr:*, the parameter Google's own Tools menu writes, renders an
  empty page on this profile; the older as_qdr=* works. Any date filter
  combined with udm=14 is also empty, so recency drops udm.
- Target.createTarget must not be raced against the abort signal:
  raceAbort abandons the promise but cannot cancel the command, and the
  command's side effect is a tab nothing is left holding.
- A search cancelled while queued has to be removed from the semaphore
  queue, or the slot handed to it later is never counted back.
- A decaying rate-limit block stops serving /sorry/ and returns an empty
  results page instead, indistinguishable from a genuine zero-hit search.
</content>
</entry>
</feed>
