fix(desktop): send layout-aware key frame instead of raw Windows VK #92

Merged
Claude-Run merged 1 commit from fix/VAPP-78-desktop-layout-aware-key-frame into main 2026-07-11 02:49:25 +02:00
Member

The Desktop client forwarded each keypress as event.keyCode & 0xff, a layout-dependent Windows OEM virtual-key byte with no modifier state. From a macOS client this broke the number row (with and without Shift), -/_, =/+, and other punctuation, because those Windows VK codes are defined as layout-dependent and the agent interpreted them as US-QWERTY. The & 0xff mask also silently dropped any keyCode >= 256.

Replace that contract with the layout-aware frame pinned by the agent side (VA-126, implemented in vervain-agent). Each keydown/keyup now sends cmd 200 [u16 200][u16 size][action][mods][layout_id][code_len][key_len][code][key]: the physical key (event.code), the produced character (event.key, already layout-applied by the browser), and an explicit shift/ctrl/alt/meta bitmask. Modifier state is conveyed only through the bitmask, never re-derived from separate VK-16 Shift events. On tunnel rendezvous the client also sends cmd 201 [u16 201][u16 6][u16 version=2] so a stale agent fails loudly rather than dropping keys. The old cmd 1 raw-VK frame and its truncation are removed; Vervain is not in production and owns this protocol, so only the new frames are sent.

The frame encoders (key_mods, client_hello_frame, key_layout_frame) are factored into the host-testable parent module and covered by unit tests, including the previously-broken cases (Shift+Digit2 -> @, Minus/Equal, unshifted digits).

#VAPP-78

The Desktop client forwarded each keypress as `event.keyCode & 0xff`, a layout-dependent Windows OEM virtual-key byte with no modifier state. From a macOS client this broke the number row (with and without Shift), `-`/`_`, `=`/`+`, and other punctuation, because those Windows VK codes are defined as layout-dependent and the agent interpreted them as US-QWERTY. The `& 0xff` mask also silently dropped any keyCode >= 256. Replace that contract with the layout-aware frame pinned by the agent side (VA-126, implemented in vervain-agent). Each keydown/keyup now sends cmd 200 `[u16 200][u16 size][action][mods][layout_id][code_len][key_len][code][key]`: the physical key (`event.code`), the produced character (`event.key`, already layout-applied by the browser), and an explicit shift/ctrl/alt/meta bitmask. Modifier state is conveyed only through the bitmask, never re-derived from separate VK-16 Shift events. On tunnel rendezvous the client also sends cmd 201 `[u16 201][u16 6][u16 version=2]` so a stale agent fails loudly rather than dropping keys. The old cmd 1 raw-VK frame and its truncation are removed; Vervain is not in production and owns this protocol, so only the new frames are sent. The frame encoders (`key_mods`, `client_hello_frame`, `key_layout_frame`) are factored into the host-testable parent module and covered by unit tests, including the previously-broken cases (Shift+Digit2 -> `@`, `Minus`/`Equal`, unshifted digits). #VAPP-78
fix(desktop): send layout-aware key frame instead of raw Windows VK
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 33s
Create release / Create release from merged PR (pull_request) Has been skipped
eb8c93a4ba
The Desktop client forwarded each keypress as `event.keyCode & 0xff`, a layout-dependent Windows OEM virtual-key byte with no modifier state. From a macOS client this broke the number row (with and without Shift), `-`/`_`, `=`/`+`, and other punctuation, because those Windows VK codes are defined as layout-dependent and the agent interpreted them as US-QWERTY. The `& 0xff` mask also silently dropped any keyCode >= 256.

Replace that contract with the layout-aware frame pinned by the agent side (VA-126, implemented in vervain-agent). Each keydown/keyup now sends cmd 200 `[u16 200][u16 size][action][mods][layout_id][code_len][key_len][code][key]`: the physical key (`event.code`), the produced character (`event.key`, already layout-applied by the browser), and an explicit shift/ctrl/alt/meta bitmask. Modifier state is conveyed only through the bitmask, never re-derived from separate VK-16 Shift events. On tunnel rendezvous the client also sends cmd 201 `[u16 201][u16 6][u16 version=2]` so a stale agent fails loudly rather than dropping keys. The old cmd 1 raw-VK frame and its truncation are removed; Vervain is not in production and owns this protocol, so only the new frames are sent.

The frame encoders (`key_mods`, `client_hello_frame`, `key_layout_frame`) are factored into the host-testable parent module and covered by unit tests, including the previously-broken cases (Shift+Digit2 -> `@`, `Minus`/`Equal`, unshifted digits).

#VAPP-78
Claude-Run deleted branch fix/VAPP-78-desktop-layout-aware-key-frame 2026-07-11 02:49:25 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/vervain-apps!92
No description provided.