fix(extension): preserve key identity and prevent VK collisions on printable keydown

PE
21h ago
+204-34

why

Closes #3236

Calling page.keyPress("w") inserts text into focused inputs, but the dispatched DOM keydown event lacks key identity (event.key === "" and event.code === "" and event.keyCode === 0). Handlers inspecting event.key or event.code (e.g. shortcut managers and games) ignore the press, even though the subsequent keyup event identifies the key.

Furthermore, punctuation characters in describePrintableKey fell back to vk: ch.toUpperCase().charCodeAt(0). In ASCII, . is charCode 46, which collides with Windows Virtual Key VK_DELETE (46). In Chromium, passing windowsVirtualKeyCode: 46 for . causes the browser's editing command handler to trigger Delete instead of inserting the period.

what changed

  • Defined a complete standard US keyboard PRINTABLE_PUNCTUATION lookup covering printable punctuation and shifted symbol keys (e.g. . maps to code: "Period", vk: 190 rather than VK 46 / VK_DELETE).
  • Updated Page.describePrintableKey():
    • Resolves punctuation through PRINTABLE_PUNCTUATION.
    • Preserves uppercase key identity when an uppercase character is passed directly (ch === upper).
    • Avoids assigning synthetic charCode virtual key codes in the fallback to prevent command collisions.
  • Updated Page.keyDown() for single printable characters (length 1) in the typing path to include key: desc.key, code: desc.code, and windowsVirtualKeyCode: desc.vk.
  • Refactored Page.type() to share describePrintableKey() for consistent keyboard event metadata across type() and keyPress().
  • Added unit tests in packages/extension/tests/page-key-press.test.ts.
  • Added a patch changeset for @browserbasehq/stagehand-extension, @browserbasehq/stagehand, @browserbasehq/stagehand-go, and @browserbasehq/stagehand-python.

test plan

  • Run format check: pnpm run fmt:check (passes)
  • Run linter: pnpm run lint (passes)
  • Run typecheck: pnpm --filter @browserbasehq/stagehand-extension run typecheck (passes)
  • Run unit tests: pnpm vitest run packages/extension/tests/page-key-press.test.ts (passes, 4/4 tests)
  • Run full extension test suite: pnpm --filter @browserbasehq/stagehand-extension run test:unit (passes, 67/67 files, 1007 tests)

Summary by cubic

Fixes page.keyPress() and page.type() dispatching keydown events without key identity (event.key, event.code, and event.keyCode were empty), which caused handlers checking those fields (shortcut managers, games) to ignore the press. Also fixes punctuation collisions where . fell back to VK 46 (VK_DELETE) and triggered Delete in Chromium instead of inserting a period.

  • Added a full US keyboard PRINTABLE_PUNCTUATION mapping so characters like . dispatch code: "Period", vk: 190 rather than VK 46.
  • Unified describePrintableKey() across keyPress(), keyDown(), and type() so all typing paths share consistent metadata; uppercase characters passed directly keep their uppercase identity.
  • The fallback for unknown characters no longer synthesizes a VK code from charCode, preventing command collisions.
  • Added unit tests and a patch changeset for all @browserbasehq/stagehand-* packages.