Terminal I/O Contract
This document defines Rezi's terminal input contract.
This document defines Rezi's terminal input contract.
Contract path:
- Raw bytes arrive from a real PTY/ConPTY session.
- Zireael normalizes bytes into ZREV batches.
- Rezi parses those batches with
parseEventBatchV1. - App/runtime routing consumes parsed events.
All behavior in this document is covered by deterministic integration tests in:
packages/node/src/__e2e__/terminal_io_contract.e2e.test.tspackages/node/src/__e2e__/fixtures/terminal-io-contract-target.ts
Constants
- Key codes follow
@rezi-ui/core/keybindings/include/zr/zr_event.h. - Modifier bitmask:
SHIFT=1CTRL=2ALT=4META=8
Keyboard Contract
Byte-to-event mapping
| Input bytes | Expected parsed event(s) |
|---|---|
ESC [ 1 ; 5 A | { kind: "key", key: ZR_KEY_UP, mods: ZR_MOD_CTRL, action: "down" } |
ESC [ Z | { kind: "key", key: ZR_KEY_TAB, mods: ZR_MOD_SHIFT, action: "down" } |
ESC [ 9 ; 5 u | { kind: "key", key: ZR_KEY_TAB, mods: ZR_MOD_CTRL, action: "down" } |
ESC [ 13 ; 5 u | { kind: "key", key: ZR_KEY_ENTER, mods: ZR_MOD_CTRL, action: "down" } |
ESC [ 127 ; 5 u | { kind: "key", key: ZR_KEY_BACKSPACE, mods: ZR_MOD_CTRL, action: "down" } |
ESC [ 97 ; 3 u | Alt/Meta text policy fallback: ESC key prefix then payload ('a' text or equivalent Alt key payload event) |
ESC [ 98 ; 9 u | Alt/Meta text policy fallback: ESC key prefix then payload ('b' text or equivalent Meta key payload event) |
ESC ambiguity/incomplete policy
- Incomplete supported escape prefixes are buffered.
- If a sequence is completed in a later read, it resolves as the completed key event.
- If a supported prefix remains incomplete at flush, fallback is deterministic:
- emit
ESCkey - emit remaining bytes as text scalars (example:
ESC [->ESCkey then'['text)
- emit
Bracketed Paste Contract
Framing
- Begin marker:
ESC [ 200 ~ - End marker:
ESC [ 201 ~ - Payload between markers produces one
pasteevent:{ kind:"paste", bytes:<exact payload bytes> }
Missing end marker
- Missing end marker must not wedge input.
- Engine may finalize and emit a best-effort
pasteevent with captured bytes, or drop the incomplete paste. - Subsequent key/text input must continue normally.
Max paste size behavior
- Paste capture is bounded by engine paste buffer capacity.
- On overrun, the oversized paste is dropped (no truncated
pasteevent is emitted). - Input stream continues after paste end or idle flush.
Focus Contract
Focus in/out
| Input bytes | Expected parsed event |
|---|---|
ESC [ I | { kind:"key", key:30 /*FOCUS_IN*/, mods:0, action:"down" } |
ESC [ O | { kind:"key", key:31 /*FOCUS_OUT*/, mods:0, action:"down" } |
Gating
- Focus events are emitted when terminal capabilities report focus support.
- Rezi integration coverage asserts capability-gated suppression (
ZIREAEL_CAP_FOCUS_EVENTS=0). - Native runtime config gating (
enableFocusEvents) is implemented by Zireael platform config.
Mouse Contract (SGR)
| Input bytes | Expected parsed event |
|---|---|
ESC [ < 0 ; 300 ; 400 M | { kind:"mouse", mouseKind:3 /*down*/, x:299, y:399, buttons:1 } |
ESC [ < 0 ; 300 ; 400 m | { kind:"mouse", mouseKind:4 /*up*/, x:299, y:399, buttons:1 } |
ESC [ < 64 ; 400 ; 500 M | { kind:"mouse", mouseKind:5 /*wheel*/, x:399, y:499, wheelY:1 } |
Notes:
- SGR coordinates are 1-based on wire and normalized to 0-based in events.
- High coordinates must remain stable (no 223-column legacy clipping).
Resize Contract
- Engine emits an initial resize event at startup.
- Subsequent terminal size changes emit
resizeevents with latest cols/rows. - Ordering expectation:
- initial resize appears before later explicit resize updates
- observed size values match PTY resize requests
Split Reads / Partial Sequences
Examples (explicitly tested):
- Split complete sequence across reads:
- read #1:
ESC [ - read #2:
A - expected output: one
UPkey event, no prematureESCfallback before completion.
- read #1:
- Split incomplete sequence flushed without completion:
- read #1:
ESC [ - no completion bytes
- expected output after flush:
ESCkey event, then'['text event.
- read #1:
- Split paste begin/content without end marker:
- read #1:
ESC [ 200 ~ xyz - no end marker
- expected output: input remains live (and may include a best-effort paste flush).
- read #1:
Platform Coverage
- Linux/macOS: full contract suite runs through real PTY.
- Windows: ConPTY-guarded test covers at least arrows/modifiers + bracketed paste.
- If ConPTY path is unavailable in environment, tests skip with explicit reason.
Startup input safety note (emoji width probe)
- By default, Rezi does not perform startup CPR probing for emoji width.
- CPR probing is opt-in (
ZRUI_EMOJI_WIDTH_PROBE=1) because probing temporarily reads stdin bytes and can race startup-time input streams. - The default contract therefore keeps startup input handling deterministic and unaffected by width probing.
Safety rules
Rezi treats all binary buffers as untrusted input. Whether a buffer was just built by the TypeScript core or received from the C engine, it is validated before any data is read from it. This page d...
Packages
Rezi is organized as a monorepo with focused packages and clear runtime boundaries.