all repos — sz-chrome-remote-extension @ 66dcc84d51bed88cbfb608b9dc6800a8edd32b3c

pi-chrome, patched to drive a signed-in browser on another host through a broker, with an MCP server

66dcc84d
ship the pi side we actually run, not a different arrangement

The README told pi users to install pi-chrome directly and run /chrome
authorize every session. That is not how this is used: pi reaches the same MCP
server through pi-mcp-adapter, with pi's own pi-chrome switched off, which is
why nobody types /chrome authorize.

pi/browser-gate.ts is that setup's missing piece, generalised from the one in
use: it answers pi-mcp-adapter's approval broker for one server, asking once per
session instead of once per call (the adapter's own grant only covers identical
arguments, so for a browser it asks nearly every time). /browser authorize,
/browser revoke, and PI_BROWSER_AUTHORIZE for headless runs. approveTools stays
on in mcp.json as the backstop if the gate does not load.

The README now leads with that: install the adapter, register the server in
mcp.json, copy the gate. Installing pi-chrome in pi is kept as the alternative,
with what it costs - two definitions of the browser, /chrome authorize per
session, and the trap of loading the extension twice.

Tested on an isolated pi config against a stand-in browser: with
PI_BROWSER_AUTHORIZE the call reaches the browser, without it the gate denies it.
Igor Soarez igor@soarez.org
Fri, 25 Sep 2026 16:42:26 +0300
fd06303b
FORK.md: what the fork changes, setup, upstream releases
Igor Soarez igor@soarez.org
Wed, 14 Oct 2026 12:08:17 +0100
7e2196bd
feat(remote): drive a companion extension on another host through a broker

PI_CHROME_REMOTE=host:port sends every command to a broker/broker.mjs there,
with its bearer token, instead of binding a bridge on this machine. Unset, the
extension behaves as upstream. A remote session never binds or takes over a
port: when the broker is unreachable the tools fail rather than drive some
other browser.

A pi-chrome-remote prompt section says which browser chrome_* drives and how
files move to and from its host; chrome_upload_file wants absolute paths there.
PI_CHROME_AUTHORIZE grants every session up front.
Igor Soarez igor@soarez.org
Wed, 14 Oct 2026 12:08:17 +0100
f2e93bba
broker: a standalone bridge for the browser's host

Upstream's bridge lives inside whichever pi session started first on the
machine running the browser, and only serves that machine. The broker takes
its place permanently: upstream's HTTP contract to the extension on loopback,
and /command and /status on a second, private address for other hosts, behind
a bearer token (PI_CHROME_TOKEN, or PI_CHROME_TOKEN_COMMAND).
Igor Soarez igor@soarez.org
Wed, 14 Oct 2026 12:08:17 +0100
1d7c4704
Release 0.15.57 (#28)
Tianren Dong tianrendong.dtr@gmail.com
Mon, 28 Sep 2026 20:24:49 -0400
72233686
Drop the full-screen tab rule for automation targets (#27)

0.15.54 opened Pi's automation target as a tab in the user's window while it
was full screen, assuming macOS would merge a new window into it as a hidden
window tab. Chrome disables automatic window tabbing (app_controller_mac.mm),
so that never happens; the hidden tabs came from the tab-group bug fixed in
0.15.56. Always prefer Pi's own window.
Tianren Dong tianrendong.dtr@gmail.com
Mon, 28 Sep 2026 20:19:57 -0400
0192da77
Report hidden Pi tab in /chrome doctor (#26)

Chrome ignores clicks and typing in hidden pages. Doctor now asks the worker
whether this session's own tab is visible (automation.status includeVisibility,
which never creates or activates a tab) and reports the reason and fix when it
is hidden.
Tianren Dong tianrendong.dtr@gmail.com
Mon, 28 Sep 2026 20:18:03 -0400
104d7f44
Keep tab ownership across extension reloads (#25)

storage.session is wiped when the extension reloads, which it does on every
pi-chrome update, so Pi lost track of its automation window and session tabs
and left them behind. Mirror ownership to storage.local, restore it only when
the session copy is missing, re-check every restored tab (same window; adopted
tabs still in Pi's group), and drop the local copy on browser startup.
Tianren Dong tianrendong.dtr@gmail.com
Mon, 28 Sep 2026 20:04:23 -0400
c78b1dc8
Merge pull request #24 from tianrendong/fix/fill-textarea-and-new-tab-capture

Fill whole textareas; cover new tabs' first page with early capture
Tianren Dong tianrendong.dtr@gmail.com
Mon, 28 Sep 2026 19:47:21 -0400
6c5d31ed
Release 0.15.55
Tianren Dong tianrendong.dtr@gmail.com
Sat, 26 Sep 2026 23:32:49 -0400
f0b4cc16
Merge pull request #23 from tianrendong/fix/cache-friendly-primer

Make Chrome primer and authorization log prompt-cache friendly
Tianren Dong tianrendong.dtr@gmail.com
Sat, 26 Sep 2026 23:29:35 -0400
4d93d850
Merge pull request #22 from tianrendong/fix/early-capture-scope

Stop console capture from tripping DevTools detection; scope early capture to Pi tabs
Tianren Dong tianrendong.dtr@gmail.com
Sat, 26 Sep 2026 22:42:19 -0400
b8dd042d
Merge pull request #21 from tianrendong/fix/hidden-input-uploads-screenshots

Report hidden-tab input, append targeted typing, smarter uploads, prune screenshots; release 0.15.54
Tianren Dong tianrendong.dtr@gmail.com
Sat, 26 Sep 2026 19:24:12 -0400
df1472b8
Merge pull request #20 from tianrendong/feat/absorb-fork-features

Absorb fork fixes and features; release 0.15.53
Tianren Dong tianrendong.dtr@gmail.com
Sat, 26 Sep 2026 16:38:22 -0400
4811ee61
Merge pull request #17 from tianrendong/refactor/chrome-command-surface

Simplify Chrome status commands and release 0.15.51
Tianren Dong tianrendong.dtr@gmail.com
Thu, 10 Sep 2026 13:34:13 -0400
a9b8023c
Merge pull request #16 from tianrendong/docs/readme-0.15.50

docs: clarify background controls and release 0.15.50
Tianren Dong tianrendong.dtr@gmail.com
Thu, 10 Sep 2026 10:53:13 -0400
38475fd3
Merge pull request #15 from tianrendong/docs/readme-onboarding

docs: improve pi-chrome README and onboarding
Tianren Dong tianrendong.dtr@gmail.com
Thu, 10 Sep 2026 10:22:43 -0400
304c40a7
Merge pull request #14 from tianrendong/fix/input-reliability

Improve Chrome input reliability and release 0.15.49
Tianren Dong tianrendong.dtr@gmail.com
Thu, 10 Sep 2026 09:58:32 -0400
f7466310
Merge pull request #13 from tianrendong/fix/hard-background-policy

Enforce hard background and release 0.15.48
Tianren Dong tianrendong.dtr@gmail.com
Wed, 09 Sep 2026 23:50:16 -0400
641ae30f
Merge pull request #12 from tianrendong/fix/session-cleanup-safety

Fix session cleanup and release 0.15.47
Tianren Dong tianrendong.dtr@gmail.com
Wed, 09 Sep 2026 23:14:54 -0400
017ff4b9
Fix Chrome authorization update logging
Tianren Dong tianrendong.dtr@gmail.com
Tue, 07 Jul 2026 01:30:54 -0400
7fcc851d
Record Chrome tool authorization state explicitly
Tianren Dong tianrendong.dtr@gmail.com
Tue, 07 Jul 2026 01:18:11 -0400
132c106b
Log Chrome tool authorization changes
Tianren Dong tianrendong.dtr@gmail.com
Tue, 07 Jul 2026 00:44:55 -0400
a609df84
Release v0.15.43
Tianren Dong tianrendong.dtr@gmail.com
Thu, 25 Jun 2026 21:15:41 -0400
ceb30599
Make README capabilities user-facing
Tianren Dong tianrendong.dtr@gmail.com
Thu, 25 Jun 2026 21:15:39 -0400
c143c5b9
Release v0.15.42
Tianren Dong tianrendong.dtr@gmail.com
Thu, 25 Jun 2026 21:08:50 -0400
4df0a074
Remove README quick start section
Tianren Dong tianrendong.dtr@gmail.com
Thu, 25 Jun 2026 21:08:48 -0400
375c28b0
Release v0.15.41
Tianren Dong tianrendong.dtr@gmail.com
Thu, 25 Jun 2026 21:06:42 -0400
d5d5bcd4
Shorten README and move architecture docs
Tianren Dong tianrendong.dtr@gmail.com
Thu, 25 Jun 2026 21:06:39 -0400
2c1a4b3c
0.15.40: keep automation targets in session group
Tianren Dong tianrendong.dtr@gmail.com
Mon, 22 Jun 2026 21:04:05 -0400
c1ed934d
Keep Pi-created tabs in one session group
Tianren Dong tianrendong.dtr@gmail.com
Mon, 22 Jun 2026 19:55:46 -0400
b8165967
0.15.39: dedicated pi-chrome automation tab/window isolation (#6)

Stop pi-chrome from hijacking the user's active tab. Page/navigation actions
with no explicit target now route to a dedicated automation window owned by the
calling Pi session (falling back to a dedicated tab if a window can't be made),
instead of overwriting whatever the user has open.

- Session-scoped ownership: Map<sessionKey, target> in the service worker, keyed
  by a sessionKey forwarded from index.ts, so concurrent sessions (one shared
  extension) never fight over a window or close each other's tabs.
- Persistence: ownership mirrored to chrome.storage.session so a service-worker
  restart re-hydrates and reuses the window instead of orphaning it. Cleared on
  full browser restart; restored windows are then left alone (never reclaimed).
- getTabByParams default branch resolves the owned target; tab.activate/close/
  group/ungroup pass createOwnedTarget:false so management never auto-creates a
  throwaway tab or falls back to the user's active tab.
- Session-group integration: the dedicated-window tab still joins this session's
  tab group, in its own window; grouping stays best-effort.
- Lifecycle: cleanup on /chrome revoke and real session end (not /reload);
  fire-and-forget so it never blocks /quit, /reload, or shutdown.
- Tests: new test-suite/unit/automation-target.test.mjs (isolation, reuse,
  concurrency, restart/reconnect re-hydration, guarded tab.*, group integration,
  window->tab fallback, robust cleanup). Docs + CHANGELOG updated.
TJ Dong 122852285+tj-dong@users.noreply.github.com
Mon, 22 Jun 2026 16:55:20 -0700
dce772f3
feat: show remaining auth time in Chrome Bridge status bar (#5)

* 0.15.36: richer chrome_snapshot + chrome_find/chrome_inspect (ported from foxfirecodes fork)

- chrome_snapshot returns a concise observation (layout, hints, actions, forms,
  page map, query matches, diff) with mode/query/maxTextChars params
- new chrome_find (query -> ranked uids) and chrome_inspect (deep element context,
  with page.inspect fallback)
- includeSnapshot on click/type/fill/key now embeds the formatted snapshot
- snapshot/inspect logic shipped as eval-free packaged snapshot_injected.js,
  sharing window.__PI_CHROME_STATE__ (el- uid scheme) with existing input helpers

* feat: show remaining auth time in Chrome Bridge status bar

Before: ● Chrome Bridge
After:  ● Chrome Bridge (14m)
        ● Chrome Bridge (indefinite)

Changes:
- authCountdownLabel() computes remaining minutes from chromeAuthorizedUntil
- updateChromeStatus appends the label to the status line
- startCountdownTicker() runs setInterval every 60s to refresh the display
  while a timed authorization is active; no-ops for indefinite auth
- clearCountdownInterval() called in lockChromeControl and session_shutdown
  so no interval leaks on revoke or reload
- scheduleAuthExpiry calls startCountdownTicker so the ticker starts
  whenever authorization is granted or restored after /reload

---------

Co-authored-by: Tianren Dong <tianrendong.dtr@gmail.com>
TJ Dong 122852285+tj-dong@users.noreply.github.com
Mon, 08 Jun 2026 20:39:14 -0700
35bebbbb
0.15.38: harden chrome input overlay handling
Tianren Dong tianrendong.dtr@gmail.com
Sun, 07 Jun 2026 23:30:40 -0400
4701544c
0.15.37: harden Chrome input targeting
Tianren Dong tianrendong.dtr@gmail.com
Sun, 07 Jun 2026 22:52:24 -0400
20585373
0.15.37: clarify README installation instructions (#4)
Tianren Dong tianrendong.dtr@gmail.com
Sun, 07 Jun 2026 20:45:51 -0400
0f3ca97b
Clarify README installation instructions (#4)
TJ Dong 122852285+tj-dong@users.noreply.github.com
Sun, 07 Jun 2026 15:22:23 -0700
341e2b3c
0.15.36: richer chrome_snapshot + chrome_find/chrome_inspect

- chrome_snapshot returns a concise observation (layout, hints, actions, forms,
  page map, query matches, diff) with mode/query/maxTextChars params
- new chrome_find (query -> ranked uids) and chrome_inspect (deep element context,
  with page.inspect fallback)
- includeSnapshot on click/type/fill/key now embeds the formatted snapshot
- snapshot/inspect logic shipped as eval-free packaged snapshot_injected.js,
  sharing window.__PI_CHROME_STATE__ (el- uid scheme) with existing input helpers
Tianren Dong tianrendong.dtr@gmail.com
Wed, 03 Jun 2026 20:57:27 -0400
fa33b079
0.15.35: persist /chrome authorize grant across /reload (re-register chrome_* tools on session_start)
Tianren Dong tianrendong.dtr@gmail.com
Mon, 01 Jun 2026 21:49:15 -0400
1c7cb993
0.15.34: tabs Pi uses join session group; fix punctuation dropped by chrome_type/chrome_key (US-layout keymap)
Tianren Dong tianrendong.dtr@gmail.com
Mon, 01 Jun 2026 21:40:07 -0400
f7f6e2a7
0.15.33: default background mode on (chrome_* run without focusing Chrome)
Tianren Dong tianrendong.dtr@gmail.com
Sun, 31 May 2026 16:12:27 -0400
afda74a2
0.15.32: session tab-group title uses 'Pi Session: <name-or-id>' (colon)
Tianren Dong tianrendong.dtr@gmail.com
Sun, 31 May 2026 15:00:57 -0400
745d1eee
0.15.31: per-session tab groups named 'Pi Session <name-or-id>'
Tianren Dong tianrendong.dtr@gmail.com
Sun, 31 May 2026 14:51:07 -0400
d57dc658
0.15.30: chrome_tab grouping — auto-group Pi tabs, group/ungroup actions, merge by title
Tianren Dong tianrendong.dtr@gmail.com
Sun, 31 May 2026 14:38:38 -0400
d21ebf7b
0.15.29: CSP bypass via CDP Runtime.evaluate for evaluate/snapshot/waitFor/initScript

- evaluateInTab/executeInTab now run through CDP Runtime.evaluate instead of
  new Function() in MAIN world, so chrome_evaluate/chrome_snapshot work on pages
  that block unsafe-eval (github.com, many bank/SaaS apps). Preserves value
  markers (undefined/function/symbol/bigint/Error), DOMRect expansion, and the
  expression/statement fallback; keeps {ok,value} error envelope.
- page.waitFor polls via service-worker-side CDP evaluation.
- chrome_navigate initScript uses Page.addScriptToEvaluateOnNewDocument.
- Add npm test (test-suite/unit/csp-eval.test.mjs) and in-browser regression
  challenge 42 strict-csp-evaluate; update challenge 39 notes + docs/primer.
Tianren Dong tianrendong.dtr@gmail.com
Sun, 31 May 2026 14:19:04 -0400
0fc5b895
0.15.28: bridge self-heal, timeout diagnostics, pressKeyInPage helper, DOMRect serialization, stale-target errors, pageMutated wording
Tianren Dong tianrendong.dtr@gmail.com
Sun, 31 May 2026 13:59:42 -0400
ea6c25d1
chore: release v0.15.27
Tianren Dong tianrendong.dtr@gmail.com
Sun, 17 May 2026 23:24:57 -0400
c1949f2d
chore: standardize gitignore
Tianren Dong tianrendong.dtr@gmail.com
Sun, 17 May 2026 23:16:19 -0400
0c361389
0.15.26: update docs for benchmark gates
Tianren Dong tianrendong.dtr@gmail.com
Sat, 16 May 2026 14:12:13 -0400
b792f22f
0.15.25: fix reload after stale singleton
Tianren Dong tianrendong.dtr@gmail.com
Sat, 16 May 2026 14:01:00 -0400
f133011c
0.15.24: unload Chrome tools when locked
Tianren Dong tianrendong.dtr@gmail.com
Sat, 16 May 2026 13:33:57 -0400
cd8195c8
0.15.23: attribute fork features
Tianren Dong tianrendong.dtr@gmail.com
Sat, 16 May 2026 13:22:52 -0400
d4d35255
0.15.22: pull fork improvements
Tianren Dong tianrendong.dtr@gmail.com
Sat, 16 May 2026 13:18:25 -0400
ae0e6d4e
feat: lazy-register chrome tools until /chrome authorize

Defer all pi.registerTool() calls until /chrome authorize fires.

Previously, 18 chrome_* tool definitions + chrome-profile-bridge
primer consumed ~5K tokens/turn even when Chrome control locked.

Changes:
- Add chromeToolsRegistered flag (default false)
- Wrap all pi.registerTool() calls in registerChromeTools(pi)
- Only register tools when authorizeFor() runs
- Make before_agent_start conditional: primer injected only when
  tools registered AND authorized
- No tools exist in context until user explicitly allows it

Saves ~5,000 tokens/turn when Chrome control is locked.
Dani Bednarski 49581958+DaniBedz@users.noreply.github.com
Sun, 17 May 2026 00:24:02 +1000
4739dce9
silence startup notify + dynamic status bar

- Remove startup notification about companion extension
- Remove static 'Chrome bridge :17318' status bar entry
- Add updateChromeStatus() helper: shows green dot + 'Chrome Bridge'
  only when authorized, clears otherwise
- Call from session_start, authorizeFor, revokeHandler
Dani Bednarski 49581958+DaniBedz@users.noreply.github.com
Sat, 16 May 2026 21:04:50 +1000
d9f13a36
fix: capture early console/network messages at document_start

Adds an early-capture script injected at document_start on every
navigation via webNavigation.onCommitted + injectImmediately:true.

Wraps console.*, fetch, and XMLHttpRequest before page JS runs, so
console messages, errors, unhandled rejections, and network requests
during page load are captured in window.__PI_CHROME_STATE__.

Sets __piChromeWrapped flags so the existing post-hoc
installPiChromeInstrumentation() is idempotent (checks instrumentationInstalled).

Fixes chrome_list_console_messages returning empty for errors
that fire during React render / initial API calls.
Dani Bednarski 49581958+DaniBedz@users.noreply.github.com
Fri, 15 May 2026 22:08:50 +1000
0168a4e8
0.15.21: version bump

Tree-equivalent to v0.15.20 (preceded by the revert commit). Bumping
to 0.15.21 sets the package up for normal patch releases on the 0.15.x
line going forward — no version-number collisions with the unpublished
0.16/0.17 work.
Tianren Dong tianrendong.dtr@gmail.com
Sat, 16 May 2026 13:14:10 -0400
b2ca8f8c
Revert 0.16.x and 0.17.x: restore tree to v0.15.20 state

Reverts the pairing + signed-envelopes work (0.16.0-0.16.1), the
standalone daemon refactor (0.17.0-0.17.3), and assorted patches.
These were unpublished from npm; this commit restores the working
tree to match the v0.15.20 release so the branch tip and the npm
'latest' dist-tag are consistent again.

Tags v0.16.0 through v0.17.3 are preserved as recovery anchors;
the work is reachable via 'git checkout v0.17.3' if ever needed.

Tree-equivalent to v0.15.20.
Tianren Dong tianrendong.dtr@gmail.com
Sat, 16 May 2026 13:13:32 -0400
ea567819
0.17.3: daemon self-heal on missing-daemon fetch failure

In 0.17.x the daemon is a separate process that can idle-shut-down
(10 min idle) or be killed externally while Pi sessions are running.
Without self-heal, the next /chrome onboard or chrome_* call surfaces
'fetch failed' instead of recovering.

bridge.admin() now catches the failed fetch, re-runs ensureDaemonRunning
(probe -> spawn if missing), and retries once.

bridge.sendViaOwner() pre-probes /status and spawns the daemon if missing
before issuing the signed /command.

Net effect: a missing daemon transparently respawns instead of bubbling
fetch failed up to the user.
Tianren Dong tianrendong.dtr@gmail.com
Sat, 16 May 2026 13:03:51 -0400
fcae5b18
0.17.2: fix /next lastSeenAt + strict daemon version match

Bugs surfaced in real-world 0.17.0/0.17.1 testing:

1. Daemon /next only recorded lastSeenAt AFTER the auth/pair checks. Pre-pair
   the daemon returns needsPairing early without recording the poll, so
   connected stayed false even though the extension was polling. /chrome
   onboard's wait-for-poll loop spun forever on a healthy extension.

   Fix: record lastSeenAt at the top of /next for any chrome-extension
   origin, before auth checks. Onboard now correctly detects 'extension
   alive but unpaired' which is the precise state during pair step.

2. Daemon compatibility was checked at major.minor match. Result: a patch
   release fixing a daemon bug (like #1) couldn't replace the running
   daemon — new Pi client joins old buggy daemon as client.

   Fix: exact version match. Patch releases auto-kill+respawn the daemon.
Tianren Dong tianrendong.dtr@gmail.com
Sat, 16 May 2026 13:00:48 -0400
43c62c2f
0.17.1: fix /chrome onboard stuck on 'Waiting for the extension to wake up'

0.17.0 made the daemon own the HTTP listener, but the Pi client's
bridge.status().connected still reflected the local Pi's lastSeenAt
(which is never updated in the daemon model). Onboard's connect-wait
loop spun forever even when the extension was polling the daemon.

Fix: new ChromeProfileBridge.daemonStatus() async helper that fetches
the daemon's /status. onboard, doctor, and status now query that for
the 'connected' flag instead of reading stale Pi-side state.
Tianren Dong tianrendong.dtr@gmail.com
Sat, 16 May 2026 12:56:24 -0400
2ac665ec
0.17.0: standalone daemon, no more owner-vs-client topology

The Chrome bridge is now a separate detached daemon at
~/.cache/pi-chrome/<version>/daemon.cjs. Every Pi session is a uniform client;
daemon lifecycle is decoupled from any Pi session. Killing a Pi never breaks
Chrome control, and pi update followed by any new Pi session auto-replaces
an incompatible daemon.

Architecture:
- New extensions/chrome-profile-bridge/daemon.cjs: ~700 LOC, zero deps, runs
  detached. Owns the HTTP listener, BridgeAuth state, command queue, /pair
  handshake. Idle-shuts-down after 10 min of no activity.
- ChromeProfileBridge on Pi side becomes a pure client: start() probes /status,
  spawns daemon if missing/incompatible, kills+respawns if version mismatch.
  send() always uses signed /command. No more server-mode branch.
- New signed /admin RPC for pair-window/reset/status state mutations the
  daemon owns. Pi clients sign with broker key (same contract as /command).
- New /heartbeat for daemon idle-timer keepalive.
- daemon.log at ~/.cache/pi-chrome/<version>/daemon.log.

/chrome pair, /chrome unpair, /chrome onboard now go through /admin RPCs
instead of mutating in-process bridge state.

Removed the foreign-owner takeover dance from /chrome onboard (auto-handled
at session_start now).

Wire protocol is byte-identical to 0.16.x; existing paired extensions don't
need to re-pair. See docs/DESIGN-0.17-daemon.md for the full write-up.
Tianren Dong tianrendong.dtr@gmail.com
Sat, 16 May 2026 12:46:57 -0400
d9f2d027
0.16.1: idempotent /chrome onboard + automatic stale-owner takeover

User pain: machines stuck in broken state after pi-chrome upgrades because an old
pi-chrome process kept holding :17318 and /chrome onboard trusted confirm() clicks
without verifying anything. Both fixed.

Idempotent /chrome onboard recovery flow:
- Always resets Pi-side pairing state first (kills half-paired ghosts).
- Step 1 covers both first-install AND v0.15->v0.16 reload (extension service worker
  must reload to gain the /pair logic).
- Actively verifies progress at each step rather than trusting clicks:
  - polls bridge.status().connected (15s window) to confirm the extension polls /next,
  - polls bridge.bridgeAuth.paired to confirm the /pair handshake completed,
  - round-trips a real tab.version command to confirm the signed-envelope path works.
- /chrome install is now an alias of /chrome onboard.

Automatic stale-owner takeover at bridge.start():
- /status now includes bridgeVersion.
- On EADDRINUSE, probe /status with 1s timeout. If unresponsive OR major.minor
  mismatches, locate the owner PID via lsof and kill it (SIGTERM -> SIGKILL),
  then re-bind. Same-version owners are joined as clients (multi-Pi sharing).
- Eliminates the 'stale Pi session holding bridge' class of bugs that broke
  upgrades from 0.15.x.

/chrome status / doctor / onboard no longer hang:
- New probeExtension() short-circuits before bridge.send when the bridge can't
  round-trip (unpaired, or extension hasn't polled recently). Returns immediately
  with an actionable message instead of waiting 5-35s.
Tianren Dong tianrendong.dtr@gmail.com
Sat, 16 May 2026 12:36:56 -0400
ad78b411
0.16.0: mandatory pairing + signed envelopes + cross-process state coherence

Major security hardening of the Chrome bridge driven by red-team findings.

- Mandatory pairing via /chrome onboard (one-time per machine). Bridge pins exact
  chrome-extension:// origin and derives HMAC keys via HKDF. Until paired, the
  bridge returns idle responses and refuses to deliver commands.
- Signed envelopes on /next, /result, /command. HMAC-SHA256 over protocol version,
  direction, method, path, extension ID, bridge ID, timestamp, fresh nonce, and
  SHA-256 of the body. +/-30s clock window; 5-min LRU nonce replay defense on
  both request and response directions; constant-time MAC compare.
- Peer Pi sessions sharing the bridge sign each /command with a broker HKDF key
  (no bearer token transmitted) and verify the owner's signed response.
- Global loopback enforcement. PI_CHROME_BRIDGE_HOST=0.0.0.0 ignored unless
  PI_CHROME_BRIDGE_DANGEROUS_REMOTE=1; loopback check still applies per-endpoint.
- Cross-process state coherence: fs.watch + per-request mtime check on
  ~/.config/pi/chrome-bridge.json. A peer Pi's unpair (or any external rm/edit)
  propagates to the owner's in-memory state before the next privileged verify;
  replay caches reset on key rotation.
- Body cap 1 MiB -> 413. Queue/pending/waiter caps. Malformed JSON -> 400.
  Non-object JSON bodies -> 400 instead of being trusted.
- page.navigate initScript: try/finally around tabs.update, per-call onCommitted
  listener with bounded commit-wait window, one-shot delete in global onCommitted.
  No more stale init scripts firing on later unrelated navigations.
- crypto.randomUUID() command IDs.
- Manifest diet (dropped activeTab); added popup.html/popup.js for the pair UI.

UX:

- /chrome onboard runs install + pair in one interactive flow.
- /chrome help reorganized into First-time / Day-to-day / Maintenance.
- Status bar lights up as green dot only when authorized.
- Early console/network capture at document_start so boot-time React errors and
  initial API calls aren't dropped (ported from DaniBedz/pi-chrome fork).

Docs:

- SECURITY.md threat model rewritten with in-scope / out-of-scope sections.
- README rewritten with the install-then-pair flow front and center.
- CHANGELOG 0.16.0 entry with full patch list.
Tianren Dong tianrendong.dtr@gmail.com
Sat, 16 May 2026 11:55:27 -0400
7992115c
Make chrome_* tools interruptible via AbortSignal (#1)

Thread the harness AbortSignal through authorizedBridgeSend, the
ChromeProfileBridge send/sendLocal/sendViaOwner paths, and every
chrome_* tool's execute(). Pressing Esc now rejects the in-flight
bridge call immediately (most visibly fixing chrome_wait_for, which
would otherwise block for the full timeoutMs).
Tianren Dong tianrendong.dtr@gmail.com
Sat, 16 May 2026 08:37:24 -0400
0ce964f9
Clear pi-chrome singleton on shutdown
Tianren Dong tianrendong.dtr@gmail.com
Fri, 15 May 2026 02:42:38 -0400
2f038442
Simplify package description
Tianren Dong tianrendong.dtr@gmail.com
Thu, 14 May 2026 22:51:30 -0400
a356e410
Refine package description
Tianren Dong tianrendong.dtr@gmail.com
Thu, 14 May 2026 22:49:09 -0400
58e86b70
Audit pi-chrome docs
Tianren Dong tianrendong.dtr@gmail.com
Thu, 14 May 2026 22:46:31 -0400
1ec189f8
Show Chrome connection check state
Tianren Dong tianrendong.dtr@gmail.com
Thu, 14 May 2026 22:23:16 -0400
3416a7e7
Remove maintainer notes
Tianren Dong tianrendong.dtr@gmail.com
Thu, 14 May 2026 22:10:55 -0400
a13b6e4b
Restore terminal Chrome authorization
Tianren Dong tianrendong.dtr@gmail.com
Thu, 14 May 2026 22:09:07 -0400
815fd19c
pi-chrome 0.15.2: recipe prompts in user-language (no tool names)
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 08:49:49 -0400
7f88205f
pi-chrome 0.15.1: ASCII architecture diagram + author object form
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 08:42:37 -0400
150d92fc
pi-chrome 0.15.0: README rewrite — top-3 recipes, sharper comparison table, section reorder
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 08:05:16 -0400
e88e8a3a
pi-chrome 0.14.9: trusted-mode primer + auto-sync manifest version

- Primer teaches agents the trusted:true escape hatch for CSP-locked pages
  (github.com, banks, many SaaS) where chrome_evaluate/chrome_snapshot throw
  EvalError due to missing 'unsafe-eval'. Agents now self-escalate instead
  of giving up and asking the user.
- Corrects stale primer claim that chrome_evaluate works without 'unsafe-eval'.
- scripts/sync-manifest-version.js wired to npm version + prepublishOnly
  lifecycle hooks; kills the recurring browser-extension manifest version
  drift (cf. 0.14.4, 0.14.8).
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 07:51:17 -0400
2ebc7e80
pi-chrome 0.14.8: bump version + CHANGELOG for standalone repo move
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 07:36:06 -0400
2ede6e18
Migrate to standalone repo: update URLs
tianrendong tianrendong@users.noreply.github.com
Wed, 13 May 2026 07:30:44 -0400
4c54b05d
pi-chrome: snapshot pre-migration state
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 07:29:41 -0400
5951b3fb
pi-chrome: remove obsolete REBUILD-NOTES.md

Internal scratch notes from an earlier rebuild; no external references.
docs/, CHANGELOG.md, CONTRIBUTING.md, and README.md cover the same
ground for the current shape of the package.
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 07:20:35 -0400
26ab0d36
pi-chrome 0.14.2: recover from foreign-extension input overlays

When a different Chrome extension (password manager, autofill popup,
etc.) anchors an overlay to the focused input, Chrome's CDP refuses
our Input.dispatchMouseEvent with 'Cannot access a chrome-extension://
URL of different extension'. The CDP target list shows the foreign
chrome-extension:// URL as type:'other' attached to the same tab.

New behaviour in cdp() wrapper:
1. On that specific error for any Input.* call, snapshot CDP targets
   to identify the offending extension id and log it (foreign-ext-
   detected event added to ring buffer).
2. Send Esc via Input.dispatchKeyEvent (key dispatch routes by focus,
   not coordinates, so it bypasses the overlay) to dismiss the popup.
3. Retry the original Input.* call once.
4. If retry still fails, throw a clear error naming the foreign
   extension id and listing three concrete workarounds: disable that
   extension on the page, use Tab-to-focus instead of clicking, or
   run /chrome quiet off.
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 02:35:01 -0400
b319c82a
pi-chrome 0.14.1: harder attachDebugger retry + trusted.debug diagnostic

attachDebugger now:
- Calls chrome.debugger.getTargets() first and force-detaches any stale
  CDP session this extension already owns on the target tab before
  attaching. Stale sessions (left over from SW reloads or sibling
  detach failures) often surface on the next attach as 'Cannot access
  a chrome-extension:// URL of different extension'.
- Retries up to two more times (180ms + 500ms) instead of once. The
  500ms back-off lets Chrome's content-script registration drain on
  fresh navigations.
- Records every attach event (attached, stale-target-found, attach-
  failed, retry-failed) in a 20-entry ring buffer for inspection.

New diagnostic action 'trusted.debug' returns extension version,
trusted mode, attached tabs, the inspected tab's URL/status, current
chrome.debugger.getTargets() output, and the recent attach-event log.
Use to investigate when a specific URL refuses to attach.
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 02:23:52 -0400
36c2f7e3
pi-chrome 0.14.0: bare /chrome opens SettingsList dialog (space=cycle, enter=save)

Bare /chrome no longer opens a sequential picker. It now opens an inline
SettingsList from pi-tui with two rows:

  Click realism   auto / on / off
  Quiet mode      on / off

Up/Down navigate, Space or Enter cycles the value of the focused row,
Esc closes. Each row's description updates live to match the current
value, so users always see what the active setting means.

When the Chrome extension hasn't been granted the debugger permission
(real-looking clicks unavailable), the Click realism row stays locked
on its current value and the description points to the fix.

Direct subcommands unchanged:
  /chrome status / doctor / onboard
  /chrome clicks [auto|off|on|status]    (no-args still cycles)
  /chrome quiet  [on|off|toggle|status]  (no-args still toggles)
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 02:22:11 -0400
04fe59f7
pi-chrome 0.13.0: flatten /chrome tree, cycle-on-pick for clicks/quiet, status header

Reorganises the /chrome command surface:

  /chrome status   - one-line snapshot of connection + click mode + quiet mode
  /chrome doctor   - full health check (unchanged)
  /chrome onboard  - install extension (unchanged)
  /chrome clicks   - cycles auto -> on -> off -> auto when called with no args.
                     Explicit args (auto|off|on|status) still work.
  /chrome quiet    - toggles on/off when called with no args.
                     Explicit args (on|off|toggle|status) still work.

Removes the nested 'settings' wrapper. /chrome settings background/trusted
still functions as a hidden legacy alias but is no longer advertised.

Bare /chrome opens a picker with a one-line status header and cycle-target
labels: 'clicks: auto -> switch to on' / 'quiet: off -> switch to on'. No
second picker; one keypress flips the setting.

Argument autocomplete now returns full-path values (e.g. 'clicks auto') so
pi-tui's whole-argument replacement applies correctly. Top-level suggestions
show clicks / quiet / status / doctor / onboard with descriptions.

User-visible names match the descriptive copy:
  - 'trusted' -> 'clicks' (matches 'real-looking clicks' / 'quiet clicks')
  - 'background' -> 'quiet' (matches 'quiet mode' / 'watch mode')

README + agent prompt primer + internal hint strings updated.
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 02:17:58 -0400
dedf4be1
pi-chrome 0.12.1: fix 07/13/21/27/28 anti-automation regressions

- 07 pointer-properties: pass force: 0.5 on every CDP mousePressed so
  PointerEvent.pressure reports 0.5 for trusted clicks (was 0,
  inconsistent with real mouse behaviour).
- 13 focus-order: after a CDP-driven click on a focusable target,
  re-focus with focusVisible: false so :focus-visible matches pointer
  focus semantics (CDP press leaves :focus-visible=true otherwise).
- 21 keyboard-modifiers: chrome.debugger.attach occasionally rejects
  with 'Cannot access a chrome-extension:// URL of different
  extension' immediately after navigation. attachDebugger now waits
  180ms, verifies the tab URL isn't privileged, and retries once.
- 27 scroll-momentum + 28 intersection-visibility: replace gentle
  ~1.25%/step linear taper with a ramp-up (~15%) + geometric decay
  (~12% drop/step). Sizes n adaptively so peak event stays ≲22px so
  small visibility targets get low-ratio IO samples while a clear
  momentum tail remains for decay-pattern detectors.
- trustedDrag mousePressed also gets force: 0.5 (consistency).
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 02:11:03 -0400
eb15e9fb
pi-chrome 0.12.0: unify slash commands under /chrome

Replaces /chrome-doctor, /chrome-onboard, /chrome-background,
/chrome-trusted with a single /chrome command that takes subcommands:

  /chrome doctor                            (was /chrome-doctor)
  /chrome onboard                           (was /chrome-onboard)
  /chrome settings background [on|off]      (was /chrome-background)
  /chrome settings trusted [auto|off|on|status]   (was /chrome-trusted)

Running /chrome with no arguments opens a picker. Argument auto-
completion walks the tree (e.g. type '/chrome se<Tab>' to land on
'settings', then '/chrome settings t<Tab>' to land on 'trusted').

All user-visible references (notify messages, doctor output, agent
prompt primer, README) updated to the new paths.
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 02:08:47 -0400
bc2f43aa
pi-chrome 0.11.5: plain-English audit pass for /chrome-doctor, /chrome-background, /chrome-onboard, README

Replaces remaining jargon in every user-facing string:

- /chrome-doctor: 'Local bridge: mode=server, url=...' -> 'This pi
  session is running the Chrome connection for this machine.'
  'EXTENSION VERSION MISMATCH ...' -> 'The Chrome companion extension
  is on an old version (X); this pi-chrome is Y.'
  'chrome_evaluate("1+1") = 2' -> 'pi-chrome can run code in the
  active Chrome tab.'
  'navigator.webdriver=true on current tab — site fingerprinting may
  flag automation.' -> 'Your Chrome is reporting itself as automated
  to websites. Some sites use this signal to block sign-ins or bot
  checks.'
- /chrome-background description + notify: drops 'silent/background
  mode', frames as 'quiet mode' vs 'watch mode'.
- /chrome-onboard: walks through the click-by-click steps instead of
  saying 'Developer mode -> Load unpacked'.
- session_start notify: drops 'Chrome profile bridge'; says 'pi-chrome
  connected' / 'waiting for the Chrome companion to connect'.
- README 'Trusted-input mode (CDP)' section rewritten as 'Click modes'
  with plain prose explaining quiet vs real-looking clicks and when
  each fails.

Tool descriptions (seen by the agent) keep precise terminology.
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 02:03:03 -0400
40c14a84
pi-chrome 0.11.4: drop 'yellow' from banner copy; quote exact banner text

Banner is themed by the user's Chrome (dark mode → dark grey for many
users), not always yellow. Use 'banner' alone or quote the actual
banner text 'Pi Chrome Connector started debugging this browser'.
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 01:58:25 -0400
c53af2f8
pi-chrome 0.11.3: plain-English copy in /chrome-trusted + /chrome-doctor

User-facing strings rewritten in layman's terms — no more 'CDP',
'isTrusted', 'user-activation gates', 'synthetic events', 'debugger
API'. /chrome-trusted picker now explains each mode by what users
actually experience: yellow banner, which sites work, when to use it.
/chrome-doctor reports 'Click mode' instead of 'Trusted-input mode'.
Tool descriptions (read by the agent) keep precise terminology.
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 01:55:49 -0400
f0d86b4f
pi-chrome 0.11.2: cap trustedScroll per-event delta so IO threshold gradient lands
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 01:50:25 -0400
066f50b9
pi-chrome 0.11.1: chrome_tap (CDP touch), smoother trustedScroll, cdp auto-recover

- chrome_tap tool: real browser-trusted touchstart/touchend via
  Input.dispatchTouchEvent for sites that gate on TouchEvent (mobile-
  first PWAs, swipe carousels). Always uses CDP; banner appears.
- trustedScroll: per-step delta capped at ~80px, n=6..60 steps with a
  bell-shaped weight curve, 22-52ms sleep between events so IntersectionObserver and rAF samples get gradient updates (fixes
  intersection-visibility / smooth-scroll tests).
- cdp() wrapper auto-detects stale debugger sessions (tab nav,
  DevTools toggle, manual cancel) and re-attaches+retries once
  instead of throwing 'Debugger is not attached'. Fixes intermittent
  failures during automated test sweeps.
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 01:48:36 -0400
07cf31df
pi-chrome 0.11.0: smart-auto trusted mode default + rename to Pi Chrome Connector

Trusted-input mode default flips from 'off' to 'auto':
- Synthetic events still run first for every chrome_* call.
- If a click produced no page change AND the target's label looks like a
  user-activation-gated affordance (play/copy/share/sign-in/etc), or the
  page just emitted NotAllowedError / 'transient activation required',
  the call is silently retried via chrome.debugger (CDP).
- Typing auto-retries when the input value didn't change at all
  (editor rejected synthetic input).
- Result carries autoRetried=true + autoRetryReason + syntheticAttempt
  so callers / agents see the upgrade happened.
- Yellow debugger banner now appears only for the retry, not every call.

Companion extension renamed 'Pi Existing Chrome Profile Bridge' →
'Pi Chrome Connector' (manifest, action title, SW client name, README,
slash-command notify strings). One-time chrome://extensions reload
needed to pick up the new name; auto-reload mechanism handles the rest.

/chrome-doctor explains the active mode and what smart-retry does.
/chrome-trusted picker shows 'auto (current)' first; on-mode now warned
as 'banner permanently visible'.
chrome_click / chrome_type tool descriptions explain auto-retry.
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 01:43:33 -0400
6478feb0
pi-chrome 0.10.2: interactive /chrome-trusted picker

- No-arg /chrome-trusted now shows ui.select picker with current mode +
  tradeoff for each option (on/off/auto/status)
- First-time on transition asks ui.confirm with banner warning
- Permission missing emits a clear ui.notify with fix instructions
- Direct args (/chrome-trusted on|off|auto|status) still work
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 01:38:41 -0400
6ddb6007
pi-chrome 0.10.1: chrome_key modifiers param for trusted chords (Cmd+V etc)
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 01:29:51 -0400
6dd4b2b0
pi-chrome 0.10.0: trusted-input mode via chrome.debugger (CDP)

Add opt-in CDP path so chrome_* tools can dispatch browser-trusted input
(isTrusted=true, satisfies user-activation gates: clipboard, fullscreen,
file picker, autoplay). Default remains synthetic events; no behavior
change unless trusted mode is enabled.

Companion extension:
- new "debugger" permission in manifest (one-time re-approval prompt in
  chrome://extensions on update)
- attach/detach lifecycle per tab with idle-detach (15s) and listener
  for onDetach (incl. canceled_by_user)
- trustedClick / trustedHover / trustedType / trustedFill / trustedKey /
  trustedScroll / trustedDrag use Input.dispatchMouseEvent +
  Input.dispatchKeyEvent through chrome.debugger.sendCommand, reusing
  humanized timing, in-rect coord variance, pointer continuity, Shift
  chord for uppercase, per-key cadence
- trusted.mode / trusted.status bridge actions

Pi side:
- /chrome-trusted slash command (on / off / auto / status)
- trusted=true param on chrome_click, chrome_type, chrome_fill,
  chrome_key, chrome_hover, chrome_drag, chrome_scroll
- /chrome-doctor probes chrome.debugger availability and reports current
  trusted mode + attached tabs
- README documents trusted-input mode and tradeoffs
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 01:23:54 -0400
33110e80
pi-chrome: humanize synthetic input + anti-automation test suite

Bump 0.8.0 → 0.9.1.

Companion extension (service_worker.js):
- humanise pointer paths: easing Bezier-ish interpolation w/ wobble, 12–42
  steps, 4–18 ms gaps; pointer position persisted across calls
- click point picked inside element rect, not dead center
- pointer events fill movementX/Y, screenX/Y, pressure (0.5 on down),
  pointerId, width/height, tilt, composed
- click sequence: humanMoveTo → pointerdown → focus → 45–140 ms hold →
  pointerup → mouseup → click
- per-character typing: keydown → keypress → beforeinput → value mutate
  (or execCommand for contenteditable) → input → keyup, with 25–95 ms
  hold + 35–140 ms inter-key; auto Shift chord for uppercase/symbols
- pressKeyInPage emits same cadence + handles printable insert / Backspace
- hoverPage: humanised path + 80–220 ms dwell
- dragPage: full HTML5 sequence (dragstart/drag/dragenter/dragover/
  dragleave/drop/dragend) with shared DataTransfer + pointer continuity
- new scrollPage: wheel events with front-loaded momentum-shaped deltas
- self-reload on version drift: SW reads x-pi-chrome-version header,
  reloads when older than pi-chrome

Pi side (index.ts):
- new chrome_scroll tool
- PI_CHROME_VERSION read from package.json (and re-read on every /next
  so future bumps take effect without pi restart)
- /chrome-doctor escalates version mismatch to top-line ✗ error and
  skips downstream capability probes when stale
- /next response carries x-pi-chrome-version header for SW self-heal

Test suite (test-suite/):
- 28 challenge pages each catching a different synthetic-event tell:
  isTrusted, webdriver/fingerprint, mouse entropy/timing/coords,
  pointer pressure/continuity, per-char cadence + framework invariants,
  user activation, honeypots, focus order, wheel scroll, drag+
  DataTransfer, contenteditable+Selection, paste/clipboard, native
  select, hover dwell, React valueTracker, keyboard modifiers, touch
  events, stack-trace fingerprint, viewport-edge clicks, mousemove
  rate, scroll momentum, IntersectionObserver visibility
- shared _lib.js exposes window.__verdict + writes localStorage so
  index.html dashboard aggregates results
- notes/bypass-ideas.md documents fixes still needed per challenge
Tianren Dong tianrendong.dtr@gmail.com
Wed, 13 May 2026 01:05:46 -0400
d78081aa
Update pi-packs installer and package names
Tianren Dong tianrendong.dtr@gmail.com
Tue, 12 May 2026 23:53:16 -0400
2206c554
Track pi-chrome package source
Tianren Dong tianrendong.dtr@gmail.com
Fri, 08 May 2026 14:15:53 -0400
1ec29fc9
Fix session cleanup and release 0.15.47
Tianren Dong tianrendong.dtr@gmail.com
Wed, 09 Sep 2026 22:54:22 -0400
c1f230ee
Enforce hard background and release 0.15.48
Tianren Dong tianrendong.dtr@gmail.com
Wed, 09 Sep 2026 23:37:02 -0400
ec23b553
Improve Chrome input reliability and release 0.15.49
Tianren Dong tianrendong.dtr@gmail.com
Thu, 10 Sep 2026 09:31:19 -0400
502bcc81
docs: improve pi-chrome README and onboarding
Tianren Dong tianrendong.dtr@gmail.com
Thu, 10 Sep 2026 10:19:54 -0400
a761876b
docs: clarify background controls and release 0.15.50
Tianren Dong tianrendong.dtr@gmail.com
Thu, 10 Sep 2026 10:36:45 -0400
04e7b89f
Simplify Chrome status commands and release 0.15.51
Tianren Dong tianrendong.dtr@gmail.com
Thu, 10 Sep 2026 13:33:17 -0400
c49dc15f
Absorb fork fixes and features; release 0.15.53

Adapted from community forks (ardhiqii, kkunkunya, nihar-oracle, steimerbyte):

- Abort stalled /next long polls after 45s; time out, retry, and single-post results.
- Fall back to CDP Runtime.evaluate when chrome.scripting is refused (about:blank automation tabs).
- Load pi-chrome in same-process subagent sessions as a bridge client; client chrome_launch reports owner status.
- Add chrome_cdp (validated raw CDP passthrough, background-guarded, payload summarization) and chrome_cdp_targets.
- chrome_type reports before/after values, insertedAt, splice/unchanged warnings; add replace=true.
- includeSnapshot waits for navigations started by the action; tab.new reports the settled tab.
- CDP timeouts surface as timeouts and are not re-sent after the cleanup detach.
- Activate/deactivate chrome_find and chrome_inspect with the rest of the chrome_* tools.
- New unit suites: bridge-resilience, cdp-passthrough, type-evidence.
Tianren Dong tianrendong.dtr@gmail.com
Sat, 26 Sep 2026 16:37:25 -0400
238f347a
Report hidden-tab input, append targeted typing, smarter uploads, prune screenshots; release 0.15.54

- Check page visibility before trusted input: Chrome drops input to hidden pages (inactive tabs,
  covered/minimized windows, macOS window tabs behind full screen). Type/key/hover/scroll/tap/drag
  fail fast with the reason; click/fill use their DOM fallback immediately (or reject with
  domFallback:false).
- Use an inactive tab instead of a new window for the automation target while the user's window is
  full screen.
- chrome_type with uid/selector moves the caret to the end (moveToEndOfDocument) after its focus click.
- chrome_upload_file: labels/wrappers resolve to their file input, same-origin iframe search, and
  intercepted file chooser for upload buttons (trusted click when visible, user-gesture click when
  hidden); reject multiple paths for single-file inputs; no duplicate change event.
- Prune default-folder chrome_screenshot captures (7 days, newest 20 kept, retentionDays override).
- input.debug without a target no longer calls tabs.get(-1).
- New hidden-input unit suite.
Tianren Dong tianrendong.dtr@gmail.com
Sat, 26 Sep 2026 19:07:56 -0400
f034b851
Stop console capture from tripping DevTools detection; scope early capture to Pi tabs

Fixes #9. Sites like BOSS Zhipin log an object with a getter/toString trap and
close the tab if it is read. Store captured console args raw and serialize them
on read without running page code (data properties only, accessors as [getter],
built-ins via native getters). Inject document-start capture only into Pi's
automation tab and session-tracked tabs.
Tianren Dong tianrendong.dtr@gmail.com
Sat, 26 Sep 2026 22:41:47 -0400
c5c048b3
Make Chrome primer and authorization log prompt-cache friendly

- Contribute the primer as a structured prompt section instead of forcing systemPrompt.
- Log pi-chrome-tool-change as a rendered custom entry that is not sent to the model.
Tianren Dong tianrendong.dtr@gmail.com
Sat, 26 Sep 2026 22:20:21 -0400
b33d1855
Create session tab groups in the tab's own window

chrome.tabs.group without createProperties.windowId creates the group in the
current (user's) window and moves the tab there, so a session's automation tab
left its dedicated window and became a hidden inactive tab. Make the unit mock
match Chrome's behavior.
Tianren Dong tianrendong.dtr@gmail.com
Mon, 28 Sep 2026 19:45:18 -0400
9e4514f0
Release 0.15.56
Tianren Dong tianrendong.dtr@gmail.com
Mon, 28 Sep 2026 19:38:27 -0400
e6c01048
Fill whole textareas; cover new tabs' first page with early capture

chrome_fill's triple-click selected one line of a multi-line textarea, so fill
replaced only that line. Select value fields with the selectAll editing command
before deleting. Early capture now waits for in-flight tab.new creations, so a
first page that commits before the tab is tracked is still captured.
Tianren Dong tianrendong.dtr@gmail.com
Mon, 28 Sep 2026 19:30:23 -0400