Repository navigation
chore: sync upstream 2026-10-08 - #75
Conversation
…4734) * fix: keep sidebar collapse clickable with agent scrollbars refs herdrdev#4692 * docs: document sidebar mouse handling and regression coverage refs herdrdev#4692
* fix: retain remote sidebar preferences across attaches refs herdrdev#4364 * test: cover remote preference identity handoff --------- Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
) Bumps the github-actions group with 3 updates: [taiki-e/install-action](https://github.com/taiki-e/install-action), [cachix/install-nix-action](https://github.com/cachix/install-nix-action) and [softprops/action-gh-release](https://github.com/softprops/action-gh-release). Updates `taiki-e/install-action` from 2.81.9 to 2.87.22 - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](taiki-e/install-action@fd2f5e3...83ac0ad) Updates `cachix/install-nix-action` from 31.10.6 to 31.11.1 - [Release notes](https://github.com/cachix/install-nix-action/releases) - [Changelog](https://github.com/cachix/install-nix-action/blob/master/RELEASE.md) - [Commits](cachix/install-nix-action@8aa0397...13d8dd5) Updates `softprops/action-gh-release` from 3.0.0 to 3.0.3 - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](softprops/action-gh-release@b430933...efb3536) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.87.22 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: cachix/install-nix-action dependency-version: 31.11.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: softprops/action-gh-release dependency-version: 3.0.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…dle for agy and grok (herdrdev#4959) refs herdrdev#3871 refs herdrdev#3530 refs herdrdev#4735 refs herdrdev#3419
…herdrdev#4964) * fix: keep agent sessions while the agent is suspended or backgrounded refs herdrdev#1647 refs herdrdev#1033 * fix: drop a suspended agent's session when a new one of its kind takes over refs herdrdev#1647
…h 3 updates (herdrdev#4955) Bumps the cargo-dependencies group with 3 updates in the / directory: [regex](https://github.com/rust-lang/regex), [serde](https://github.com/serde-rs/serde) and [futures-util](https://github.com/rust-lang/futures-rs). Updates `regex` from 1.12.3 to 1.13.1 - [Release notes](https://github.com/rust-lang/regex/releases) - [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md) - [Commits](rust-lang/regex@1.12.3...1.13.1) Updates `serde` from 1.0.228 to 1.0.229 - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](serde-rs/serde@v1.0.228...v1.0.229) Updates `futures-util` from 0.3.33 to 0.3.34 - [Release notes](https://github.com/rust-lang/futures-rs/releases) - [Changelog](https://github.com/rust-lang/futures-rs/blob/main/CHANGELOG.md) - [Commits](rust-lang/futures-rs@0.3.33...0.3.34) --- updated-dependencies: - dependency-name: futures-util dependency-version: 0.3.34 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: cargo-dependencies - dependency-name: regex dependency-version: 1.13.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: cargo-dependencies - dependency-name: serde dependency-version: 1.0.229 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: cargo-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from e18b497796c12c097a38f9edb9d0641fb99eee32 to f0d9c3887740aee45f6153b24b3a6b815192ec16. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](Swatinem/rust-cache@e18b497...f0d9c38) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: f0d9c3887740aee45f6153b24b3a6b815192ec16 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* fix: keep the server alive when thread spawns fail refs herdrdev#3324 * fix: keep thread spawn fallbacks from dropping work refs herdrdev#3324 * fix: validate agent prompts before starting the waiter refs herdrdev#3324 * fix: retry failed startup git refresh without clients refs herdrdev#3324 * fix: time plugin commands from their exit refs herdrdev#3324 * fix: make plugin finish time test independent of load refs herdrdev#3324
* 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 herdrdev#4356 * test: replay reporter captures for fixed split-read and key-release issues refs herdrdev#4856 refs herdrdev#4025 refs herdrdev#4184 * fix: forward f1, f2 and f4 from kitty keyboard hosts refs herdrdev#4403 * fix: send pixel mouse reports to unfocused panes and only when the host supports them refs herdrdev#4750 * fix: treat 0x08 as backspace when the terminal's erase key is ctrl+h refs herdrdev#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: tell fish that herdr reflows its prompt on resize refs herdrdev#3672 * fix: match fish_handle_reflow casing exactly on windows refs herdrdev#3672 * test: keep temp path out of fish env spawn test command refs herdrdev#3672
WalkthroughNote A clean, hand-resolved upstream sync: Merge-resolution verification (at
|
| Conflict (per PR body) | Verified result |
|---|---|
1. src/api/server.rs |
✅ Fork's fail-soft http-push listener retained (resolved_http_push_listen_addr / start_http_push_server, :127-128) and started before the accept loop; upstream's named accept thread adopted (spawn_named("herdr-api-accept", …), :138); ServerHandle still owns the listener handle. |
2. src/app/api/worktrees/reads.rs |
✅ Fork's bounds-checked get(ws_idx) else { return Err(...) } runs before upstream's require_restored_worktree_ready(ws_idx)? (:129); the second site keeps the fork's get + return false. |
3. src/client/input.rs |
✅ Fork's flush_unix_input_after_idle helper kept (:339; call sites :131, :220) and now waits on upstream's RawInputByteFramer::idle_flush_timeout_ms (:129, :216); the fork's stale free timeout fn is gone. |
4. src/detect/mod.rs |
✅ Fork's Agent::Ziki survives the union (variant :67, ALL :96, SCREEN_MANIFEST_AGENTS :122, label/executable :152/:188, lookup :229); upstream's identify_agent_process_in_job keeps the pid via the fork's identify_agent_for_process (:259, called :283/:291). |
5. src/client/terminal_setup.rs |
✅ Upstream's HostTerminalProbe + query_host_escape_disambiguation() kept (:76-86) with the fork's deliberate cfg(unix)/#[cfg(not(unix))] split preserved over upstream's broader cfg(not(windows)) (:4, :147-152). |
6. src/pane.rs |
✅ Both loops carry upstream's agent_job: AgentJobTracker (:991) and the fork's last_lifecycle_authority_scan_at (:992); the combined skip splits into upstream's background-job skip (:1159) and the fork's 5s screen scan (SCREEN_SCAN_UNDER_LIFECYCLE_AUTHORITY_INTERVAL, :425). |
7. src/platform/mod.rs |
✅ unix_common re-exports keep both sides (fork: check_config_write_target, config_file_link_count, write_existing_config; upstream: begin_cli_output, end_cli_output, forward_remote_bridge_stdio, …); test module keeps the fork's live_pane_process_group tests + SHELL_TEST_LOCK (:701). |
8. Semantic — activate_endpoint |
✅ Upstream's new client_shell_requests_host_pixels_for_an_unfocused_pixel_pane (src/server/headless/tests/mod.rs:6265) constructs ServerEvent::ClientShellConnected with the fork's activate_endpoint: false (:6305), matching all 11 sites in the file. |
Changes
| Layer / File(s) | Summary |
|---|---|
| Conflict seams (7 files + 1 semantic) | Hand-resolved unions — see the verification table above. |
src/client/shell/tests/input_conformance.rs (+1,641), src/client/shell/tests/input.rs, src/raw_input.rs, src/client/input/windows_vti.rs |
Upstream input-framing arrival: new conformance suite and framer-timeout work. |
src/plugin_installations.rs (+470), src/cli/plugin.rs, src/app/api/plugins/**, tests/cli/plugins.rs (+594/-378) |
Upstream plugin-system work. |
src/pane.rs (+678/-70), src/pane/terminal.rs, src/pane/background_agent.rs, src/pane/input.rs |
Upstream pane / background-agent tracking work. |
src/platform/windows.rs, tests/windows_server_security.rs (+262), src/platform/ssh_agent.rs |
Upstream Windows + ssh-agent platform work. |
src/app/git_refresh.rs, src/app/agent_suspend_tests.rs, tests/session_restore.rs |
Upstream app-layer arrival. |
src/server/headless/tests/mod.rs (+321) |
Headless test additions, incl. the activate_endpoint semantic seam. |
distribution/*, docs snapshots |
Generated release surfaces — snapshot guard clean. |
Estimated Review Effort: 3 (30 minutes)
| Category of Change | Complexity |
|---|---|
Merge-conflict seams (7 files + activate_endpoint) |
🔨 Medium |
| Upstream code arrival (136 files, +11,259/-3,662) | ⚡ Low — reviewed upstream, spot-checked here |
| Guardrails / CI | ⚡ Low |
Pre-Merge Checks
| Check | Status |
|---|---|
cargo fmt --check @ 3e0260d7 |
✅ Clean (run in this review) |
scripts/fork_owned_guard.py @ 3e0260d7 |
✅ 68 entries intact (run in this review) |
scripts/generated_snapshot_guard.py @ 3e0260d7 |
✅ 69 files clean (run in this review) |
| Maintenance suites (fork guard, snapshot guard, sync-resolve-generated, agent-detection manifest, docs translation parity, changelog, config reference) | ✅ Green |
check (ubuntu-latest) / check (windows-latest) / Windows ConPTY package / validate / flake check / conventional-commits |
✅ Pass |
check (macos-latest) |
❌ server_stop_request_logs_its_caller — pre-existing fork-side exit-path trait (documented in PR body; upstream 4f896118 passes it, fork binary wedges identically) — not a merge regression |
| Mergeability | ✅ MERGEABLE (base master) |
🧸 Poetry
🐰 Seven seams stitched paw to paw,
the fork's own flags survive it all.
Guardrails green, the tide complete —
one red check that predates this feat. 🥕
Merge taskmerged as 262e780 |
Merges
upstream/master(4f896118) intomaster(bcc91654) and resolves the 7 conflicts by hand — no-X theirs, no-s ours, per the fork-upstream-sync policy.The branch starts from the current upstream tip — one commit newer than the
4dc23bb1recorded in the sync-conflict comment — so the next scheduled sync is a true no-op. (The issue body is from the 2026-10-06 run; the 2026-10-08 comment raised the conflict set from 5 files to 7. All seven are resolved here.)How each conflict was resolved
1.
src/api/server.rs— keep the Ziki http-push listener, adopt the named accept threadThe fork's http-push listener (
resolved_http_push_listen_addr/start_http_push_server, fail-soft) stays, started before the accept loop; upstream's newcrate::thread_spawn::spawn_named("herdr-api-accept", …)wraps the accept thread. TheServerHandlekeeps owning the listener handle so shutdown still handles it.2.
src/app/api/worktrees/reads.rs— bounds check + restored-worktree gateFork's bounds-checked
self.state.workspaces.get(ws_idx) else { return Err(...) }and upstream'sself.require_restored_worktree_ready(ws_idx)?both run, in that order.3.
src/client/input.rs— keep the fork's idle-flush helper, drop the stale free timeout fnFork's
flush_unix_input_after_idlehelper is kept (both call sites and the tests use it). The fork's freeidle_flush_timeout_msfunction is dropped: upstream moved that logic ontoRawInputByteFramer::idle_flush_timeout_ms, and every merged call site already uses the framer method — keeping the free function would leave dead code referencing methods that no longer exist.4.
src/detect/mod.rs— union of the ziki detection pathidentify_agent_for_process(...)(fork) still runs first and yields(agent, candidate, process.pid).5.
src/client/terminal_setup.rs— upstream's host terminal probe, fork'scfg(unix)guardsUpstream's
HostTerminalProbe+query_host_escape_disambiguation()(with the?1016$pSGR pixel-mouse query) is kept, with the fork's deliberatecfg(unix)/#[cfg(not(unix))]split (fork commit65ef2cb9, which avoids a non-unix build regression) preserved over upstream's broadercfg(not(windows)).6.
src/pane.rs— both tracking fields, split skip conditionsBoth task loops carry upstream's
agent_job: AgentJobTracker::default()and the fork'slast_lifecycle_authority_scan_at. The old combined skip condition splits in two: upstream's background-job skip (agent_job.in_background() && !process_exited) stays, and the fork's every-5s screen scan under lifecycle authority (SCREEN_SCAN_UNDER_LIFECYCLE_AUTHORITY_INTERVAL, drivingforce_refresh: lifecycle_refresh_due) runs independently.7.
src/platform/mod.rs— re-export union + test-module unionunix_commonre-exports keep both sides (fork:check_config_write_target,config_file_link_count,write_existing_config; upstream:begin_cli_output,end_cli_output,forward_remote_bridge_stdio,ignore_server_hangup,local_stream_peer_description,spawn_server_signal_monitor,RemoteBridgeWake). The test module keeps the fork's twolive_pane_process_grouptests followed by the fork'sSHELL_TEST_LOCKstatic.8. Semantic conflict that merged cleanly —
activate_endpointonServerEvent::ClientShellConnectedNot every conflict hits a marker. Fork
master(PR #74) addedactivate_endpointtoServerEvent::ClientShellConnected, while upstream added a new test (client_shell_requests_host_pixels_for_an_unfocused_pixel_pane, upstream herdrdev#4750) that constructs the same event insrc/server/headless/tests/mod.rs. Different hunks merged cleanly, but the tree did not compile (error[E0063]: missing field activate_endpoint,tests/mod.rs:6291). Resolved by passingactivate_endpoint: falseat that construction site, matching the other ten sites in the file.cargo check --locked --all-targetsthen compiles every target.Validation
On this host (Intel macOS 15.7.9, repo-pinned Rust 1.96.1, Zig 0.16.0, MSVC cross SDK at
~/.local/share/herdr/windows-cross/), run on the merge staged for this PR:cargo fmt --checkcargo clippy --all-targets --locked -- -D warningscargo nextest run --locked --no-fail-fastjust maintenance-testjust ui-hot-path-architecture-testjust integration-assets-testjust windows-lint(MSVC cross)just docs-contract-testscripts/fork_owned_guard.pyscripts/generated_snapshot_guard.pyPre-existing local failure:
server_stop_request_logs_its_callerserver_stop_request_logs_its_caller(tests/server_headless.rs:636) failsdeterministically on this host —
assertion failed: wait_for_exit(&mut spawned.child, Duration::from_secs(10)).It is not a merge regression:
The server's own log shows a complete, correct shutdown in ~77 ms (
server shutdown initiated→completing server shutdown→headless server exiting→herdr exiting); only the child reap lags.The test is brand new here: it arrives with upstream
c4653a4f(fix: keep the server running on hangup and log why it stops herdrdev/herdr#4965) and exists on neither thefork's
master(bcc91654) nor the merge base — this merge is its first exposure.Upstream
4f896118passes it in ~2 s on this same host.Standalone pty harness (spawn
herdr serverwith stdout/stderr on a pty whose master is neverread — exactly what the test harness does — then send
server.stop):server.stop4f896118?NEs→ZN), 0 bytes drained?NEsfor 20 s+, exits the instant the master is drained (476 bytes pending)herdr 0.9.3, built 2026-10-08 06:43)So the fork's exit path has always waited on the unread pty; upstream's does not. This is a
pre-existing fork-side trait, not something the merge changed — the merge only adds the first
test that spawns the server on a never-read pty and asserts reaping.
It is not generic unread-pty behaviour: a C program writing ≤900 pending bytes to an unread pty
exits normally (only a literally full 1024-byte queue blocks, and that blocks the write, not
the exit). A precise root-cause patch is out of scope for a sync PR; the wedged process is
un-inspectable from userland (
ps -Mempty;sampleandlldbrefuse), and the fix belongswith the fork's exit path. Follow-up: harden the fork exit path against an undrained pty.
Load-sensitive failure:
server_survives_hangup_and_logs_why_it_stopsserver_survives_hangup_and_logs_why_it_stopsfails only in the full parallel run (under 3904concurrent tests it misses its "server shutdown initiated" log deadline) and passes in isolation
on the same binary (
cargo nextest run -E 'test(server_survives_hangup_and_logs_why_it_stops)'→
PASS [1.758s]). A local load-sensitive timing flake, not a merge artifact. Both this testand the stop test arrive with upstream
c4653a4f(herdrdev#4965); neither exists on the merge base.Closes #72