As libtmux has evolved with the 0.57.0 "Neo" ORM parity, we've solved the N+1 problem for object hydration. However, synchronizing with terminal output still largely relies on client-side loop polling via capture_pane().
We propose introducing a two-tier waiter system modeled after the patterns battle-tested in libtmux-mcp. This brings "Wait, Don't Poll" semantics natively to libtmux.
1. Best-of-Breed: Deterministic Channel Sync (Server.wait_for_channel)
For commands where the user controls the execution, we should avoid scraping scrollback entirely and rely on tmux's native OS-level IPC blocks.
- The Concept: Bracket shell commands with
tmux wait-for -S <channel> and block the libtmux client until the signal fires.
- libtmux-mcp Prior Art: See
wait_for_tools.py which implements wait_for_channel via subprocess.run(timeout=timeout).
- tmux Internals: This leverages tmux's
cmd-wait-for.c (tmux/tmux@18ddda4), allowing the Python thread to sleep completely until tmux wakes it.
- Proposed API:
pane.send_keys("pytest; tmux wait-for -S tests_done")
server.wait_for_channel("tests_done", timeout=60.0)
2. Intelligent Fallback: Delta Polling (Pane.wait_for_text)
When observing third-party output (where we can't inject a signal), we must poll. However, naive capture_pane loops often match stale screen paint. We need Absolute Grid Anchoring.
- The Concept: At entry, snapshot the grid's absolute baseline (
history_size + cursor_y). On each tick, capture only the rows below this absolute anchor to ensure we strictly match new text.
- libtmux-mcp Prior Art: See
pane_tools/wait.py for the anchor math and scrollback limit protections.
- tmux Internals: This approach is grounded in how tmux defines the grid (see
format_cb_history_bytes and format_cb_history_size in format.c at 3.2a). It ensures compatibility with grid_collect_history and clear-history shifts.
- Proposed API:
# Under the hood, this will use Neo-style batch hydration
# to fetch `#{history_size}|#{cursor_y}` in a single IPC turn.
pane.wait_for_text("READY", timeout=8.0)
3. Future Scope: Control Mode Listener
A long-term architectural goal could involve spawning a background tmux -C client (as currently used in libtmux testing via ControlMode) to listen for %output or %pane-mode-changed streams, offering a completely event-driven API.
As
libtmuxhas evolved with the 0.57.0 "Neo" ORM parity, we've solved the N+1 problem for object hydration. However, synchronizing with terminal output still largely relies on client-side loop polling viacapture_pane().We propose introducing a two-tier waiter system modeled after the patterns battle-tested in
libtmux-mcp. This brings "Wait, Don't Poll" semantics natively tolibtmux.1. Best-of-Breed: Deterministic Channel Sync (
Server.wait_for_channel)For commands where the user controls the execution, we should avoid scraping scrollback entirely and rely on tmux's native OS-level IPC blocks.
tmux wait-for -S <channel>and block thelibtmuxclient until the signal fires.wait_for_tools.pywhich implementswait_for_channelviasubprocess.run(timeout=timeout).cmd-wait-for.c(tmux/tmux@18ddda4), allowing the Python thread to sleep completely until tmux wakes it.2. Intelligent Fallback: Delta Polling (
Pane.wait_for_text)When observing third-party output (where we can't inject a signal), we must poll. However, naive
capture_paneloops often match stale screen paint. We need Absolute Grid Anchoring.history_size + cursor_y). On each tick, capture only the rows below this absolute anchor to ensure we strictly match new text.pane_tools/wait.pyfor the anchor math and scrollback limit protections.format_cb_history_bytesandformat_cb_history_sizeinformat.cat 3.2a). It ensures compatibility withgrid_collect_historyandclear-historyshifts.3. Future Scope: Control Mode Listener
A long-term architectural goal could involve spawning a background
tmux -Cclient (as currently used inlibtmuxtesting viaControlMode) to listen for%outputor%pane-mode-changedstreams, offering a completely event-driven API.