<feed xmlns='http://www.w3.org/2005/Atom'>
<title>sz-chrome-remote-cdp.git/test/manual, branch master</title>
<subtitle>pi-browser-harness, patched to drive a Chrome on another host over CDP, with castle_cdp_search web search
</subtitle>
<id>https://git.soarez.net/sz/sz-chrome-remote-cdp.git/atom?h=master</id>
<link rel='self' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/'/>
<updated>2026-08-02T13:42:46+00:00</updated>
<entry>
<title>refactor: merge nine files whose reason to exist was gone</title>
<updated>2026-08-02T13:42:46+00:00</updated>
<author>
<name>amankumarsingh77</name>
<email>amankumarsingh7702@gmail.com</email>
</author>
<published>2026-07-28T17:11:18+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/commit/?id=dcb79fc62bdd4d9ec0cbdb2c97d3eb2b2dacdf22'/>
<id>urn:sha1:dcb79fc62bdd4d9ec0cbdb2c97d3eb2b2dacdf22</id>
<content type='text'>
src/ goes from 84 files to 75 at the same line count — every merge below
had exactly one consumer.

cdp/event-queue.ts and cdp/pending-requests.ts become daemon/queue.ts.
I split these in the daemon-internals commit on the grounds that they
held two unrelated concerns, but the transport commit had already deleted
the second transport, so both halves had one consumer and the split
bought nothing. They also sat in cdp/ while only daemon/ used them.

cdp/transport.ts held only the CdpTransport interface after the second
transport went; five of its six consumers import it type-only, so it
moves to cdp/types.ts. cdp/evaluate.ts moves into cdp/session.ts rather
than into its lone consumer isolated-tab.ts — it reaches session.call
directly, so under src/domains/ it would trip the raw-cdp-call rule.

util/keycodes.ts and util/time.ts fold into their only callers
(keyboard.ts, page.ts); util/bytes.ts into util/truncate.ts, which
already owns output-size formatting; readpage/capture.ts and
search/extract.ts into the tools beside them; profile/picker.ts into
profile/command.ts.

Kept deliberately: mutex.ts and domains/target.ts each have their own
test suite, and folding target.ts into click.ts would make a tool file
export a helper. sharp-shim.ts is an isolation boundary carrying two
allowlisted casts. result.ts, guards.ts, js-template.ts and paths.ts are
small but have 44, 12, 8 and 3 consumers.

Co-Authored-By: Claude Opus 5 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>refactor: daemon internals, and fix a leak the merge exposed</title>
<updated>2026-08-02T13:42:46+00:00</updated>
<author>
<name>amankumarsingh77</name>
<email>amankumarsingh7702@gmail.com</email>
</author>
<published>2026-07-28T16:38:16+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/commit/?id=b913f2d4af218ae61de7073778feb56da89a15de'/>
<id>urn:sha1:b913f2d4af218ae61de7073778feb56da89a15de</id>
<content type='text'>
IdMultiplexer and EventRouter were the only classes in src/; they are now
createIdMultiplexer/createEventRouter closures like every other factory
here. The multiplexer kept {clientId, localId} per daemon id while the
bridge kept {send, clientId, localId, timer, isAttach} under the same key
— two maps of overlapping data, and mux.resolve()'s return value was
discarded at three of four call sites. One map now holds the union.

That merge fixes a real leak: removeClient cleared the multiplexer but
never the callbacks map, so a disconnected client's timers stayed armed
and later fired a response at a socket that was gone. clearClient now
cancels them, and a test pins it.

handleRequest polled every 250ms for up to 15s per request when Chrome
was down; N queued requests now await one connect signal.

server.ts uses its own protocol's serialize/deserialize instead of
hand-rolled JSON, and broadcast no longer routes through `this` — the
only `this` in the codebase, and the one thing that breaks when the
server object is destructured.

cdp/event-queue.ts held two unrelated concerns; the request/timeout half
moves to cdp/pending-requests.ts. cdp/daemon-transport.ts is the client
half of the daemon protocol, so it moves to daemon/transport.ts.

test/daemon/ needs adding to the test glob to run at all — there was no
daemon test directory before because none of this was reachable without
live Chrome.

Co-Authored-By: Claude Opus 5 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>refactor: one CDP transport, not two</title>
<updated>2026-08-02T13:42:46+00:00</updated>
<author>
<name>amankumarsingh77</name>
<email>amankumarsingh7702@gmail.com</email>
</author>
<published>2026-07-28T16:20:38+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/commit/?id=893a9d338ceb5d37aefece1df637c9a5888a120e'/>
<id>urn:sha1:893a9d338ceb5d37aefece1df637c9a5888a120e</id>
<content type='text'>
createCdpTransport was a second full WebSocket-to-Chrome implementation
reachable only through client.ts's `?? createCdpTransport()` fallback,
which production never took — every session and every automated test
passes the daemon transport. transport.ts now holds only the CdpTransport
interface, and the client requires a transport instead of inventing one.

The "Session with given id not found" match that both transports carried
becomes classifyRemoteError in cdp/errors.ts, next to the error kind it
produces.

test/manual/profile-concurrency-test.ts was the sole caller of the
fallback: it drives its own Chrome on a private port. It now spawns a
daemon per scenario with BU_CDP_WS set, and refuses to run when a daemon
is already up, since that one is bound to a different browser.

Co-Authored-By: Claude Opus 5 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>refactor: tidy the whitespace the comment removal left behind</title>
<updated>2026-08-02T13:42:46+00:00</updated>
<author>
<name>amankumarsingh77</name>
<email>amankumarsingh7702@gmail.com</email>
</author>
<published>2026-07-28T15:04:49+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/commit/?id=a9bdbd8590801cd9c2fa0cda6203948ac19a779c'/>
<id>urn:sha1:a9bdbd8590801cd9c2fa0cda6203948ac19a779c</id>
<content type='text'>
Drops leading blank lines where a file header was removed, collapses
double blank lines, and closes the empty catch/handler blocks whose only
content had been a comment. Whitespace only.

Co-Authored-By: Claude Opus 5 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>refactor(test): strip comment noise</title>
<updated>2026-08-02T13:42:46+00:00</updated>
<author>
<name>amankumarsingh77</name>
<email>amankumarsingh7702@gmail.com</email>
</author>
<published>2026-07-28T15:02:57+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/commit/?id=34e5cfdcbe29bbb5f2f46ba54eb67903abd9274d'/>
<id>urn:sha1:34e5cfdcbe29bbb5f2f46ba54eb67903abd9274d</id>
<content type='text'>
Removes suite banners, section dividers, and test-id markers that restate
the test names. Keeps one-line notes for the deleted-binary fixture order,
the never-arriving event consumer, the absent-vs-undefined key distinction,
the unexpected-method-echo rationale, and the real-browser teardown and
Local State flush quirks.

Co-Authored-By: Claude Opus 5 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix(domains): stop fill_form misreporting select-by-label, let it fail</title>
<updated>2026-08-02T13:42:46+00:00</updated>
<author>
<name>amankumarsingh77</name>
<email>amankumarsingh7702@gmail.com</email>
</author>
<published>2026-07-27T19:10:22+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/commit/?id=1c73f4af643aa60ca554a3770264791d299e64e2'/>
<id>urn:sha1:1c73f4af643aa60ca554a3770264791d299e64e2</id>
<content type='text'>
Three review fixes on the restored fill_form:

- FILL_FN matches a &lt;select&gt; option by value, label or text but returns
  el.value, so filling by visible label ("Canada" -&gt; value "ca") tripped
  the read-back mismatch check and reported a correctly-set field as
  "Filled 0/1 fields; issues: ref e4 (value mismatch)". The mismatch test
  now also accepts res.text, which FILL_FN sets only for selects, so
  mismatch detection is preserved for every other field kind.
- fill_form always returned ok(), so total failure surfaced as a
  successful result reading "Filled 0/4 fields". It now returns
  invalid_state when okCount is 0, carrying the per-field issue list as
  the error message. Partial success stays ok().
- Removed the two comment blocks this task had added to
  test/manual/forms-test.ts.

Neither behaviour fix has automated coverage; the manual suite that would
exercise them still needs a live Chrome and was not run.

Co-Authored-By: Claude Opus 5 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix(domains): restore fill_form and set_checked, drop the stale fork</title>
<updated>2026-08-02T13:42:46+00:00</updated>
<author>
<name>amankumarsingh77</name>
<email>amankumarsingh7702@gmail.com</email>
</author>
<published>2026-07-27T18:58:45+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/commit/?id=e46f080c03a511b626b21455cfd6094554b02733'/>
<id>urn:sha1:e46f080c03a511b626b21455cfd6094554b02733</id>
<content type='text'>
domains/forms.ts was a stale fork of form.ts imported only by a manual
test. It held two working tools the registry never registered: fill_form
and set_checked. Ported both onto the current pattern and deleted the
fork; form.ts versions of the shared tools win.

The fork's tools took ref as Type.Integer (a raw CDP backendNodeId).
browser_snapshot has since moved to string refs ('e7') resolved through
the session ref map, so as written the tools were not callable at all.
Ported them onto resolveRefToObjectId and Type.String refs; the only
user-facing strings that changed are the four that documented the old
[ref=N] syntax. All page-side JS is carried byte-for-byte.

form.ts exceeded 250 lines, so fillForm moved to form-batch.ts per the
single-field/batch split. form.ts is still 425 lines — the pre-existing
three tools account for that, so the split does not reach the target.

test/manual/forms-test.ts is repointed and adapted to publish an 'eN'
ref map, but NOT executed — it needs a live Chrome. Type-checked only.

Co-Authored-By: Claude Opus 5 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>chore(build): split tsconfig, typecheck tests, add node:test runner</title>
<updated>2026-08-02T13:42:46+00:00</updated>
<author>
<name>amankumarsingh77</name>
<email>amankumarsingh7702@gmail.com</email>
</author>
<published>2026-07-26T18:43:06+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/commit/?id=3607b5523f9d23ec6b9facf8474c7bdf047537e1'/>
<id>urn:sha1:3607b5523f9d23ec6b9facf8474c7bdf047537e1</id>
<content type='text'>
test/ was never type-checked and had no runner. Adds tsconfig.base.json
shared by a src project and a test project, an npm test script scoped to
non-manual suites, and a CI step. Ports the profile store suite as proof.

Co-Authored-By: Claude Opus 5 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat: pin the agent to a chosen browser profile (#16)</title>
<updated>2026-07-24T18:53:05+00:00</updated>
<author>
<name>Aman Kumar</name>
<email>145339995+amankumarsingh77@users.noreply.github.com</email>
</author>
<published>2026-07-24T18:53:05+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/commit/?id=6dd79f7c3bafacba54acb2840f8655fcb05318bf'/>
<id>urn:sha1:6dd79f7c3bafacba54acb2840f8655fcb05318bf</id>
<content type='text'>
* feat: pin the agent to a chosen browser profile (#15)

Harness tabs were created with a bare Target.createTarget, which places
them in Chrome's defaultBrowserContextId — and that value follows window
focus. Which profile the agent acted as therefore depended on which
browser window the user last clicked, so the same task could run as a
work account on one run and a personal account on the next.

/browser-profile lists the connected browser's profiles as "Name (email)"
and pins one to ~/.pi/agent/browser-harness.json, so the choice outlives
the session. The same picker appears on first browser_setup when nothing
is pinned; with no interactive UI, or on cancel, setup continues with a
note and the previous behavior.

Chrome offers no direct route to another profile: Target.createTarget
rejects a foreign browserContextId, and openerId does not inherit the
opener's context. The window is opened through the browser's own command
line (--profile-directory, handed to the running browser by Chromium's
ProcessSingleton) and identified by a unique file:// sentinel page, since
a bare about:blank#token argument is discarded. Further tabs come from
window.open evaluated with userGesture: true — the only CDP-reachable way
to place a tab in a non-default browser context — correlated by a unique
token so concurrent isolated tabs stay distinct. A failed handshake is
reported, never silently redirected to another profile.

All four tab-creation sites now funnel through cdp/target-factory, so
open_urls and the isolated tabs behind web_search / read_page cannot run
with a different profile's cookies than the visible tabs.

Also fixed along the way: snap Chromium is discovered, Windows uses
%LOCALAPPDATA%, browsers launched with an explicit --user-data-dir are
found, the running browser is chosen by ranking candidates against the
connected user-data-dir rather than taking the first process found, and
an installed-but-closed browser no longer reads as running.

CI runs the profile suites and a real-browser end-to-end test on ubuntu,
macOS, and Windows — profile discovery is the one part of the harness
whose behavior genuinely differs per OS.

Co-Authored-By: Claude Opus 4.8 (1M context) &lt;noreply@anthropic.com&gt;

* fix: single-flight harness window creation

Isolated tabs run concurrently (browser_read_page is registered
unserialized), so several could each find "no harness window" and create
one — two blank windows before this feature, two LAUNCHED profile windows
after it. ensureHarnessWindow now shares one in-flight creation per
client.

Sharing introduces a second hazard: freshlyCreated is a claim on the seed
tab, and if every waiter received it, they would all drive the same tab.
Only the caller that started the creation keeps the claim; the rest open
their own tab beside it.

Covered by test/manual/profile-concurrency-test.ts (five concurrent
isolated tabs, pinned and unpinned: distinct targets, one window, correct
profile), wired into CI on Linux only — the coordination is pure JS and
identical across platforms.

Co-Authored-By: Claude Opus 4.8 (1M context) &lt;noreply@anthropic.com&gt;

* fix: tilde expansion differs by purpose on Windows

CI on windows-latest caught this: join("~", "x") yields "~\x" there, and
expandTilde only handled "~/".

The two uses are not the same, so they no longer share a helper. The
agent dir stays byte-compatible with pi's own expandTildePath ("~/" only,
every platform) — being more lenient would put the pin file somewhere pi
never looks. $CHROME_USER_DATA_DIR is ours to interpret, so it accepts
"~\" as well, which is what a Windows user would type.

Co-Authored-By: Claude Opus 4.8 (1M context) &lt;noreply@anthropic.com&gt;

---------

Co-authored-by: Claude Opus 4.8 (1M context) &lt;noreply@anthropic.com&gt;</content>
</entry>
<entry>
<title>feat: add forms domain and durable window binding (v0.9.0) (#14)</title>
<updated>2026-07-21T19:29:58+00:00</updated>
<author>
<name>Aman Kumar</name>
<email>145339995+amankumarsingh77@users.noreply.github.com</email>
</author>
<published>2026-07-21T19:29:58+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/commit/?id=fe00d3361dece3d35f788d3eeb1c750eedbf6294'/>
<id>urn:sha1:fe00d3361dece3d35f788d3eeb1c750eedbf6294</id>
<content type='text'>
- Add forms domain with a universal field setter that auto-detects element
  type and fires framework-compatible input events (React native-setter trick)
- Add window-binding helper to capture and bind the real Chrome windowId as the
  session's durable window identity on every new-window code path
- Wire supporting changes across CDP session/ownership, client, navigate,
  isolated-tab, prompt, and state
- Add manual tests for forms and window binding
- Bump version 0.8.3 -&gt; 0.9.0

Co-authored-by: Claude Opus 4.8 (1M context) &lt;noreply@anthropic.com&gt;</content>
</entry>
</feed>
