How ada sends a message from your own browser

Driving the Chrome you are already signed into — no second profile, no logging in twice.

The problem — three routes into your real profile, all closed

--remote-debugging-port

Chrome 136+ refuses to open the port when the profile directory is the one Chrome itself is using. The port silently never opens.

--remote-debugging-pipe

Same wall, stated out loud: “DevTools remote debugging requires a non-default data directory.”

copy the profile

Cookies are app-bound (v20). Chrome discards every one on first launch against a copy — measured here: 3879 cookies → 0.

The answer — get inside the browser instead of knocking on it

ada
browser.ts
verbs: read, click, type…
node
──▶commands◀──results
ada bridge
MV3 extension
holds one open connection
in chrome
──▶inject
the page
your logged-in
Instagram tab
your session

The key idea: the extension dials out to ada, so nothing on your machine listens for inbound connections — and because it lives inside Chrome, it inherits every login you already have.

Reading the page: two ways, one of them cheap

chrome.debugger (old)

  • attaches a debugger — Chrome shows “ada bridge started debugging this browser”
  • a protocol round trip per query
  • clicks land on coordinates, so an overlay silently eats them

content script (now)

  • shares the page's DOM directly — no banner
  • one message returns the whole snapshot
  • clicks the element, so overlays are irrelevant

What a page looks like to ada

https://www.instagram.com/direct/inbox/ button "vikash You: ada test · 1h" [ref_18] @72,298 button "Valo 2.0 Ashwani sent an attachment. · 19h" [ref_19] @72,370 link "Open the profile page of its__vikash__" [ref_28] @488,16 textbox [ref_40] @538,897

Every interactive element, named and numbered. Acting is then just click {ref: "ref_18"} — no DOM archaeology, no guessing at selectors.

Sending “hi” — the whole run, 9.7 seconds

1
open
the DM inbox, in your Chrome
2.5s
2
read
snapshot → the 1:1 row is ref_18 (the group “vikash ಮತ್ತು Abhay” is excluded)
0.2s
3
click ref_18
opens the thread
2.5s
!
read → gate
header must show exactly one profile, and it must be its__vikash__ — otherwise abort before typing
0.2s
4
type
“hi” into the composer ref_40
0.9s
5
click Send
the Send control only exists once there is text → ref_41
2.5s
verify
inbox row now reads “vikash — You: hi · 1m”
0.9s

Three bugs that cost the afternoon

A click that lands but does nothing

A coordinate click hit an overlay sitting on the row. The page received a perfectly isTrusted: true event and ignored it — no error, nothing to debug. Fix: check the target is topmost via elementFromPoint; if not, dispatch on the element itself.

Commands falling into the gap between polls

The extension long-polled. Between one poll being answered and the next going out, a command had nowhere to land — and if Chrome tore the service worker down in that window, it waited for the 30s alarm. Fix: one open stream + a 15s heartbeat. Worst latency now 7ms.

A stale service worker

Chrome's reload button kept serving an older background.js, so newly added ops timed out instead of erroring — which looked exactly like the bug that had just been fixed. Fix: remove and re-add the unpacked extension.