* fix: tell fish that herdr reflows its prompt on resize
refs #3672
* fix: match fish_handle_reflow casing exactly on windows
refs #3672
* test: keep temp path out of fish env spawn test command
refs #3672
* test: add input transparency conformance harness
* test: cover split host replies in input conformance harness
* fix: let libghostty encode every key sent to panes
* refactor: read live keyboard modes only from libghostty
* fix: wait for split input sequences instead of typing their fragments
* refactor: drop unused keyboard protocol argument from pane key encoding
* test: fix windows-only keyboard mode tests after libghostty mode refactor
* test: measure windows key record transparency against the keyboard layout
* fix: forward windows punctuation and altgr chords that produce no character
* test: expect libghostty kitty encoding in the windows conpty probe and cover release, alternate, text and modifyOtherKeys passthrough
* ci: lint windows-only test code in the cross-platform windows check
* refactor: delete herdr's own key encoder
* test: expect xterm modifyOtherKeys 2 encoding for shifted letters in the windows gauntlet
* refactor: read windows input through one native console reader
* refactor: parse sgr mouse reports once and map pixel reports without rewriting them
* refactor: decode framed input once instead of re-framing it in the client
* fix: keep macos alt, non-us kitty keys, text releases and reply-tail recovery correct after the libghostty switch
* fix: stop waiting for host replies the terminal never sends
* fix: deliver shifted punctuation releases after shift is let go
* fix: never type super, hyper or meta chords into panes that cannot read them
refs #4356
* test: replay reporter captures for fixed split-read and key-release issues
refs #4856
refs #4025
refs #4184
* fix: forward f1, f2 and f4 from kitty keyboard hosts
refs #4403
* fix: send pixel mouse reports to unfocused panes and only when the host supports them
refs #4750
* fix: treat 0x08 as backspace when the terminal's erase key is ctrl+h
refs #3244
* test: fix macos lint and keep the pixel mouse server test unix-only
* fix: deliver legacy alt+escape on macos instead of dropping it
* fix: address review findings on super+enter, held key releases and the pixel mouse probe
* test: expect super+enter to stay out of plain windows panes
* test: wait for the host pixel mouse mode instead of reading the channel once
* fix: send ctrl+shift+letter to plain shells as its control byte
* fix: encode each key under one keyboard mode snapshot
* fix: keep ime text intact and stop inventing key releases for committed text
* fix: release held key reports on focus loss and never drop held-key leases
* fix: send ctrl chords on non-latin layouts as their physical key
* fix: match non-latin ctrl chord releases after ctrl is let go
* fix: drop super chords before the windows conpty fallback in plain panes
* fix: keep agent sessions while the agent is suspended or backgrounded
refs #1647
refs #1033
* fix: drop a suspended agent's session when a new one of its kind takes over
refs #1647
* fix(windows): prefer pwsh for the default pane shell
Windows panes launched Windows PowerShell 5.1 (powershell.exe) whenever [terminal] default_shell was unset, ignoring PowerShell 7 even when pwsh.exe resolved on PATH. Resolve the unset default against PATH and prefer pwsh.exe, falling back to the inbox powershell.exe. An explicit default_shell still wins.
* fix(windows): validate pwsh before using it as the default shell
portable-pty resolves the configured shell with Path::exists and passes that path to CreateProcessW, which does not fall through to later PATH entries. Selecting an invalid pwsh.exe would therefore break new panes instead of falling back. Only prefer a pwsh.exe that starts with the PE MZ magic, and return its path so the validated binary is the one launched.
* fix(windows): validate the full PE header before preferring pwsh
Checking only the DOS MZ signature still accepted truncated images, DLLs, and foreign-architecture binaries. Because portable-pty passes the resolved path straight to CreateProcessW without trying later PATH entries, validate the PE signature, the COFF header (machine, executable-image bit, not a DLL, section count), and the optional header before preferring pwsh.exe.
* fix(windows): accept native ARM64 pwsh and validate the section table
Herdr ships an x64 Windows build that also runs on Windows ARM64 under x64 emulation, so cfg!(target_arch) cannot tell whether a native ARM64 pwsh.exe is launchable. Accept every executable machine type Windows supports instead of rejecting ARM64 on the x86_64 build. Also bound-check the section table (40 bytes per declared section) after the optional header so a truncated table falls back instead of being selected.
* fix(windows): detect the native host machine for pwsh compatibility
Machine compatibility was derived from cfg!(target_arch), which reports the emulated x64 process on Windows ARM64, and then briefly accepted ARM64 on every host. Read the native machine with IsWow64Process2 in the platform layer instead: ARM64 Windows accepts ARM64/x64/x86 images, x64 accepts x64/x86, and x86 accepts only x86.
Windows login-mode PowerShell panes are now launched directly, but the PowerShell prompt-based cwd integration was still gated to non-login mode, leaving those panes without repository-owned cwd reporting after Set-Location. Enable the existing integration for login-mode PowerShell and build the integration args on the shared direct-shell path.
Follow-up to #4060.
* fix(windows): honor default_shell in login shell mode
Login-mode panes used portable-pty's default-program builder, which resolves to %ComSpec% (cmd.exe) on Windows and ignores the SHELL env override, so [terminal].default_shell was silently ignored. Build the launch command from the configured shell instead and append the shell's own login flag for POSIX-style shells that support one.
refs #1445
* fix(windows): add csh and tcsh to login shell flags
refs #1445
Native Windows lint and check only clippy-checked the herdr binary, while macOS and Linux run clippy across all targets. Enable --all-targets on Windows and gate the Unix-only test helpers that this exposed so Windows check matches the Unix one.
* feat: keep the outer terminal window title in sync with the session
Herdr emulates the terminals in its panes, so an OSC 0/OSC 2 title
written inside a pane stops at Herdr and never reaches the terminal
Herdr itself runs in. That outer title is what window managers read for
title bars, tab bars, and group bars, so it kept showing whatever the
shell or ssh happened to leave behind.
Add `ui.window_title`, rendered from {hostname}, {workspace}, {tab},
{pane}, and {terminal_title}, and push it to the foreground client
whenever it changes. It renders on the server, so {hostname} names the
machine the panes actually run on rather than the machine a thin remote
client runs on, and it is gated on a pending render so an idle loop
never pays for it.
A title is only remembered as delivered once a foreground client takes
it, so the first client to attach is written to rather than skipped.
`client.window_title.set` still wins over the configured title, and
clearing it now hands the title back to `ui.window_title` instead of
only "herdr".
* fix: deliver the outer window title to a newly attached client
ClientConnected assigns the foreground client directly rather than going
through promote_client_to_foreground, so clearing the sent-title cache
there missed the case that matters most: attaching a second terminal to a
running session. The title was usually unchanged, so the sync returned
early and the new terminal kept whatever its shell or ssh had left.
Key the cache on the client that received the title instead of relying on
every foreground assignment to invalidate it.
* perf: keep hidden pane output off the window title path
Output from a hidden or background pane sets needs_render without setting
needs_full_render, and the retained render plan then skips presentation
for it entirely. Syncing the title on needs_render meant every coalesced
hidden-output tick still formatted, sanitized, and allocated a title that
could not have changed, against the hidden-source early exit AGENTS.md
requires.
Every input to the title is app state, which always requests a full
render, so gate on that instead. The one exception is the focused pane's
own terminal title, which arrives through PTY parsing, so ask for a full
render when that changes and the configured title uses it.
* fix: only cache a window title a client writer received
* fix: carry an api set window title across a live handoff
* fix: make outer window title updates event-driven
---------
Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>