Skip to content

Keep a Bot's browser closed until its reset has finished, so a reset always signs it out - #554

Merged
davidmckayv merged 2 commits into
CopilotKit:mainfrom
zopeVaibhav:fix/reset-racing-action
Sep 15, 2026
Merged

davidmckayv merged 2 commits into
CopilotKit:mainfrom
zopeVaibhav:fix/reset-racing-action

Conversation

@zopeVaibhav

Copy link
Copy Markdown
Contributor

Fixes #553

What this changes

A reset closes a Bot's browser and then deletes its profile, and for the two seconds the close takes, the browser was already out of live. A Bot action in that window launched a new browser from the profile about to be deleted and kept its logins.

createProfiles now tracks closes in flight in a closing map. evict holds an entry for the whole close, reset holds one across the close and the delete, and page() waits for that entry before it looks for a live browser or a launch. The action is answered a couple of seconds later, on an empty profile.

page() waits only when an entry exists. An unconditional await put a tick between the call and the launch registering in starting, and the two existing tests for a stop or a reset landing mid-launch failed on it.

The same wait covers a close from the cap, the idle sweep and a stop, which could previously start a second Chromium on a profile whose first one was still exiting. A request for that Bot now waits up to about two seconds instead.

Left alone: the supervisor's reset, which removes the container and was not affected.

Where it runs

  • New state that outlives a request? A map of closes in flight, keyed by Bot and cleared as each close finishes. It sits beside live and starting and is per process for the same reason they are: the browsers and their profile directories belong to this process.
  • What happens on the second replica? Server replicas reach the same computer process, which does the waiting.
  • Anything serialised? A Bot's launch waits for that Bot's close to finish, by awaiting the entry.
  • Anything fanned out to a browser? No.
  • New listener, port, or schedule? No.

Boundary and audit

No change to the gateway or the rows it writes. The computer.reset row is now true when a Bot acts during a reset.

Changelog

A line under Unreleased.

Proof

  • New test in agent-computer/tests/browser-close-announcement.test.ts: it sets a persistent cookie, reopens the browser to confirm the cookie is on disk, starts a reset, asks for the page 100ms later and reads its cookies at once, then checks them after the reset. Run in the agent-computer image with OPENBOT_LIVE_SCREEN=1, it fails against main (a96d88c), receiving the cookie, and passes on this branch. A first version that read cookies only after the reset passed on main, because Chromium loads cookies on first use; reading them at once is what an action does.
  • The whole agent-computer suite in the same image: 316 pass and 4 fail on main, 317 pass and 4 fail on this branch. The four failures are the same tests on both sides, so none comes from this change.
  • Two throwaway computers from that image, one on main's code and one on this branch, each with a Bot signed in to a local page and a reset raced by a navigate. On main, navigates at 1.0s and 1.8s left the Bot SIGNED-IN. On this branch each navigate was answered after about 2.2s and the Bot ended SIGNED-OUT, and a control reset signed out.
  • tsc --noEmit in agent-computer: clean. Biome reports nothing new; its import-order note on profiles.ts:36 is on main too, and this change does not touch the imports.
  • The Reset button clicked in the app, with the server on one shared computer, first on main's code and then on this branch, while Bot actions went through the server every 250ms for 12 seconds. On main all 48 actions and a check afterwards were SIGNED-IN, and the audit trail recorded computer.reset with "the computer and its saved state were deleted". On this branch, the eight actions sent while the browser was closing waited about two seconds for the reset, every later action was SIGNED-OUT, and so was the check afterwards. Those eight came back net::ERR_ABORTED, because they were released together and navigated one page at once; nine simultaneous navigations with no reset give the same errors.
  • The Helm chart rendered with ci/gke-values.yaml: the API server at 2 replicas, all pointed at one shared computer StatefulSet with replicas: 1. Not installed on a cluster.

@zopeVaibhav
zopeVaibhav force-pushed the fix/reset-racing-action branch from 5b3d8bc to b1789ea Compare September 15, 2026 16:27

@davidmckayv davidmckayv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code-verified clean; CI green on this sha.

@davidmckayv
davidmckayv merged commit 69b16ca into CopilotKit:main Sep 15, 2026
15 checks passed
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.

A reset that overlaps a Bot action leaves the Bot's browser signed in

2 participants