Fix bridge lifecycle and confirmed FSB issue batch - #143
LakshmanTurlapati wants to merge 23 commits into
Conversation
…e-death # Conflicts: # package.json
There was a problem hiding this comment.
💡 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".
| const inserted = await dispatchCdpTextInsertion(tabId, text, position, selector); | ||
| if (!inserted.success) { | ||
| sendResponse(inserted); | ||
| return; |
There was a problem hiding this comment.
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 👍 / 👎.
| flight = ensureContentScriptInjectedUnlocked(tabId, maxRetries); | ||
| contentScriptInjectionFlights.set(tabId, flight); | ||
| flight.finally(() => { | ||
| if (contentScriptInjectionFlights.get(tabId) === flight) contentScriptInjectionFlights.delete(tabId); | ||
| }).catch(() => {}); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
…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.
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.