gpty-core
Library crate for the gpty multi-PTY emulator. This is the engine — all terminal lifecycle, ANSI parsing, concept matching, capture buffering, and grid management live here.
Module Layout
| Module | Purpose | Key Types |
|---|---|---|
types |
Data vocabulary shared across all modules | Concept, Action, TerminalConfig, CaptureMode, CapturedOutput, PaneType |
concept |
Pure functions for trigger matching and capture routing | match_line() |
agent_state |
Tiered, display-only agent-state detection (events / OSC / heuristics) | AgentState, StateTier, AgentStateTracker |
engine |
Runtime orchestrator; spawns terminal tasks, capture state machine | WorkspaceEngine, PtyTerminalHandle, SpawnedTerminal, TaskContext |
pty |
Cross-platform PTY lifecycle via portable-pty |
PtyHandle |
parser |
Strips ANSI escape sequences; extracts plain-text lines | LineParser |
term |
Full terminal grid + damage tracking via alacritty_terminal |
TermGrid, CellInfo, GridUpdate |
color |
ANSI color mapping — named, indexed, true-color → RGB | color_to_rgb() |
keymap |
Keyboard event → byte sequence translation | key_event_to_bytes() |
history |
SQLite-backed scrollback history store | HistoryStore |
Concept System
Concepts capture terminal output and route it to a pane: a regular expression trigger, a stop condition, and a target pane kind.
Concept {
name: "cat_command",
trigger_regex: Regex::new(r"(?:^|[$#>]\s)\bcat\s+\S").unwrap(),
enabled: true,
capture_mode: CaptureMode::UntilStop { stop_timeout_ms: 300, stop_on_input: true },
destinations: vec![Action { target_label: "code_viewer".into() }],
}How it works:
- PTY output bytes stream through the
vteparser - The parser strips ANSI escape sequences and extracts visible text lines
- Each line is tested against every registered concept’s
trigger_regex - On an
UntilStopmatch the terminal enters capture mode and buffers raw bytes; aSingleLinematch is queued as a notice and stops here - The capture ends on timeout (silence for N ms) or user input
- GDScript drains the completed capture and routes its text to the first pane
whose type matches the action’s
target_label— with no receiver, the raw bytes are replayed into the grid
Concepts never execute anything. A concept definition is data: it starts a
capture and chooses which pane kind displays the result. It cannot write to a
PTY. That is deliberate — the trigger is a regex over terminal output, which is
untrusted, so a concept that could act would be an execution primitive driven by
whatever a program happens to print. capture_mode is UntilStop (capture and route) or SingleLine
(notify-only: the match is published on the event socket and nothing is
captured, routed, or shown); a missing or unknown value captures, and a legacy
cmd key in an action is ignored.
Key functions: match_line(), finalize_capture(), handle_command(), feed_grid(), store_line().
Use Cases
- Read the code you just catted:
cat README.mdin a shell pane opens the file in a code viewer pane. - Error triage: route a compiler error block, a traceback, or a failing test log to the Inspector — gated behind its own opt-in (
accept_concept_captures), because captured output leaves the terminal. - Diff and log review:
git diff/git logoutput lands in a viewer pane instead of eating scrollback. - Documentation lookup: match a compiler error code and open the relevant snippet or link in an adjacent pane.
Why Flat?
All modules are single files in src/. When a module grows beyond ~200 lines or needs helper files, it will be promoted to src/<name>/mod.rs. This keeps navigation simple during prototyping while leaving room for future nesting.
Key Dependencies
| Crate | Version | Role |
|---|---|---|
portable-pty |
0.9 | Cross-platform PTY (Linux /dev/ptmx, Windows ConPTY) |
vte |
0.15 | ANSI/VT100 escape sequence parser |
alacritty_terminal |
0.26 | Full terminal grid emulator |
tokio |
1.52 | Async runtime + broadcast channel |
regex |
1.12 | Concept trigger patterns |
rusqlite |
0.31 | SQLite history storage |
serde |
1 | Serialization framework (derive support) |
serde_json |
1 | JSON encoding for IPC + schema output |