Skip to content

Fix bridge lifecycle and confirmed FSB issue batch - #143

Open
LakshmanTurlapati wants to merge 23 commits into
mainfrom
diagnose-random-bridge-death
Open

LakshmanTurlapati wants to merge 23 commits into
mainfrom
diagnose-random-bridge-death

Conversation

@LakshmanTurlapati

Copy link
Copy Markdown
Collaborator

Recovers stale bridge connections and records bounded lifecycle events, shutdown causes, and attachment diagnostics across hub, relay, doctor, status, and health.
Restricts local MCP HTTP to loopback clients, prevents duplicate or uncertain mutations, unifies tool routing, repairs text editing and selector handling, and bounds hung-page recovery.
Distinguishes locked and unconfigured vaults, improves X guidance and onboarding, and documents extension setup, pairing, versioning, and Chrome 137 behavior.
Adds browser fixtures and regression coverage; npm run ci, version synchronization, and registry parity checks pass.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c212aa4a60

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread extension/background.js
Comment on lines +19296 to +19299
const inserted = await dispatchCdpTextInsertion(tabId, text, position, selector);
if (!inserted.success) {
sendResponse(inserted);
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Detach the debugger before returning insertion failures

When dispatchCdpTextInsertion returns { success: false } (for example, replace_all on a canvas editor or a selector that does not resolve to one editable field), this early return bypasses both the normal detach and the catch cleanup after attachFsbDebugger has succeeded. The tab remains attached to chrome.debugger, so subsequent CDP operations on that tab fail as debugger contention until something else detaches it.

Useful? React with 👍 / 👎.

Comment thread extension/background.js
Comment on lines +5940 to +5944
flight = ensureContentScriptInjectedUnlocked(tabId, maxRetries);
contentScriptInjectionFlights.set(tabId, flight);
flight.finally(() => {
if (contentScriptInjectionFlights.get(tabId) === flight) contentScriptInjectionFlights.delete(tabId);
}).catch(() => {});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Clear timed-out content-script injection flights

If ensureContentScriptInjectedUnlocked stalls, the 12-second Promise.race rejects its callers but leaves the unresolved promise in contentScriptInjectionFlights; cleanup only runs when that promise eventually settles. Every later recovery attempt for the tab then reuses that same hung flight and times out again, so a page that needs reinjection cannot recover until the service worker is restarted.

Useful? React with 👍 / 👎.

queues.delete(tabId);
}
};
const watchdog = setTimeout(() => lease.release(), 20000);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid releasing live CDP leases

A network-capture session uses the default 30-second duration and intentionally holds its per-tab lease until endSession, but this watchdog releases that lease after 20 seconds. A queued screenshot or input operation can then acquire the lease while capture still owns the debugger and fails with debugger contention instead of waiting, causing spurious CDP failures during the final third of a normal capture session.

Useful? React with 👍 / 👎.

…r on refusal

A replace with no selector checked the top frame's focused element, which on
Google Docs is the text-event iframe, so the insertion was refused and never
dispatched. Focus inside a nested frame now defers selection to that frame.

A refused insertion also returned with the debugger still attached, leaving
the infobar up and every later attach on the tab failing as busy.
…andonment

Abandoning the hub skipped the extension close handler, so status and doctor
kept reporting the departed extension's id and window count.

A lost listener was journaled as hub_server_error, the same key as a
still-listening accept failure, so an EMFILE within the coalescing window
suppressed it. It is now its own hub_listener_lost event.
Node 20 has no global WebSocket, so fall back to the mcp package's ws
client. Wait for Chrome to exit before removing its profile; removing it
while Chrome still writes raced into ENOTEMPTY on Linux.
LakshmanTurlapati and others added 5 commits September 29, 2026 15:39
…xt is sent

A timed-out injection flight pinned the tab, and debugger errors before any input were treated as uncertain delivery.

Co-authored-by: Cursor <cursoragent@cursor.com>
Anything that rejects out of sendMessageWithRetry for executeAction is a
proven non-delivery, yet the bridge labelled it mayHaveExecuted, so the
agent was told to inspect before retrying a click that never reached the
page. The direct-send fallback now resolves a possibly delivered action as
uncertain, so a rejection always means the action was not sent.

Adds regressions for the hung injection flight, the per-attempt dispatch
flag, and CDP insertion failures before input.
Regenerate the llms and sitemap timestamps from the current build date.

Co-authored-by: Cursor <cursoragent@cursor.com>
Request paths were parsed against http://<host>:<port>, which is not a
valid URL base for ::1, so every request on an IPv6 bind answered 500.
Only the path is read and the Host header is validated beforehand, so
parse against a fixed base instead.
… nothing

The Docs formatted-paste path clears the document first, then treated
every unconfirmed paste as uncertain and skipped the plain insertion,
which could leave the document empty while telling the caller not to
retry. The paste helper now reports nothingInserted when the clipboard
write failed before the paste key was sent, or when measurable document
text stayed unchanged after a late-landing recheck. Only those cases
fall through; an unmeasurable canvas or a failed paste key stays
uncertain.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant