Skip to content

Make Enter act while a person is driving the browser - #477

Open
kevin9327 wants to merge 1 commit into
CopilotKit:mainfrom
kevin9327:fix/enter-while-driving
Open

Make Enter act while a person is driving the browser#477
kevin9327 wants to merge 1 commit into
CopilotKit:mainfrom
kevin9327:fix/enter-while-driving

Conversation

@kevin9327

Copy link
Copy Markdown
Contributor

A Bot hits a sign-in it cannot do itself and asks for help. A person takes the wheel, types their
email and password into the page — the characters appear — and presses Enter. Nothing happens. The
form does not submit. Nothing is refused, nothing is said, and the only way on is to find the button
and click it.

The same Enter does nothing in a text box (no new line) and nothing on a button somebody has tabbed
to. Every other key works, which is what makes it hard to believe: the typing lands, so the wheel
must be working.

Why

Input.dispatchKeyEvent has two kinds of key-down. rawKeyDown is a key that produces no
character: Chrome delivers the event to the page and stops there, so listeners fire and nothing else
happens. keyDown carries text, and that is what makes Chrome perform the key's own default action.

screencast.ts picks between them by whether the surface sent text, and the surface sends text only
for a printable character:

// live-screen.tsx
...(event.key.length === 1 ? { text: event.key } : {}),

// screencast.ts
type: message.event === "up" ? "keyUp" : message.text ? "keyDown" : "rawKeyDown",

"Enter".length is 5. So Enter went as a rawKeyDown, forever.

Measured, not reasoned

Driven through the shipped startScreencast against Chromium 151 (playwright-core 1.62.1's own
build), with the message live-screen.tsx actually constructs for a keydown — text present only
when key.length === 1:

=== as sent today: Enter with no text ===
  Enter submits the form        : false
  single-line input holds       : "h"
  textarea after a, Enter, b    : "ab"
  Enter activates a focused btn : false

=== with text: '\r' on Enter ===
  Enter submits the form        : true
  single-line input holds       : "h"
  textarea after a, Enter, b    : "a\nb"
  Enter activates a focused btn : true

The page's own keydown listener saw Enter/13 in both runs. It was heard and not acted on.

One key, and that was measured too

Every other editing key already does what it means as a rawKeyDown, so filling text in for them
would be inventing an insertion. Same harness, same browser, caret and value read back after each
press in a textarea holding hello world:

Home        -> [0,0,"hello world"]
End         -> [11,11,"hello world"]
ArrowRight  -> [1,1,"hello world"]
ArrowUp     -> [0,0,"hello world"]
Delete      -> [0,0,"ello world"]
Backspace   -> [1,1,"elo world"]
Tab         -> [0,0,"hello world"]

Identical before and after the change.

The fix

A carriage return for Enter when the surface sent no text, in screencast.ts rather than in the
surface, because this is a fact about the protocol and the file already holds the neighbouring one —
its VIRTUAL_KEY_CODES comment names Enter for the same reason.

\r rather than \n: it is what a keyboard's Enter carries, and Chrome turns it into whatever the
field it lands in needs. The measurement above shows the single-line input left holding exactly what
was typed and no extra character.

Filled in only on the way down, where a default action happens. A key going up carries what it was
given and nothing more.

Tests

New: agent-computer/tests/screencast.test.ts, 5 tests. screencast.ts imports Playwright for its
types only and a type import is erased at run time, so these reach the translation with a fake CDP
session and no browser — they run anywhere the suite does.

  • Against origin/main (1c7bd92) with only the new file applied: 4 pass, 1 fail.
expect(params.type).toBe("keyDown");
error: expect(received).toBe(expected)
Expected: "keyDown"
Received: "rawKeyDown"
(fail) a key a person presses while they hold the wheel > Enter is a press the page acts on, not one it merely hears
  • With the change: 5 pass, 0 fail.

The other four are the guard against over-correcting and pass before and after: a printable
character keeps its own text, Backspace / Delete / Tab / ArrowLeft / Home stay rawKeyDown with no
text at all, Enter going up is a keyUp carrying nothing, and the virtual key code the surface
offered is still the one sent.

bun test agent-computer/tests236 pass, 15 skip, 20 fail. The same 20 fail on origin/main
(231 pass there): 11 in shell.test.ts, which spawns /bin/bash, and 9 in workspace.test.ts,
which creates symlinks — neither is available on the Windows machine these were run on, and neither
is touched here.

Also run, clean: bunx biome check on both files, bun run typecheck in agent-computer.

Note on the changelog

A person driving a browser behaves differently afterwards, so there is an entry. It goes at the top
of ## Unreleased, the line every open PR touching the changelog inserts at, so it will conflict
with any other that lands first. Happy to rebase.

`Input.dispatchKeyEvent` has two kinds of key-down. `rawKeyDown` is a key that
produces no character: Chrome delivers it to the page and stops there, so a
listener fires and nothing else happens. `keyDown` carries text, and that is what
makes Chrome perform the key's own default action.

Text was sent only for a printable character -- `event.key.length === 1` on the
surface -- so Enter arrived as a `rawKeyDown`. Measured against Chromium 151
through this file, with the message the live screen actually builds: the form did
not submit, a textarea took no new line, and a focused button was not pressed,
while the page's own keydown listener saw every one of them. That is the sign-in
at the end of almost every takeover.

Enter now carries "\r" when the surface sent no text. One key rather than a list,
measured the same way: Backspace, Delete, Tab, Home, End and the arrows all do
what they mean as a rawKeyDown, and a single-line input still holds exactly what
was typed. The fill-in is only on the way down, where a default action happens.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

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