Skip to content

Let a Bot choose from a dropdown, and carry on after a browser step fails - #833

Merged
davidmckayv merged 6 commits into
mainfrom
david/browser-dropdowns-and-failed-actions
Oct 24, 2026
Merged

davidmckayv merged 6 commits into
mainfrom
david/browser-dropdowns-and-failed-actions

Conversation

@davidmckayv

Copy link
Copy Markdown
Contributor

Two bugs that stop a Bot partway through a form in its browser.

A Bot could not choose from a dropdown. The computer's /type used Playwright's fill on every field, and a native <select> does not take it: the call waited until it timed out, and the server reported the timeout as an element gone from the page. The Bot snapshotted again and repeated itself, so a form with an Industry or Stage dropdown could never be saved. Typing into a dropdown now chooses the option by its label (ignoring case and surrounding spaces), then by its value. Text that names no enabled option gets a 400 saying so, without echoing the text or the page's options. Snapshots also listed a select as a bare combobox with no options and no value, because its options carry no refs; the element now carries options (at most 50) and the selected option as its value. Custom listboxes are unchanged: their options already have refs.

A failed browser step ended the Bot's turn silently. Clicks, typed entries and key presses go through the approval gate, and when the gate answered with a failure, the wrapper in approval-tools.tsx threw. CopilotKit starts no follow-up run after a frontend tool throws, so the turn stopped with the step reading "Didn't work" and nothing more from the Bot. The wrapper now answers through the same failedComputerCall that callComputer already used for ungated steps: a failed result with the reason and the refusal, stale-ref or person-has-control flags. A gate that cannot be reached, or a 200 with no result, is a failed result too.

Found running a Bot against a CRM form with no API: the first attempt stopped at the Industry dropdown with no reply; with both fixes, the Bot saved five accounts and recovered from two failed steps on the way.

Tests. Each new test fails on main and passes here.

  • agent-computer/tests/enter-text.test.ts drives a real form through an AI snapshot and the aria-ref locator, as the computer does. It is added to the computer (real browser) job's list, the only job that sets OPENBOT_COMPUTER_BROWSER.
  • agent-computer/tests/aria-snapshot.test.ts pins Playwright's captured snapshot of two selects and a custom listbox.
  • app/tests/approval-tools.test.tsx drives the handler the SDK keeps against a gate answering 409, 403, no connection, and a 200 with no result.

No migration, and the server, computer and app can be upgraded in any order: an older server ignores the new snapshot field.

The computer's /type filled every field with Playwright's fill, which a native <select> does not take: it waited until the action timed out, and the server reported the timeout as an element gone from the page. The Bot took a fresh snapshot and tried the same thing again, so a form with a dropdown (a CRM's Industry or Stage) could never be completed. enterText now picks the option whose label is the text, ignoring case and surrounding spaces, and failing that the one whose value is, which fires the change a person's choice does. Text that names no enabled option is refused with a 400 that names neither the text nor the page's options.
A native <select>'s options carry no refs, so the snapshot dropped them, and its value is the list of options rather than text, so the combobox came through with no value either. A Bot saw combobox "Industry" with nothing it could choose and no way to tell what it was set to. The element now carries its options (at most 50) and, as its value, the one marked selected. A custom listbox's options keep their own refs and stay elements of their own, listed once.
… tests

Drives a real form through an AI snapshot and the aria-ref locator, as the computer does: a label and a value are chosen, an unknown or disabled option is refused without changing the field, a text box is still filled, and the snapshot lists the dropdown's options. The parser tests pin the captured snapshot of two selects and a custom listbox. The browser test joins the computer job's list, which is the only CI job that sets OPENBOT_COMPUTER_BROWSER.
A click, typed entry or key press goes through the approval gate, and when the gate answered with a failure the wrapper threw. CopilotKit starts no follow-up run after a frontend tool throws, so the Bot's turn ended on the spot: the step read "Didn't work" and the Bot never spoke again. The wrapper now answers the way callComputer already did for the steps that skip the gate, through the same failedComputerCall: a failed result with the reason, plus the refusal, stale-ref and person-has-control flags the model reads to choose its next step. A request that cannot reach the gate, or an answer with no result, is a failed result too.
Drives the handler the SDK keeps against a gate answering 409, 403, a request that cannot connect, and a 200 with no result. Each comes back as a failed result rather than a throw.
@davidmckayv
davidmckayv merged commit 46642d6 into main Oct 24, 2026
20 checks passed
@davidmckayv
davidmckayv deleted the david/browser-dropdowns-and-failed-actions branch October 24, 2026 00:07
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