Skip to content

feat: add Letta Code detection and restore - #3107

Merged
JJLiebig merged 1 commit into
masterfrom
issue/3106-letta-code
Sep 13, 2026
Merged

JJLiebig merged 1 commit into
masterfrom
issue/3106-letta-code

Conversation

@ogulcancelik

@ogulcancelik ogulcancelik commented Aug 22, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • detect interactive Letta Code processes and track idle, working, and blocked states from OSC signals with screen fallbacks
  • add an optional session-only hook and native conversation restore, including default conversations
  • expose Letta through agent start, herdr integration install|uninstall letta, config, and release docs

Builds on the initial integration work from @just-cameron in https://github.com/just-cameron/herdr/tree/letta-code-integration.

Endpoint compatibility note

Letta is deliberately not added to the frozen generation-1 client endpoint IntegrationTarget enum. Adding a variant there changes the integration.install method shape shipped in stable 0.9.0 (the endpoint contract added in #3509), so it would break the published v1 contract.

Instead Letta is an experimental, CLI-only target: herdr integration install letta / herdr integration uninstall letta work through a dedicated local path, and herdr integration status reports it as letta (experimental). It is intentionally absent from the JSON API schema and the TUI integration list.

The agent registry (architecture/agent-registry) should be merged before we finish the full v1 fixture, so a new target can be registered and negotiated without touching the frozen enum.

Validation

  • cargo fmt --check
  • cargo clippy --all-targets --locked -- -D warnings
  • cargo nextest run --locked (2975 passed), plus maintenance, integration-asset, and docs-contract checks
  • live Letta Code v0.32.6 on Windows PowerShell: process detection, integration install / status / uninstall, and session hook reporting
  • live Letta Code v0.30.28 process, idle detection, hook installation, and default-conversation session reporting

Refs #3106

@coderabbitai

coderabbitai Bot commented Aug 22, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 27db56c2-a9e0-4a55-b6d8-e35ced75eb4a

📥 Commits

Reviewing files that changed from the base of the PR and between e03e033 and daf9d95.

📒 Files selected for processing (21)
  • docs/next/CHANGELOG.md
  • docs/next/api/herdr-api.schema.json
  • docs/next/website/src/content/docs/agent-automation.mdx
  • docs/next/website/src/content/docs/agents.mdx
  • docs/next/website/src/content/docs/cli-reference.mdx
  • docs/next/website/src/content/docs/ja/agent-automation.mdx
  • docs/next/website/src/content/docs/ja/agents.mdx
  • docs/next/website/src/content/docs/ja/cli-reference.mdx
  • docs/next/website/src/content/docs/zh-cn/agent-automation.mdx
  • docs/next/website/src/content/docs/zh-cn/agents.mdx
  • docs/next/website/src/content/docs/zh-cn/cli-reference.mdx
  • docs/next/website/src/data/config-reference.json
  • src/config/sound.rs
  • src/detect/manifest.rs
  • src/detect/manifests/letta.toml
  • src/detect/mod.rs
  • src/integration/mod.rs
  • src/integration/targets.rs
  • src/integration/tests.rs
  • src/main.rs
  • website/agent-detection/letta.toml
🚧 Files skipped from review as they are similar to previous changes (6)
  • docs/next/website/src/content/docs/cli-reference.mdx
  • docs/next/website/src/content/docs/agents.mdx
  • docs/next/website/src/content/docs/agent-automation.mdx
  • src/detect/manifest.rs
  • docs/next/website/src/content/docs/ja/agents.mdx
  • docs/next/CHANGELOG.md

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.


📝 Walkthrough

Walkthrough

Herdr adds Letta Code detection, session hooks, integration installation, native conversation restore, configuration entries, API and CLI support, localized documentation, and changelog entries.

Changes

Letta Code support

Layer / File(s) Summary
Letta process and screen detection
src/detect/*, website/agent-detection/*
Letta process identification, interactive-command filtering, aliases, screen-manifest registration, terminal-state rules, and tests are added.
Letta session integration lifecycle
src/integration/*
Unix and Windows session hooks report Letta session metadata. Installation and removal update ~/.letta/settings.json and manage files transactionally.
Letta targets and native resume
src/api/schema/integrations.rs, src/cli/integration.rs, src/agent_resume.rs, src/config/*, src/main.rs, docs/next/api/herdr-api.schema.json, docs/next/website/src/data/config-reference.json
Letta becomes an integration target and supported agent kind. Native resume handles conversation IDs and default:<agent-id> references. Sound and CJK IME configuration accept Letta names.
Letta documentation and release metadata
docs/next/website/src/content/docs/*, docs/next/CHANGELOG.md
English, Japanese, and Chinese documentation describe Letta installation, detection, session reporting, restoration, and CLI support. The changelog records Letta support and other fixes.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: 🔵 Low · up to daf9d

The PR adds Letta session detection, conversation restore, and a user-level SessionStart hook. It is mergeable with explicit owner awareness because status may remain stale after approval, session identity reporting may fail if hook settings are rejected, and interrupted or concurrent setup changes could leave configuration inconsistent.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.07% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 61 functions across 17 files. (14 skipped… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the main changes: Letta Code detection and conversation restoration.
Description check ✅ Passed The description directly covers Letta Code detection, session hooks, conversation restoration, CLI integration, configuration, documentation, and validation. It is related to the changeset, although i…
Full details: Docstring Coverage

Explanation

Docstring coverage is 36.07% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 61 functions across 17 files. (14 skipped: 14 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue/3106-letta-code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@kangal-bot kangal-bot added the ai-review Trigger automated AI reviews for pull requests admitted by the PR gate label Aug 22, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
src/config/sound.rs (1)

47-47: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Add an explicit regression assertion for omitted letta.

#[serde(default)] already preserves AgentSoundOverrides::default(). Add an assertion that an absent letta field produces AgentSoundSetting::Default.

src/detect/mod.rs (1)

629-640: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

The bare letta-code/letta path match is broad.

The windows(2) check matches any path that contains a letta-code directory followed by a letta component, outside node_modules. A local checkout path such as /home/user/src/letta-code/letta/build.js would be classified as Letta. The scoped windows(4) match above already covers the published package layout. The is_interactive_letta_process gate limits the damage, so this is a small risk only.

Consider anchoring the fallback to a node_modules or .bin context, or to a trailing component.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: aa59483c-24ca-4cc4-8592-87c045048df4

📥 Commits

Reviewing files that changed from the base of the PR and between 1c76079 and 04c821e.

📒 Files selected for processing (39)
  • docs/next/CHANGELOG.md
  • docs/next/api/herdr-api.schema.json
  • docs/next/website/src/content/docs/agent-automation.mdx
  • docs/next/website/src/content/docs/agents.mdx
  • docs/next/website/src/content/docs/cli-reference.mdx
  • docs/next/website/src/content/docs/integrations.mdx
  • docs/next/website/src/content/docs/ja/agent-automation.mdx
  • docs/next/website/src/content/docs/ja/agents.mdx
  • docs/next/website/src/content/docs/ja/cli-reference.mdx
  • docs/next/website/src/content/docs/ja/integrations.mdx
  • docs/next/website/src/content/docs/ja/session-state.mdx
  • docs/next/website/src/content/docs/session-state.mdx
  • docs/next/website/src/content/docs/zh-cn/agent-automation.mdx
  • docs/next/website/src/content/docs/zh-cn/agents.mdx
  • docs/next/website/src/content/docs/zh-cn/cli-reference.mdx
  • docs/next/website/src/content/docs/zh-cn/integrations.mdx
  • docs/next/website/src/content/docs/zh-cn/session-state.mdx
  • docs/next/website/src/data/config-reference.json
  • src/agent_resume.rs
  • src/api/schema/integrations.rs
  • src/cli/integration.rs
  • src/config/model.rs
  • src/config/sidebar.rs
  • src/config/sound.rs
  • src/detect/manifest.rs
  • src/detect/manifests/letta.toml
  • src/detect/mod.rs
  • src/integration/actions.rs
  • src/integration/assets/letta/herdr-agent-session.ps1
  • src/integration/assets/letta/herdr-agent-session.sh
  • src/integration/env.rs
  • src/integration/mod.rs
  • src/integration/registry.rs
  • src/integration/targets.rs
  • src/integration/tests.rs
  • src/integration/types.rs
  • src/main.rs
  • website/agent-detection/index.toml
  • website/agent-detection/letta.toml

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Comment thread src/detect/manifests/letta.toml Resolved
Comment thread src/detect/mod.rs Resolved
Comment thread src/integration/targets.rs Resolved
Comment thread src/main.rs Resolved
@ogulcancelik
ogulcancelik force-pushed the issue/3106-letta-code branch from 04c821e to cfeed00 Compare August 22, 2026 02:17
@greptile-apps

greptile-apps Bot commented Aug 22, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

The PR appears safe to merge because no blocking failure remains within the eligible follow-up review scope.

Summary

  • Adds process and screen-state detection using OSC signals and terminal fallbacks.
  • Adds session hook installation, conversation reporting, and native restore planning.
  • Adds Letta configuration, CLI help, integration status, tests, and localized documentation.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  CLI[herdr integration CLI] --> Install[Install Letta SessionStart hook]
  Letta[Letta Code process] --> Detect[Process and screen detection]
  Detect --> State[Idle / Working / Blocked]
  Letta --> Hook[SessionStart hook]
  Hook --> Report[Report conversation reference]
  Report --> Restore[Restore planner]
  Restore --> Command[letta --conversation ...]
Loading

Reviews (7) · Last reviewed commit: "feat: add Letta Code detection and resto..."

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
src/config/sound.rs (1)

226-226: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the Letta-specific lookup with a non-default test.

The current assertion verifies only the default value. It does not verify that a configured value reaches AgentSoundOverrides::for_agent for Agent::Letta. Add letta = "on" to the fixture and assert both the parsed field and the lookup result.

Suggested test extension
 [ui.sound.agents]
 droid = "off"
 claude = "on"
+letta = "on"
 ...
-        assert_eq!(config.ui.sound.agents.letta, AgentSoundSetting::Default);
+        assert_eq!(config.ui.sound.agents.letta, AgentSoundSetting::On);
+        assert_eq!(
+            config
+                .ui
+                .sound
+                .agents
+                .for_agent(Some(crate::detect::Agent::Letta)),
+            AgentSoundSetting::On
+        );
src/detect/mod.rs (1)

692-735: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider normalizing the --flag=value form once.

The list repeats each option twice: once as an exact match and once as a --name= prefix. Splitting the argument at the first = and matching the name once removes the duplication and keeps both forms in sync when the option list changes.

♻️ Possible simplification
-    if cli_args.iter().any(|arg| {
-        matches!(
-            arg.as_str(),
+    if cli_args.iter().any(|arg| {
+        let name = arg.split_once('=').map_or(arg.as_str(), |(name, _)| name);
+        matches!(
+            name,
             "-p" | "--print"

Then drop the starts_with("--…=") chain.

src/integration/targets.rs (1)

1209-1211: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reuse a single letta_dir() result.

letta_dir() is called twice. Bind it once and derive both paths from it.

♻️ Proposed change
-    let hook_path = letta_dir()?.join("hooks").join(LETTA_HOOK_INSTALL_NAME);
-    let settings_path = letta_dir()?.join("settings.json");
+    let dir = letta_dir()?;
+    let hook_path = dir.join("hooks").join(LETTA_HOOK_INSTALL_NAME);
+    let settings_path = dir.join("settings.json");

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a6b68c80-a89f-46f3-907b-3c80fcdc37c8

📥 Commits

Reviewing files that changed from the base of the PR and between 04c821e and cfeed00.

📒 Files selected for processing (6)
  • docs/next/website/src/data/config-reference.json
  • src/config/sound.rs
  • src/detect/mod.rs
  • src/integration/assets/letta/herdr-agent-session.ps1
  • src/integration/targets.rs
  • src/integration/tests.rs

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.

@ogulcancelik
ogulcancelik force-pushed the issue/3106-letta-code branch 2 times, most recently from 0e6f70e to 6c0ff7b Compare August 22, 2026 02:25

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/integration/targets.rs (1)

1011-1018: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Use a named millisecond timeout constant for the Letta hook. Letta Code v0.30.28 supports both timeout in milliseconds and quiet as a boolean. Replace the unexplained 10_000 literal with LETTA_HOOK_TIMEOUT_MS.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0a396887-ed40-43ca-91ce-80b4fa6492ca

📥 Commits

Reviewing files that changed from the base of the PR and between 0e6f70e and 6c0ff7b.

📒 Files selected for processing (3)
  • src/config/sound.rs
  • src/detect/mod.rs
  • src/integration/targets.rs

Included review availability: Your plan provides up to 10 included reviews per hour; 6 remain after this review.

@ogulcancelik
ogulcancelik force-pushed the issue/3106-letta-code branch from 6c0ff7b to e03e033 Compare August 22, 2026 12:34

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 25b3509d-f279-4a69-94a2-22d181088986

📥 Commits

Reviewing files that changed from the base of the PR and between 6c0ff7b and e03e033.

📒 Files selected for processing (3)
  • src/integration/mod.rs
  • src/integration/targets.rs
  • src/integration/tests.rs

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Comment thread src/integration/targets.rs Resolved
@ogulcancelik
ogulcancelik force-pushed the issue/3106-letta-code branch from e03e033 to f96c294 Compare August 22, 2026 12:47
@ogulcancelik

Copy link
Copy Markdown
Collaborator Author

hey @just-cameron could you verify if this branch works great with letta?

@just-cameron

Copy link
Copy Markdown
Contributor

Yeah, taking a look now. Thanks!

@just-cameron

Copy link
Copy Markdown
Contributor

Okay -- battletested and found one blocking bug. You can see my agent's fix in this commit.

I have included my agent's report for your perusal.

As a side note, one of our users added a lot of bells and whistles to improve the herdr/letta experience, but I think it is out of scope for this PR. Would you like me to open issues for some of the features I liked in his work? https://github.com/klittle32/letta-herdr-mod

Agent report

Tested this thoroughly against exact PR head f96c29477ec8c842a122eb04548bb3f83590de04 using Letta Code 0.30.30 on macOS.

What worked:

  • Letta process detection
  • Default conversation restore
  • Named conversation restore (local-conv-24)
  • Live idle → working → idle transitions during a real model turn
  • Live blocked → idle transitions using an AskUserQuestion interaction
  • SessionStart identity reporting through herdr:letta
  • Clean return to the shell after Letta exits

The 11 Letta-focused tests pass, as do strict Clippy, manifest validation, and the documentation builds.

I found one startup edge case:

  1. Start Letta with an invalid --agent value.
  2. Letta opens its profile selector instead of a conversation.
  3. Herdr can report the agent as idle and interactive_ready: true, causing agent start to succeed even though Letta is still waiting for profile selection.

agent explain showed this came from default_known_agent_idle_fallback; there was no explicit live-idle match. The settled profile-selector screen can be recognized, but there is also an initial rendering race where the generic fallback can mark startup ready before that selector is visible. I think startup should remain pending or unknown until Letta exposes explicit idle composer evidence.

The PR currently conflicts with master. I rebased it locally onto 7d56b4c5; only the changelog required manual conflict resolution.

One note on validation: the full just check run still hits live_handoff_keeps_unmanaged_agent_name_bound_to_saved_session, but I reproduced that same failure on exact origin/master, so it does not appear related to this PR.

Overall, the integration works well in real use. The false-ready profile-selector case is the only Letta-specific issue I found.

@ogulcancelik
ogulcancelik force-pushed the issue/3106-letta-code branch from f96c294 to daf9d95 Compare August 27, 2026 13:29
@ogulcancelik

Copy link
Copy Markdown
Collaborator Author

Agreed—the known-agent idle fallback could make startup succeed before Letta showed live composer evidence. I applied your manifest fix and regression coverage, then rebased the PR onto current master. just check passes. Thanks for the thorough real-world validation.

@ogulcancelik

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 27, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ogulcancelik

Copy link
Copy Markdown
Collaborator Author

@just-cameron could you re-review please and verify if it works with letta?

@langri-sha

Copy link
Copy Markdown

FWIW, tested dev with latest v0.31.5 for simple case that Letta now appears in the agent list and the indicators, agent restoration work alright.

@just-cameron

Copy link
Copy Markdown
Contributor

Agent report attached after some back and forth. These two are both minor to be honest. The first about the conversation switching could be kind of annoying down the road so I would address it now, the other is about a relatively uncommon flag in Letta that is not used often.

Otherwise I would say this is mergable.

Review summary

I reviewed #3107 at commit daf9d9575f021794f75cb247e0ea6cc1faf8244e.

The integration is largely working, and the earlier profile-selector false-ready fix is correctly included. I found one restore-correctness blocker and one smaller process-detection issue.

What works

  • cargo build --locked, formatting, Clippy, and the focused Letta test suite pass.
  • The Letta integration installs and uninstalls cleanly.
  • Repeated installation is idempotent. It leaves one managed hook and one SessionStart entry.
  • Uninstall removes the managed hook and settings entry.
  • The hook reports the expected identities:
    • Default conversation: default:<agent-id>
    • Named conversation: conv-*
    • Malformed default-conversation payloads are ignored.
  • Native Letta commands for default, new, and resumed named conversations work with Letta Code 0.31.7.
  • The current detection manifest correctly classifies captured real Letta screens:
    • Empty startup: unknown
    • Profile selector: unknown
    • Typed composer: unknown
    • Empty composer: idle
    • Active turn: working
    • Command approval: blocked
    • Historical approval text above a live composer: idle, not falsely blocked
  • Cameron's previous profile-selector fix is present byte-for-byte in both Letta manifests.

Blocker: conversation changes retain the stale restore ID

The Letta hook reports subsequent SessionStart events when the user creates, clears, forks, selects, or resumes a conversation. However, session_report_allows_session_replacement does not currently allow herdr:letta to replace an existing same-owner conversation ID.

As a result:

  1. Herdr stores conversation A.
  2. The same Letta process changes to conversation B.
  3. The hook reports conversation B.
  4. Herdr rejects the update as conflicting_same_owner_session_ref.
  5. A later Herdr restart can restore stale conversation A.

I proved this with a temporary regression test against the unmodified PR. The test failed while retaining the old conversation ID, then passed with this additional allow-list arm:

| ("herdr:letta", "letta", Some("new" | "resume"))

Suggested fix:

  • Add that arm to session_report_allows_session_replacement.
  • Add regressions for both new and resume.
  • Keep the existing process/ownership checks so reports cannot replace sessions across unrelated panes or processes.

I would fix this before merge because it affects the core restore behavior introduced by the PR.

Smaller issue: --disable-memory-guard is treated as noninteractive

is_interactive_letta_process currently includes --disable-memory-guard in its list of headless selectors. That flag is also valid when launching the normal interactive Letta TUI, so:

letta --disable-memory-guard

can display an interactive session while Herdr filters it out.

Suggested fix:

  • Remove --disable-memory-guard from the noninteractive-option list.
  • Add an argv regression confirming that an otherwise interactive invocation containing this flag is still detected.

@leonardschneider

Copy link
Copy Markdown

Is there any ETA to address the issues and merge the PR? It seems straightforward and I could make it work locally.

@JJLiebig

Copy link
Copy Markdown
Collaborator

Is there any ETA to address the issues and merge the PR? It seems straightforward and I could make it work locally.

Installing it Letta Code rn and will see if we can get this sorted

refs #3106

Co-authored-by: Cameron <cameron@pfiffer.org>
@JJLiebig
JJLiebig force-pushed the issue/3106-letta-code branch from daf9d95 to 5e54411 Compare September 13, 2026 15:23
@JJLiebig

Copy link
Copy Markdown
Collaborator

@greptileai review

@JJLiebig
JJLiebig self-requested a review September 13, 2026 15:33

@JJLiebig JJLiebig left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is adding Letta Code detection, native conversation restore and the SessionStart hook in a preview form so people can use Letta with Herdr right now.

Use herdr integration install letta to manually install the integration.

Note that this is not a first-class integration as the agent registry rework was started without Letta in-scope.

There should be a follow up once the registry PR is merged to fully integrate Letta V1 that way.

Until then, this is a reasonable usability compromise.

@JJLiebig
JJLiebig merged commit fc1cb77 into master Sep 13, 2026
8 checks passed
@JJLiebig
JJLiebig deleted the issue/3106-letta-code branch September 13, 2026 15:36
@leonardschneider

Copy link
Copy Markdown

Thanks for picking this up. We got a locally patched Herdr + Letta 0.32.2 setup working on macOS, and these were the changes we made along the way.

On the Herdr side, we applied the two fixes Cameron described:

  • Allowed herdr:letta reports for new and resume to update the same process’s stored conversation ID, keeping the existing ownership checks.
  • Removed --disable-memory-guard from the noninteractive flag list.

We also encountered a few issues on the Letta side:

  • Same-agent /resume could switch the conversation without emitting SessionStart. We added that reporting and verified exactly one event per successful resume.
  • Background activity could outlast Letta’s busy indicator, making Herdr appear finished while jobs or their follow-ups were still running. Our local patch keeps the activity signal active through that work.
  • Long Codex responses could abort around five minutes because a header-timeout timer remained active after headers arrived. We corrected that and verified active streams lasting 305 seconds. Completely silent connections have a separate idle-timeout limit.

Our patched pair passed startup, conversation switching, and resume tests under Node and Bun on macOS. These results are from our local builds; we haven’t runtime-tested your latest rebased revision yet.

arrrrny added a commit to arrrrny/herdr that referenced this pull request Sep 15, 2026
* fix: disconnect stalled terminal observers (herdrdev#4039)

* fix: order ui accent before nested config tables (herdrdev#2698)

* fix: order ui accent before nested config tables

refs herdrdev#2697

* test: reject duplicate ui accent examples

refs herdrdev#2697

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: decouple pixel mouse from pane graphics (herdrdev#4045)

refs herdrdev#3295

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: restrict claude session hooks to native sources (herdrdev#4046)

refs herdrdev#4018

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix(input): preserve super-modified pane keys (herdrdev#4044)

refs herdrdev#3710

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* test: relax windows docs snapshot timeout (herdrdev#4049)

Windows CI runs these documentation contract fixtures under variable load. A degraded runner pushed the preview and versions snapshot integration tests past their 30s ceiling, while a healthy run finishes in seconds. Keep 30s elsewhere and give Windows a 120s ceiling.

* fix: retain validated split mouse reports across idle gaps (herdrdev#4048)

refs herdrdev#3911

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix(server): transfer handoff descriptors in batches (herdrdev#3411)

Live handoff refused any session with more than 64 panes. The pane count
was checked twice against MAX_FDS_PER_HANDOFF, and the transfer itself put
every pane's pty master into one SCM_RIGHTS control message, so the guard
was the only thing keeping the send inside the kernel's per-message limit.
A session past the limit could only be updated by closing panes or by a
normal restart, which ends every pane process.

Send the descriptors in batches of 64 instead and drop both guards. The
receiving side accumulates across recvmsg calls until the expected count
arrives, bounds every SCM_RIGHTS payload it reads by the control bytes the
kernel returned, rejects a batch that carries more descriptors than it
asked for, and closes the descriptors it already holds on any failure. A
session of 64 panes or fewer still produces one batch, so the bytes on the
wire are unchanged and HANDOFF_VERSION stays at 1.

refs herdrdev#3393

Co-authored-by: kataokatsuki <kataokatsuki@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* test: deflake windows metadata ttl expiry assertion (herdrdev#4050)

The clear-only metadata test used a 1 ms ttl, so on loaded Windows runners set_agent_metadata could see the entry as expired, drop it, and recreate it with a None ttl before next_agent_metadata_expiry() ran. Expiry is forced explicitly later in the test, so use a long ttl to keep the no-extension assertion independent of wall-clock scheduling.

* fix: detect kimi npm shim on windows (herdrdev#4053)

refs herdrdev#3317

* fix(remote): resolve ssh host aliases from git bash on windows (herdrdev#4054)

Windows OpenSSH resolves drive-letter Include paths, but Git for Windows' MSYS OpenSSH does not, so the managed remote SSH config silently ignored the user's ~/.ssh/config when herdr ran under Git Bash. Include the user config through ~/.ssh/config, which both implementations expand to the same file.

refs herdrdev#3947

* feat: add Letta Code detection and restore (herdrdev#3107)

refs herdrdev#3106

Co-authored-by: Cameron <cameron@pfiffer.org>

* fix(windows): disable host mouse reporting on terminal restore (herdrdev#4055)

Windows skipped the xterm mouse-reporting reset because clear_host_mouse_reporting was a no-op there. Standalone Git Bash (mintty) keeps SGR mouse reporting enabled after detach, so mouse motion leaks escape sequences into the shell. Emit the reset sequence on Windows too and include the X10 mode.

refs herdrdev#3748

* fix: keep hermes enabled list valid yaml (herdrdev#4056)

* fix: keep hermes enabled list valid yaml

refs herdrdev#3839

* fix: preserve hermes inline list quoting and comments

refs herdrdev#3839

* fix(ui): keep muted sidebar and tab labels readable (herdrdev#4062)

refs herdrdev#2692

* fix(windows): honor default_shell in login shell mode (herdrdev#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 herdrdev#1445

* fix(windows): add csh and tcsh to login shell flags

refs herdrdev#1445

* fix(windows): keep powershell cwd reporting in login mode (herdrdev#4065)

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 herdrdev#4060.

* fix: keep an agent name through a process-exit observation (herdrdev#3574)

A process-exit observation is not proof the agent is gone: the same
observation can be wrong while the agent keeps running, and the name is
the only handle its owner has on the pane. Detection uncertainty already
preserves the name, so releasing it on the observation alone contradicts
that contract and leaves a live agent unreachable by name with a rename
as the only recovery.

Free the name at the point the agent actually leaves the pane - a
recorded exit with no agent detected any more - so a wrong observation
costs nothing and a real exit still releases the name for reuse.

refs herdrdev#3225

Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: auto-expire worktree action error banners (herdrdev#4063)

* fix: auto-expire worktree action error banners

refs herdrdev#1827

* fix: restart endpoint error timeout on repeated errors

Route every transient client-side action error through a setter that installs a fresh deadline, so an identical repeated message cannot inherit an older, nearly-expired deadline.

refs herdrdev#1827

* test: derive endpoint error expiry assertions from stored deadline

refs herdrdev#1827

* test: deflake federated local input race (herdrdev#4066)

refs herdrdev#3670

* fix: recognize kitty CSI-u codepoints for F1 through F12 (herdrdev#2378)

Codepoints 57364-57375 (F1-F12) were missing from the kitty-protocol codepoint table, so terminals that send function keys as full CSI-u sequences (e.g. Ghostty, which enables the kitty keyboard protocol by default) had those keys silently dropped. Confirmed live against a running herdr session: injecting the raw F3/F4 codepoint sequences did nothing before this fix and correctly triggered previous_tab/next_tab after.

refs herdrdev#1809

Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix(input): accept parameterized CSI-tilde F1-F4 keys (herdrdev#4067)

Terminals such as foot send F1-F4 as parameterized CSI-tilde sequences (for
example F3 as `\x1b[13;1:1~`). `parse_xterm_modified_special_sequence` mapped
codes 15-24 but not 11-14, so these presses were dropped before keybindings
saw them. Map 11-14 to F1-F4, matching the unmodified `\x1b[11~`..`\x1b[14~`
aliases already accepted.

refs herdrdev#1809

* fix: clean up failed terminal observer setup (herdrdev#4071)

refs herdrdev#3612

* chore: add jjliebig as approved contributor and maintainer

* feat: install windows remote host packages (herdrdev#3687)

* feat: install windows remote host packages

refs herdrdev#3651

* docs: clarify windows saved reconnect package behavior

refs herdrdev#3651

* fix: include windows installer in nix sources

refs herdrdev#3651

* fix: preserve paths in windows scp transfers

refs herdrdev#3651

* refactor: isolate windows remote package setup

refs herdrdev#3651

* feat: add ctrl-hover highlighting and fix wrapped links (herdrdev#4074)

refs herdrdev#1282

* fix(linux): bound the foreground process-tree scan (herdrdev#3674)

refs herdrdev#3621

Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: keep plugins working after client-only updates (herdrdev#4077)

* ci: migrate remaining pinned actions to node24 (herdrdev#4023)

* ci: migrate remaining pinned actions to node24

actions/cache@v4 and mlugg/setup-zig@v2.2.1 still target Node 20, which
GitHub removes from runners on 2026-09-23. Bump actions/cache to v6.1.0 and
replace mlugg/setup-zig with vercel-labs/setup-zig@v1.0.2.

vercel-labs/setup-zig takes only `version`, so the use-cache/cache-size-limit
tuning and Zig global-cache reuse are dropped. See the PR for alternatives.

* ci: restore zig caching for vercel setup-zig

vercel-labs/setup-zig has no cache controls, so redirect the Zig global cache
into the workspace .zig-cache and run the Zig build cache step on Linux too.

Drop this commit if we switch to a setup action that owns Zig caching, e.g.
step-security/setup-zig or xyzzylabs/setup-zig.

* fix: clarify agent integration settings description

* fix: scope agent views to the selected machine (herdrdev#3784)

* fix: scope agent views to the selected machine

refs herdrdev#3732

* fix: preserve stable agent view ordering

refs herdrdev#3732

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix(windows): restore mouse reporting when reattaching (herdrdev#4080)

* fix(client): re-assert host mouse modes after a terminal reconnect (herdrdev#4079)

Co-authored-by: dengos <dengoswei@gmail.com>

* test(windows): deflake media player invalid media assertion (herdrdev#4082)

* test(windows): deflake media player invalid media assertion

The Windows sound test required MediaPlayer's MediaFailed event for invalid
media. Some runners never raise it, so the script exits through its playback
timer and the test saw "sound playback timed out" instead, panicking on the
MediaFailed assertion.

Accept either terminal error and keep the timer short under test via
HERDR_SOUND_TIMEOUT_SECONDS so a missed MediaFailed no longer waits out the
production 15 second bound.

* test(windows): keep media player timeout override test-only

Generate the short-timer player script only in tests instead of reading a
process environment override. This keeps the production script's fixed 15
second bound unchanged, and the script-content test now proves the test
variant rewrites the timer.

* fix: hide only kitty image placements overlapping overlays (herdrdev#4081)

* fix: clean up idle ssh bridges and back off flapping connections (herdrdev#4083)

* fix: clean up idle ssh bridges and back off flapping connections

* chore: keep ssh liveness smoke tooling local

* fix: preserve agent panel scroll when switching machines (herdrdev#4101)

refs herdrdev#3937

Root cause:
Activating another endpoint applies a snapshot with a different
endpoint-qualified graphics scope. This triggers reset_endpoint_projection,
which clears agent_scroll together with endpoint-local state even though
the agent list aggregates all machines. The selected agent receives focus,
but the list jumps back to the top.

Fix:
Save the client-owned agent scroll offset and restore it only when
activation changes endpoints. Keep same-endpoint reboot resets,
workspace/tab scroll resets, input cleanup and existing list bounds
clamping unchanged. This adds constant-time work on endpoint activation,
with no protocol, persisted-state or pane-scaled rendering changes.

Validation:
The new regression fails on the original implementation with scroll 0
instead of 6. Tests cover repeated bidirectional agent clicks, stable
visible rows, shrinking lists and same-machine reboots. All 242 client
shell tests pass.

Real SSH A/B testing against the same two disposable 0.9.0 servers
reproduces the reset with both the stable and current-master clients.
The patched client preserves the visible rows through six cross-machine
switches and two same-machine control clicks.

Native just ci passes: 3535 Rust tests, six default skips, plus lint,
maintenance, architecture and integration-asset checks. All seven docs
contract tests pass. Windows cross-lint was not run because its SDK is
not installed; native macOS and Windows UI behavior was not tested.

* fix: remove stale maintainer username

* fix: detect codex activity with static titles and queued inputs (herdrdev#4099)

* fix: detect codex activity with static titles and queued inputs

refs herdrdev#4092

Codex 0.154.0 can omit the activity bullet when tui.animations is false
and replace the literal Working label with a task-specific label. Its
terminal title can also remain static during an active turn, so the OSC
spinner rule is not sufficient to identify work.

The screen fallback required a bullet followed by Working in the last
three non-empty lines. It therefore missed both the animation-disabled
status line and dynamic labels. Queued follow-ups add a heading, input
text, and an edit hint below the activity line, moving that line outside
the three-line region. The lower-priority non-empty OSC title rule then
reported idle while Codex still showed an active timer and interrupt
control.

Recognize the elapsed-time and esc-to-interrupt controls in the detection
snapshot before the current composer. Allow an optional bullet, a dynamic
activity label, and queued follow-ups below that line. Require that no
later response, completion, or interruption marker supersedes the live
activity. This excludes composer text and stale output without letting an
earlier interruption suppress a subsequent active turn.

Keep OSC state precedence and transcript-viewer skip behavior unchanged.
Update the bundled and distributed manifests together to 2026.09.14.1.
Add regression coverage for optional bullets, dynamic labels, queued
inputs, resumed work after interruption, and stale or quoted controls.

Avoid discarding Regex search caches on every detection poll. The loader
previously cloned the entire compiled rule vector, and each Regex clone
created a fresh search cache pool despite sharing compiled read-only
state. Share the immutable rules through Arc instead. Reload publishes a
new array while in-flight readers retain a valid old generation, without
holding the global cache lock during matching. Add a cheap contains gate
for the required interrupt text before running the multiline expression.

Add a deterministic regression for compiled-rule sharing, reload
replacement, retained old readers, and concurrent detection. Measure the
baseline and optimized detector kernels in three alternating rounds on
CPU 24 with NUMA node 0 and CPU 25 with NUMA node 1. Real queued detection
falls from about 116 to 22 microseconds, and two evaluations per pane over
15 panes fall from about 3.45 to 0.65 milliseconds on both sockets, about
81 percent less time. No benchmark match results change. The cache
improvement requires the updated binary, not just a manifest reload.

Validate on the official Herdr 0.9.0 release with Codex 0.154.0 by toggling
the old and new manifests during the same active queued task. Confirm
working for active turns and idle after completion or interruption.
Pass 86 captured-screen and semantic checks through the optimized source
binary, 69 manifest/cache tests, CPU-pinned render scaling, and the full
native CI suite: 3535 Rust tests passed, with six default skips. Run CI
serially with inherited Herdr session context removed; no tests were
excluded or expectations relaxed to make CI pass.

* fix: detect codex pending follow-ups as working

refs herdrdev#4092

* fix: handle wrapped codex follow-up hints

refs herdrdev#4092

---------

Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>

* fix: detect pi working border (herdrdev#4113)

refs herdrdev#3629

* ci: restore zig local build cache reuse (herdrdev#4118)

* fix: default pane splits to the calling pane (herdrdev#4123)

* fix: restore saved machine screens after reconnect (herdrdev#4126)

refs herdrdev#3842

* fix: scope window titles to each client view (herdrdev#4130)

refs herdrdev#4091

* fix(windows): avoid lossy native Win32 input mode (herdrdev#4133)

refs herdrdev#3932
refs herdrdev#3702
refs herdrdev#3910

* fix: keep modified enter compatible with legacy panes (herdrdev#4110)

* fix: keep modified enter compatible with legacy panes

refs herdrdev#4103

* fix: synchronize modified key negotiation state

refs herdrdev#4103

* test: cover windows legacy modified enter

refs herdrdev#4103

---------

Co-authored-by: Jonathan Liebig <jonathan.liebig@gmail.com>

* fix: avoid resending terminal cells for metadata updates (herdrdev#4137)

* feat: add cursor-based editing to text inputs (herdrdev#3698)

* feat: add cursor-based editing to text inputs

refs herdrdev#1803

* fix: preserve dialog actions with associated text

refs herdrdev#1803

* refactor: simplify text editor outcomes and tests

refs herdrdev#1803

---------

Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: protect integration configs from failed writes (herdrdev#3973)

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: map pixel mouse by integer cell pitch (herdrdev#4140)

refs herdrdev#3295

* fix: reject stale pane surfaces after reconnect (herdrdev#4141)

* test: deflake recent CI failures (herdrdev#4139)

* fix(windows): batch input trace diagnostics (herdrdev#4146)

refs herdrdev#3932

* fix(windows): prefer VT input for SSH clients (herdrdev#4148)

refs herdrdev#3932

* fix: reveal the full last tab in overflowing tab strips (herdrdev#4152)

refs herdrdev#4151

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* chore: guard the ziki agent enum against future sync drops

The 2026-09-15 sync showed the gap: both sides added an Agent variant and
each bumped only its own array length in src/detect/mod.rs, so the union
auto-merged one entry short and did not compile. Nothing in
FORK_OWNED_FILES covered the file, so the marker guardrail could not see it.

refs #50

* fix: address review comments on #52

refs #50

---------

Co-authored-by: Can Celik <ogulcancelik@gmail.com>
Co-authored-by: akbash <akbash@herdr.dev>
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
Co-authored-by: Kataoka Katsuki <49934462+kataokatsuki@users.noreply.github.com>
Co-authored-by: kataokatsuki <kataokatsuki@users.noreply.github.com>
Co-authored-by: Cameron <cameron@pfiffer.org>
Co-authored-by: caner-akca <94343893+caner-akca@users.noreply.github.com>
Co-authored-by: Kakumanu Ashok Reddy <kakumanuashokreddyias2024@gmail.com>
Co-authored-by: dengos <dengoswei@gmail.com>
Co-authored-by: Eric Yue <jiacheng.yue@foxmail.com>
Co-authored-by: Mark Jaquith <mark@jaquith.me>
Co-authored-by: zuraffa-review[bot] <326983185+zuraffa-review[bot]@users.noreply.github.com>
@just-cameron

Copy link
Copy Markdown
Contributor

Thanks y'all! Looking forward to the next release, our users are quite excited.

@JJLiebig

Copy link
Copy Markdown
Collaborator

@just-cameron @leonardschneider thanks for those, note that this is truly a) manual opt-in for now and b) has to wait until the registry PR is merged.

When that's done, I'd invite you to send a patch and tag me so I can make sure we can get Letta Code added properly then

arrrrny added a commit to arrrrny/herdr that referenced this pull request Sep 17, 2026
* fix: disconnect stalled terminal observers (herdrdev#4039)

* fix: order ui accent before nested config tables (herdrdev#2698)

* fix: order ui accent before nested config tables

refs herdrdev#2697

* test: reject duplicate ui accent examples

refs herdrdev#2697

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: decouple pixel mouse from pane graphics (herdrdev#4045)

refs herdrdev#3295

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: restrict claude session hooks to native sources (herdrdev#4046)

refs herdrdev#4018

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix(input): preserve super-modified pane keys (herdrdev#4044)

refs herdrdev#3710

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* test: relax windows docs snapshot timeout (herdrdev#4049)

Windows CI runs these documentation contract fixtures under variable load. A degraded runner pushed the preview and versions snapshot integration tests past their 30s ceiling, while a healthy run finishes in seconds. Keep 30s elsewhere and give Windows a 120s ceiling.

* fix: retain validated split mouse reports across idle gaps (herdrdev#4048)

refs herdrdev#3911

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix(server): transfer handoff descriptors in batches (herdrdev#3411)

Live handoff refused any session with more than 64 panes. The pane count
was checked twice against MAX_FDS_PER_HANDOFF, and the transfer itself put
every pane's pty master into one SCM_RIGHTS control message, so the guard
was the only thing keeping the send inside the kernel's per-message limit.
A session past the limit could only be updated by closing panes or by a
normal restart, which ends every pane process.

Send the descriptors in batches of 64 instead and drop both guards. The
receiving side accumulates across recvmsg calls until the expected count
arrives, bounds every SCM_RIGHTS payload it reads by the control bytes the
kernel returned, rejects a batch that carries more descriptors than it
asked for, and closes the descriptors it already holds on any failure. A
session of 64 panes or fewer still produces one batch, so the bytes on the
wire are unchanged and HANDOFF_VERSION stays at 1.

refs herdrdev#3393

Co-authored-by: kataokatsuki <kataokatsuki@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* test: deflake windows metadata ttl expiry assertion (herdrdev#4050)

The clear-only metadata test used a 1 ms ttl, so on loaded Windows runners set_agent_metadata could see the entry as expired, drop it, and recreate it with a None ttl before next_agent_metadata_expiry() ran. Expiry is forced explicitly later in the test, so use a long ttl to keep the no-extension assertion independent of wall-clock scheduling.

* fix: detect kimi npm shim on windows (herdrdev#4053)

refs herdrdev#3317

* fix(remote): resolve ssh host aliases from git bash on windows (herdrdev#4054)

Windows OpenSSH resolves drive-letter Include paths, but Git for Windows' MSYS OpenSSH does not, so the managed remote SSH config silently ignored the user's ~/.ssh/config when herdr ran under Git Bash. Include the user config through ~/.ssh/config, which both implementations expand to the same file.

refs herdrdev#3947

* feat: add Letta Code detection and restore (herdrdev#3107)

refs herdrdev#3106

Co-authored-by: Cameron <cameron@pfiffer.org>

* fix(windows): disable host mouse reporting on terminal restore (herdrdev#4055)

Windows skipped the xterm mouse-reporting reset because clear_host_mouse_reporting was a no-op there. Standalone Git Bash (mintty) keeps SGR mouse reporting enabled after detach, so mouse motion leaks escape sequences into the shell. Emit the reset sequence on Windows too and include the X10 mode.

refs herdrdev#3748

* fix: keep hermes enabled list valid yaml (herdrdev#4056)

* fix: keep hermes enabled list valid yaml

refs herdrdev#3839

* fix: preserve hermes inline list quoting and comments

refs herdrdev#3839

* fix(ui): keep muted sidebar and tab labels readable (herdrdev#4062)

refs herdrdev#2692

* fix(windows): honor default_shell in login shell mode (herdrdev#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 herdrdev#1445

* fix(windows): add csh and tcsh to login shell flags

refs herdrdev#1445

* fix(windows): keep powershell cwd reporting in login mode (herdrdev#4065)

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 herdrdev#4060.

* fix: keep an agent name through a process-exit observation (herdrdev#3574)

A process-exit observation is not proof the agent is gone: the same
observation can be wrong while the agent keeps running, and the name is
the only handle its owner has on the pane. Detection uncertainty already
preserves the name, so releasing it on the observation alone contradicts
that contract and leaves a live agent unreachable by name with a rename
as the only recovery.

Free the name at the point the agent actually leaves the pane - a
recorded exit with no agent detected any more - so a wrong observation
costs nothing and a real exit still releases the name for reuse.

refs herdrdev#3225

Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: auto-expire worktree action error banners (herdrdev#4063)

* fix: auto-expire worktree action error banners

refs herdrdev#1827

* fix: restart endpoint error timeout on repeated errors

Route every transient client-side action error through a setter that installs a fresh deadline, so an identical repeated message cannot inherit an older, nearly-expired deadline.

refs herdrdev#1827

* test: derive endpoint error expiry assertions from stored deadline

refs herdrdev#1827

* test: deflake federated local input race (herdrdev#4066)

refs herdrdev#3670

* fix: recognize kitty CSI-u codepoints for F1 through F12 (herdrdev#2378)

Codepoints 57364-57375 (F1-F12) were missing from the kitty-protocol codepoint table, so terminals that send function keys as full CSI-u sequences (e.g. Ghostty, which enables the kitty keyboard protocol by default) had those keys silently dropped. Confirmed live against a running herdr session: injecting the raw F3/F4 codepoint sequences did nothing before this fix and correctly triggered previous_tab/next_tab after.

refs herdrdev#1809

Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix(input): accept parameterized CSI-tilde F1-F4 keys (herdrdev#4067)

Terminals such as foot send F1-F4 as parameterized CSI-tilde sequences (for
example F3 as `\x1b[13;1:1~`). `parse_xterm_modified_special_sequence` mapped
codes 15-24 but not 11-14, so these presses were dropped before keybindings
saw them. Map 11-14 to F1-F4, matching the unmodified `\x1b[11~`..`\x1b[14~`
aliases already accepted.

refs herdrdev#1809

* fix: clean up failed terminal observer setup (herdrdev#4071)

refs herdrdev#3612

* chore: add jjliebig as approved contributor and maintainer

* feat: install windows remote host packages (herdrdev#3687)

* feat: install windows remote host packages

refs herdrdev#3651

* docs: clarify windows saved reconnect package behavior

refs herdrdev#3651

* fix: include windows installer in nix sources

refs herdrdev#3651

* fix: preserve paths in windows scp transfers

refs herdrdev#3651

* refactor: isolate windows remote package setup

refs herdrdev#3651

* feat: add ctrl-hover highlighting and fix wrapped links (herdrdev#4074)

refs herdrdev#1282

* fix(linux): bound the foreground process-tree scan (herdrdev#3674)

refs herdrdev#3621

Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: keep plugins working after client-only updates (herdrdev#4077)

* ci: migrate remaining pinned actions to node24 (herdrdev#4023)

* ci: migrate remaining pinned actions to node24

actions/cache@v4 and mlugg/setup-zig@v2.2.1 still target Node 20, which
GitHub removes from runners on 2026-09-23. Bump actions/cache to v6.1.0 and
replace mlugg/setup-zig with vercel-labs/setup-zig@v1.0.2.

vercel-labs/setup-zig takes only `version`, so the use-cache/cache-size-limit
tuning and Zig global-cache reuse are dropped. See the PR for alternatives.

* ci: restore zig caching for vercel setup-zig

vercel-labs/setup-zig has no cache controls, so redirect the Zig global cache
into the workspace .zig-cache and run the Zig build cache step on Linux too.

Drop this commit if we switch to a setup action that owns Zig caching, e.g.
step-security/setup-zig or xyzzylabs/setup-zig.

* fix: clarify agent integration settings description

* fix: scope agent views to the selected machine (herdrdev#3784)

* fix: scope agent views to the selected machine

refs herdrdev#3732

* fix: preserve stable agent view ordering

refs herdrdev#3732

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix(windows): restore mouse reporting when reattaching (herdrdev#4080)

* fix(client): re-assert host mouse modes after a terminal reconnect (herdrdev#4079)

Co-authored-by: dengos <dengoswei@gmail.com>

* test(windows): deflake media player invalid media assertion (herdrdev#4082)

* test(windows): deflake media player invalid media assertion

The Windows sound test required MediaPlayer's MediaFailed event for invalid
media. Some runners never raise it, so the script exits through its playback
timer and the test saw "sound playback timed out" instead, panicking on the
MediaFailed assertion.

Accept either terminal error and keep the timer short under test via
HERDR_SOUND_TIMEOUT_SECONDS so a missed MediaFailed no longer waits out the
production 15 second bound.

* test(windows): keep media player timeout override test-only

Generate the short-timer player script only in tests instead of reading a
process environment override. This keeps the production script's fixed 15
second bound unchanged, and the script-content test now proves the test
variant rewrites the timer.

* fix: hide only kitty image placements overlapping overlays (herdrdev#4081)

* fix: clean up idle ssh bridges and back off flapping connections (herdrdev#4083)

* fix: clean up idle ssh bridges and back off flapping connections

* chore: keep ssh liveness smoke tooling local

* fix: preserve agent panel scroll when switching machines (herdrdev#4101)

refs herdrdev#3937

Root cause:
Activating another endpoint applies a snapshot with a different
endpoint-qualified graphics scope. This triggers reset_endpoint_projection,
which clears agent_scroll together with endpoint-local state even though
the agent list aggregates all machines. The selected agent receives focus,
but the list jumps back to the top.

Fix:
Save the client-owned agent scroll offset and restore it only when
activation changes endpoints. Keep same-endpoint reboot resets,
workspace/tab scroll resets, input cleanup and existing list bounds
clamping unchanged. This adds constant-time work on endpoint activation,
with no protocol, persisted-state or pane-scaled rendering changes.

Validation:
The new regression fails on the original implementation with scroll 0
instead of 6. Tests cover repeated bidirectional agent clicks, stable
visible rows, shrinking lists and same-machine reboots. All 242 client
shell tests pass.

Real SSH A/B testing against the same two disposable 0.9.0 servers
reproduces the reset with both the stable and current-master clients.
The patched client preserves the visible rows through six cross-machine
switches and two same-machine control clicks.

Native just ci passes: 3535 Rust tests, six default skips, plus lint,
maintenance, architecture and integration-asset checks. All seven docs
contract tests pass. Windows cross-lint was not run because its SDK is
not installed; native macOS and Windows UI behavior was not tested.

* fix: remove stale maintainer username

* fix: detect codex activity with static titles and queued inputs (herdrdev#4099)

* fix: detect codex activity with static titles and queued inputs

refs herdrdev#4092

Codex 0.154.0 can omit the activity bullet when tui.animations is false
and replace the literal Working label with a task-specific label. Its
terminal title can also remain static during an active turn, so the OSC
spinner rule is not sufficient to identify work.

The screen fallback required a bullet followed by Working in the last
three non-empty lines. It therefore missed both the animation-disabled
status line and dynamic labels. Queued follow-ups add a heading, input
text, and an edit hint below the activity line, moving that line outside
the three-line region. The lower-priority non-empty OSC title rule then
reported idle while Codex still showed an active timer and interrupt
control.

Recognize the elapsed-time and esc-to-interrupt controls in the detection
snapshot before the current composer. Allow an optional bullet, a dynamic
activity label, and queued follow-ups below that line. Require that no
later response, completion, or interruption marker supersedes the live
activity. This excludes composer text and stale output without letting an
earlier interruption suppress a subsequent active turn.

Keep OSC state precedence and transcript-viewer skip behavior unchanged.
Update the bundled and distributed manifests together to 2026.09.14.1.
Add regression coverage for optional bullets, dynamic labels, queued
inputs, resumed work after interruption, and stale or quoted controls.

Avoid discarding Regex search caches on every detection poll. The loader
previously cloned the entire compiled rule vector, and each Regex clone
created a fresh search cache pool despite sharing compiled read-only
state. Share the immutable rules through Arc instead. Reload publishes a
new array while in-flight readers retain a valid old generation, without
holding the global cache lock during matching. Add a cheap contains gate
for the required interrupt text before running the multiline expression.

Add a deterministic regression for compiled-rule sharing, reload
replacement, retained old readers, and concurrent detection. Measure the
baseline and optimized detector kernels in three alternating rounds on
CPU 24 with NUMA node 0 and CPU 25 with NUMA node 1. Real queued detection
falls from about 116 to 22 microseconds, and two evaluations per pane over
15 panes fall from about 3.45 to 0.65 milliseconds on both sockets, about
81 percent less time. No benchmark match results change. The cache
improvement requires the updated binary, not just a manifest reload.

Validate on the official Herdr 0.9.0 release with Codex 0.154.0 by toggling
the old and new manifests during the same active queued task. Confirm
working for active turns and idle after completion or interruption.
Pass 86 captured-screen and semantic checks through the optimized source
binary, 69 manifest/cache tests, CPU-pinned render scaling, and the full
native CI suite: 3535 Rust tests passed, with six default skips. Run CI
serially with inherited Herdr session context removed; no tests were
excluded or expectations relaxed to make CI pass.

* fix: detect codex pending follow-ups as working

refs herdrdev#4092

* fix: handle wrapped codex follow-up hints

refs herdrdev#4092

---------

Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>

* fix: detect pi working border (herdrdev#4113)

refs herdrdev#3629

* ci: restore zig local build cache reuse (herdrdev#4118)

* fix: default pane splits to the calling pane (herdrdev#4123)

* fix: restore saved machine screens after reconnect (herdrdev#4126)

refs herdrdev#3842

* fix: scope window titles to each client view (herdrdev#4130)

refs herdrdev#4091

* fix(windows): avoid lossy native Win32 input mode (herdrdev#4133)

refs herdrdev#3932
refs herdrdev#3702
refs herdrdev#3910

* fix: keep modified enter compatible with legacy panes (herdrdev#4110)

* fix: keep modified enter compatible with legacy panes

refs herdrdev#4103

* fix: synchronize modified key negotiation state

refs herdrdev#4103

* test: cover windows legacy modified enter

refs herdrdev#4103

---------

Co-authored-by: Jonathan Liebig <jonathan.liebig@gmail.com>

* fix: avoid resending terminal cells for metadata updates (herdrdev#4137)

* feat: add cursor-based editing to text inputs (herdrdev#3698)

* feat: add cursor-based editing to text inputs

refs herdrdev#1803

* fix: preserve dialog actions with associated text

refs herdrdev#1803

* refactor: simplify text editor outcomes and tests

refs herdrdev#1803

---------

Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: protect integration configs from failed writes (herdrdev#3973)

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: map pixel mouse by integer cell pitch (herdrdev#4140)

refs herdrdev#3295

* fix: reject stale pane surfaces after reconnect (herdrdev#4141)

* test: deflake recent CI failures (herdrdev#4139)

* fix(windows): batch input trace diagnostics (herdrdev#4146)

refs herdrdev#3932

* fix(windows): prefer VT input for SSH clients (herdrdev#4148)

refs herdrdev#3932

* fix: reveal the full last tab in overflowing tab strips (herdrdev#4152)

refs herdrdev#4151

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: reveal newly focused workspaces in the machines sidebar

* fix: propagate focused pane moves to attached clients (herdrdev#4159)

refs herdrdev#4153

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* test: wait for runtime readiness in integration tests (herdrdev#4168)

Root cause:
The handoff test starts an agent immediately after workspace creation,
before the new shell is necessarily ready. The reconnect test assumes a
fixed sidebar row and searches raw ANSI output for text that can arrive
as separate incremental cell updates.

Fix:
Wait for a shell builtin to write a readiness marker before sending the
single raw agent.start request. Exercise the gap with a delayed shell.
Locate the recovered workspace in the rendered sidebar and check the
reconstructed screen for selection and input, including overlay borders.
Do not extend existing timeouts or add retries that hide failures.

Validation:
All 25 affected group and helper tests pass. Ten repeated rounds of the
two runtime tests and the row helper pass, for 30 test executions without
retries. Standalone native just ci passes 3582 Rust tests with six default
skips, formatting, all-target Clippy, 106 maintenance tests, six architecture
tests and 39 integration-asset tests. Windows cross-lint was not run because
its SDK is not installed.

Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: reconcile client locations after no-focus pane moves (herdrdev#4171)

refs herdrdev#4153

Root cause:
A no-focus pane move can remove the source tab without changing the
server's numeric workspace and tab coordinates. PaneMove is not classified
as a topology change, so a client viewing that tab retains its deleted ID.
Its target lookup falls back to the server default and can follow a later,
unrelated focus change instead of staying in the source workspace.

Fix:
Include PaneMove in the existing client-location reconciliation path.
Extend the existing focus-guard test with a client on the removed tab and
verify that a later server-default change cannot move its view. Preserve
the other client's view and existing error, no-op and zoom assertions.

Validation:
The minimized regression fails five times on PR herdrdev#4159's unchanged head and
passes five times with the one-line fix. All 19 pane-move tests, native
lint, maintenance, architecture and integration-asset checks pass.
Full native just ci on the publication branch, including the unchanged
prerequisites from herdrdev#4159 and herdrdev#4168, passes 3585 Rust tests with six default
skips plus lint, 106 maintenance, six architecture and 39 integration-asset
tests. All seven docs contract tests pass. Local Windows cross-lint was
not run because its SDK is unavailable.

No geometry policy, protocol or periodic rendering behavior is changed.

* fix: preserve live selections during pane output (herdrdev#4193)

refs herdrdev#3841

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix(windows): preserve sgr mouse encoding after native capture (herdrdev#4195)

refs herdrdev#4155

* fix(windows): normalize pane launch cwd casing (herdrdev#4199)

* fix(windows): reject dynamic msvc runtime in packaging (herdrdev#4200)

The v0.8.2 Windows archive shipped an herdr.exe that imported
VCRUNTIME140.dll and the api-ms-win-crt-* Universal CRT, none of which the
archive provides. Static CRT linkage was restored for x86_64-pc-windows-msvc
in .cargo/config.toml, but nothing verified the packaged executable, so the
dependency could silently regress.

Parse the PE import and delay-import tables and reject herdr.exe when it
depends on the dynamic Microsoft C/C++ runtime, including release and debug
variants. Check the input while staging and re-check the staged executable
before archiving so a direct archive cannot bypass the guard.

refs herdrdev#3129

* fix: preserve unloaded sessions before replacement (herdrdev#4190)

refs herdrdev#4125

* fix: preserve macos services across logout (herdrdev#4202)

refs herdrdev#4100

* chore: approve vinayshah1998 as contributor

* fix: keep remote surface updates incremental (herdrdev#4209)

refs herdrdev#3769

refs herdrdev#3745

* fix: keep local usable when remote machines stall (herdrdev#4234)

refs herdrdev#3903

* fix: let local agent clicks cancel remote handoffs (herdrdev#4236)

refs herdrdev#3903

* fix: require exact session names before deletion (herdrdev#4233)

* fix: require exact session names before deletion

refs herdrdev#3819

* test: isolate session deletion regression tests

refs herdrdev#3819

---------

Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* test: wait for app readiness before late session injection (herdrdev#4224)

Root cause:
The API socket is bound before App evaluates session restore. Waiting only
for that socket lets the late-session test inject its file during startup,
so the server may load it normally instead of exercising late-file recovery.
The missing backup then fails an otherwise correct persistence assertion.

Fix:
Wait for a read-only workspace-list request to pass through App and assert
that the workspace list is still empty before injecting the session file.
Keep the existing autosave deadline and byte-for-byte backup assertion.
Add no sleeps, retries, longer timeouts or production changes.

Validation:
The original assertion fails with unchanged master. With the barrier, five
independent runs pass. Full native just ci passes 3624 Rust tests with six
default skips, formatting, all-target Clippy, 112 maintenance tests, six
architecture tests and 39 integration-asset tests. All seven docs contract
tests pass. Local Windows cross-lint was not run because its SDK is absent.

Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: offer forced removal for worktrees with submodules (herdrdev#4238)

refs herdrdev#1797

* ci: promote stable releases from protected previews (herdrdev#4241)

* fix: address release promotion review findings

* fix: avoid workstation sdk requirement in preview checks

* docs: publish preview documentation

* release: synchronize metadata for v0.9.1

* fix: respect opencode tui.json registrations (herdrdev#4250)

refs herdrdev#4240

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: cache saved machine connection metadata (herdrdev#4256)

refs herdrdev#4252

* fix(windows): preserve VT input during mouse refresh (herdrdev#4257)

refs herdrdev#4251

* fix(windows): restore terminal after setup errors (herdrdev#4261)

refs herdrdev#4251

* fix(windows): recover VT input without changing readers (herdrdev#4269)

refs herdrdev#4251

* fix: address review comments on #55

---------

Co-authored-by: Can Celik <ogulcancelik@gmail.com>
Co-authored-by: akbash <akbash@herdr.dev>
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
Co-authored-by: Kataoka Katsuki <49934462+kataokatsuki@users.noreply.github.com>
Co-authored-by: kataokatsuki <kataokatsuki@users.noreply.github.com>
Co-authored-by: Cameron <cameron@pfiffer.org>
Co-authored-by: caner-akca <94343893+caner-akca@users.noreply.github.com>
Co-authored-by: Kakumanu Ashok Reddy <kakumanuashokreddyias2024@gmail.com>
Co-authored-by: dengos <dengoswei@gmail.com>
Co-authored-by: Eric Yue <jiacheng.yue@foxmail.com>
Co-authored-by: Mark Jaquith <mark@jaquith.me>
Co-authored-by: kangal-bot <285672167+kangal-bot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: zuraffa-review[bot] <326983185+zuraffa-review[bot]@users.noreply.github.com>
kaushikgopal added a commit to kaushikgopal/herdr that referenced this pull request Sep 18, 2026
Absorbs upstream through v0.9.1 release metadata. Notable fixes:
stable release promotion from protected previews (herdrdev#4241), windows VT
input recovery and pwsh default shell (herdrdev#4269, herdrdev#4297), cursor-based
text input editing (herdrdev#3698), ctrl-hover highlighting and wrapped links
(herdrdev#4074), whole-word selection during drag (herdrdev#3969), client location
reconciliation after pane moves (herdrdev#4171, herdrdev#4159), saved machine screen
restore after reconnect (herdrdev#4126), overflowing tab strips reveal the
full last tab (herdrdev#4152), muted sidebar/tab label readability (herdrdev#4062),
machine connection metadata caching (herdrdev#4256), unloaded session
preservation (herdrdev#4190, herdrdev#4125), kitty CSI-u/CSI-tilde F-keys (herdrdev#2378,
herdrdev#4067), pixel mouse integer cell pitch (herdrdev#4140), ssh bridge cleanup
and backoff (herdrdev#4083), Letta Code detection (herdrdev#3107).

Conflicts resolved per FORK-CHANGELOG.md: justfile kept upstream's
test_release/test_windows_input additions plus the fork's
test_fork_changelog_check; tabs.rs took upstream's max_tab_scroll
rework and muted tab-label style, keeping the vertical tab strip
intact.
shawnkoh added a commit to enable-xyz/herdr that referenced this pull request Sep 18, 2026
* docs: publish release distribution for v0.9.0

* fix: reduce idle ssh cpu without dropping final output (herdrdev#3728)

* fix: stop polling idle ssh uploads on unix

* fix: preserve ssh output during idle bridge cancellation

* test: exercise active ssh upload cancellation

* docs: publish preview documentation

* fix: show ssh errors during machine setup (herdrdev#3733)

refs herdrdev#3731

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* chore: move marketplace worker to website repository

* fix: collapse worktree groups with saved machines (herdrdev#3781)

* fix: collapse worktree groups with saved machines

refs herdrdev#3778

* fix: scope worktree collapse by machine

refs herdrdev#3778

* fix: collapse machines without switching focus

refs herdrdev#3778

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>

* fix: support preinstalled windows remote hosts (herdrdev#3661)

* fix: support windows hosts in saved ssh machines

refs herdrdev#3651

* fix: keep windows ssh bridge writes blocking

refs herdrdev#3651

* fix: close saved windows ssh bootstrap input

refs herdrdev#3651

* refactor: simplify remote bridge helpers

refs herdrdev#3651

* refactor: isolate remote bridge platform behavior

refs herdrdev#3651

* fix: close captured ssh input under windows consoles

refs herdrdev#3651

* test: remove redundant remote command assertions

refs herdrdev#3651

refs herdrdev#3701

* test: handle framed ssh commands in machine setup

refs herdrdev#3651

---------

Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>

* fix: restore autofocus when creating worktrees (herdrdev#3787)

* fix: restore autofocus when creating worktrees

refs herdrdev#3766

* fix: let the client focus newly created worktrees

refs herdrdev#3766

* feat: navigate and highlight workspaces across machines (herdrdev#3755)

* fix: highlight workspace navigation with saved machines

refs herdrdev#3754

* feat: navigate workspaces across connected machines

refs herdrdev#3754

* feat: navigate workspaces across machines

refs herdrdev#3754

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>

* fix: avoid full-screen copies for retained client updates (herdrdev#3837)

refs herdrdev#3822

* fix: restore plugin focus events for client navigation (herdrdev#3850)

refs herdrdev#3824

* fix: retain double-click selections and make tests root-safe (herdrdev#3854)

refs herdrdev#3847

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: project public agent focus to attached clients (herdrdev#3764)

refs herdrdev#3760

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: restore connected session navigator tree glyphs (herdrdev#3861)

refs herdrdev#3843

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix(pi): support Windows session paths (herdrdev#3729)

refs herdrdev#3726

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: stop token selection at CJK punctuation (herdrdev#3708)

refs herdrdev#3706

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: keep spaces sidebar stable during resize (herdrdev#3862)

refs herdrdev#3817

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: accept session names after option terminator (herdrdev#3222)

refs herdrdev#3220

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix(linux): identify Herdr desktop notifications (herdrdev#3639)

refs herdrdev#3638

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix(windows): preserve legacy mouse reports over ssh (herdrdev#3742)

* fix(windows): preserve legacy mouse reports over ssh

refs herdrdev#3735

* fix(windows): preserve input after missing mouse release

refs herdrdev#3735

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: use host background for pane selection highlights (herdrdev#3894)

refs herdrdev#3798

* fix: preserve focused viewer geometry during activation (herdrdev#3746)

* fix: preserve focused viewer geometry during activation

refs herdrdev#3744

* fix: preserve geometry through activation sync

refs herdrdev#3744

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix(input): accept WezTerm control key reports (herdrdev#3592)

* fix(input): accept WezTerm control key reports

refs herdrdev#3589

* fix(input): preserve WezTerm tab and escape reports

refs herdrdev#3589

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: accept machine add options before the SSH target (herdrdev#3897)

refs herdrdev#3883

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: bound mouse selection repaint cadence (herdrdev#3901)

refs herdrdev#3895

* chore: upgrade libghostty and preserve terminal behavior (herdrdev#3906)

* feat: route cli commands to saved ssh machines (herdrdev#3918)

* feat: support conditional sidebar token hiding (herdrdev#3925)

* fix: keep terminal snapshots consistent while scrolling (herdrdev#3928)

refs herdrdev#3900

* fix: explain zig version requirement on build failure (herdrdev#3931)

* fix: preserve plugin registry symlinks (herdrdev#3927)

refs herdrdev#3922

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* ci: trigger nix updates after stable releases

* chore: add one-time windows cross-check setup

* test: make windows cross-check path assertions portable (herdrdev#3954)

* fix: recognize claude unicode spinner during active turns (herdrdev#3953)

refs herdrdev#3949

* fix: preserve local direct kitty graphics transport (herdrdev#3957)

refs herdrdev#3785

* fix: detect cline launchers and idle states (herdrdev#3959)

refs herdrdev#2396

* fix: flush Windows Codex paste burst before prompt submit (herdrdev#3961)

Codex's Windows input reader does not surface bracketed paste. It buffers
the prompt as a paste burst and rewrites a following Enter into a newline
until the burst idles out, so the previous size-based delay only reduced
the chance of losing the submit. Append a non-character key after the
paste so Codex flushes the burst synchronously, giving every prompt a
deterministic submit boundary.

refs herdrdev#3187

* fix: lint all targets on windows (herdrdev#3963)

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.

* fix(config): keep utf8 bom from breaking config reads and writes (herdrdev#3962)

* fix(config): keep utf8 bom from breaking config reads and writes

refs herdrdev#3678

* fix(config): only strip displaced boms when config is invalid

refs herdrdev#3678

* fix(config): strip only boms the toml parser rejects

refs herdrdev#3678

* fix(remote): show SSH authentication output (herdrdev#3609)

* fix(remote): show SSH authentication output

refs herdrdev#3606

* test(remote): stabilize SSH authentication output regression

refs herdrdev#3606

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* ci: reuse compiler state across validation runs (herdrdev#3956)

* ci: retain zig build caches across validation runs

* ci: reduce debug information in validation builds

* ci: build only the ghostty test target for scalar checks

* ci: measure debug profiles and remove duplicate macos caching

* ci: compare scalar build scopes on one runner

* ci: keep the zig compiler path stable across runs

* ci: run macos and windows lint alongside tests

* ci: inspect remaining zig cache misses

* ci: remove completed measurement probes

* ci: compare windows linkers without changing release builds

* ci: test linux incremental reuse and correct the linker probe

* ci: measure windows incremental state reuse

* test: verify incremental reuse after a source change

* ci: finalize measured compiler cache optimizations

* fix: preserve whole-word selection while dragging (herdrdev#3969)

refs herdrdev#1836

* docs: enable nesting in throwaway repro setup

* fix: opencode v2 lifecycle reporting (herdrdev#3757)

* fix: support opencode v2 lifecycle reporting

* fix: repair Japanese docs

* fix: ignore payload-less OpenCode events

* fix: harden opencode v2 integration install and reporting

Follow-ups on top of the V2 lifecycle support:

- create `cli.json` when OpenCode has no V1 TUI preferences (`tui.json` or
  `kv.json`) to migrate, instead of only registering into an existing file
- settle stalled socket attempts with a plain connect timer, and resend the
  latest lifecycle state after a failed delivery so the pane cannot get stuck
- resolve the OpenCode state directory from `XDG_STATE_HOME`
- remove the managed `herdr-opencode` directory on uninstall
- document failed executions reporting `blocked`, keep the integration test
  environment independent of an inherited `XDG_STATE_HOME`, and simplify
  `reconcileBlockers` to reassign its map

---------

Co-authored-by: Jonathan Liebig <jonathan.liebig@gmail.com>

* fix: suppress Windows dead-key fallback characters in remote panes (herdrdev#3972)

refs herdrdev#3948

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: preserve complete alternate-screen history reads (herdrdev#3979)

* fix: ignore nested OMP session reports (herdrdev#3994)

refs herdrdev#2593

* fix: finish windows ssh setup after bridge exit (herdrdev#3719)

refs herdrdev#3651

* fix: keep windows endpoint writes progressing (herdrdev#3721)

refs herdrdev#3651

* fix: batch queued endpoint input frames (herdrdev#4004)

refs herdrdev#3833

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: allow retrying partial worktree removal (herdrdev#3315)

refs herdrdev#3314

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: recognize codex composer sparkles (herdrdev#4015)

refs herdrdev#3988

* fix: honor space row gaps across machines (herdrdev#3740)

* fix: honor space row gaps across machines

refs herdrdev#3738

* test: cover scrolling with machine space gaps

refs herdrdev#3738

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: restore alternate-screen pane width (herdrdev#4016)

refs herdrdev#3329

* fix: update grok session after new (herdrdev#2683)

* fix: update grok session after new

refs herdrdev#2681

* fix: avoid powershell args shadowing

refs herdrdev#2681

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: deflake windows endpoint flush drain test (herdrdev#4021)

The polling peer drains at production's 2ms Windows read cadence, so a 1 MiB
frame took ~5.5s locally and exceeded the test's 10s flush deadline under CI
load. Use a 256 KiB frame, still well above the 64 KiB batch limit, and a 30s
deadline so the assertion no longer depends on CI scheduling.

* fix(linux): avoid blocking proc reads for wsl agents (herdrdev#2179)

Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: disconnect stalled terminal observers (herdrdev#4039)

* fix: order ui accent before nested config tables (herdrdev#2698)

* fix: order ui accent before nested config tables

refs herdrdev#2697

* test: reject duplicate ui accent examples

refs herdrdev#2697

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: decouple pixel mouse from pane graphics (herdrdev#4045)

refs herdrdev#3295

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: restrict claude session hooks to native sources (herdrdev#4046)

refs herdrdev#4018

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix(input): preserve super-modified pane keys (herdrdev#4044)

refs herdrdev#3710

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* test: relax windows docs snapshot timeout (herdrdev#4049)

Windows CI runs these documentation contract fixtures under variable load. A degraded runner pushed the preview and versions snapshot integration tests past their 30s ceiling, while a healthy run finishes in seconds. Keep 30s elsewhere and give Windows a 120s ceiling.

* fix: retain validated split mouse reports across idle gaps (herdrdev#4048)

refs herdrdev#3911

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix(server): transfer handoff descriptors in batches (herdrdev#3411)

Live handoff refused any session with more than 64 panes. The pane count
was checked twice against MAX_FDS_PER_HANDOFF, and the transfer itself put
every pane's pty master into one SCM_RIGHTS control message, so the guard
was the only thing keeping the send inside the kernel's per-message limit.
A session past the limit could only be updated by closing panes or by a
normal restart, which ends every pane process.

Send the descriptors in batches of 64 instead and drop both guards. The
receiving side accumulates across recvmsg calls until the expected count
arrives, bounds every SCM_RIGHTS payload it reads by the control bytes the
kernel returned, rejects a batch that carries more descriptors than it
asked for, and closes the descriptors it already holds on any failure. A
session of 64 panes or fewer still produces one batch, so the bytes on the
wire are unchanged and HANDOFF_VERSION stays at 1.

refs herdrdev#3393

Co-authored-by: kataokatsuki <kataokatsuki@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* test: deflake windows metadata ttl expiry assertion (herdrdev#4050)

The clear-only metadata test used a 1 ms ttl, so on loaded Windows runners set_agent_metadata could see the entry as expired, drop it, and recreate it with a None ttl before next_agent_metadata_expiry() ran. Expiry is forced explicitly later in the test, so use a long ttl to keep the no-extension assertion independent of wall-clock scheduling.

* fix: detect kimi npm shim on windows (herdrdev#4053)

refs herdrdev#3317

* fix(remote): resolve ssh host aliases from git bash on windows (herdrdev#4054)

Windows OpenSSH resolves drive-letter Include paths, but Git for Windows' MSYS OpenSSH does not, so the managed remote SSH config silently ignored the user's ~/.ssh/config when herdr ran under Git Bash. Include the user config through ~/.ssh/config, which both implementations expand to the same file.

refs herdrdev#3947

* feat: add Letta Code detection and restore (herdrdev#3107)

refs herdrdev#3106

Co-authored-by: Cameron <cameron@pfiffer.org>

* fix(windows): disable host mouse reporting on terminal restore (herdrdev#4055)

Windows skipped the xterm mouse-reporting reset because clear_host_mouse_reporting was a no-op there. Standalone Git Bash (mintty) keeps SGR mouse reporting enabled after detach, so mouse motion leaks escape sequences into the shell. Emit the reset sequence on Windows too and include the X10 mode.

refs herdrdev#3748

* fix: keep hermes enabled list valid yaml (herdrdev#4056)

* fix: keep hermes enabled list valid yaml

refs herdrdev#3839

* fix: preserve hermes inline list quoting and comments

refs herdrdev#3839

* fix(ui): keep muted sidebar and tab labels readable (herdrdev#4062)

refs herdrdev#2692

* fix(windows): honor default_shell in login shell mode (herdrdev#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 herdrdev#1445

* fix(windows): add csh and tcsh to login shell flags

refs herdrdev#1445

* fix(windows): keep powershell cwd reporting in login mode (herdrdev#4065)

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 herdrdev#4060.

* fix: keep an agent name through a process-exit observation (herdrdev#3574)

A process-exit observation is not proof the agent is gone: the same
observation can be wrong while the agent keeps running, and the name is
the only handle its owner has on the pane. Detection uncertainty already
preserves the name, so releasing it on the observation alone contradicts
that contract and leaves a live agent unreachable by name with a rename
as the only recovery.

Free the name at the point the agent actually leaves the pane - a
recorded exit with no agent detected any more - so a wrong observation
costs nothing and a real exit still releases the name for reuse.

refs herdrdev#3225

Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: auto-expire worktree action error banners (herdrdev#4063)

* fix: auto-expire worktree action error banners

refs herdrdev#1827

* fix: restart endpoint error timeout on repeated errors

Route every transient client-side action error through a setter that installs a fresh deadline, so an identical repeated message cannot inherit an older, nearly-expired deadline.

refs herdrdev#1827

* test: derive endpoint error expiry assertions from stored deadline

refs herdrdev#1827

* test: deflake federated local input race (herdrdev#4066)

refs herdrdev#3670

* fix: recognize kitty CSI-u codepoints for F1 through F12 (herdrdev#2378)

Codepoints 57364-57375 (F1-F12) were missing from the kitty-protocol codepoint table, so terminals that send function keys as full CSI-u sequences (e.g. Ghostty, which enables the kitty keyboard protocol by default) had those keys silently dropped. Confirmed live against a running herdr session: injecting the raw F3/F4 codepoint sequences did nothing before this fix and correctly triggered previous_tab/next_tab after.

refs herdrdev#1809

Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix(input): accept parameterized CSI-tilde F1-F4 keys (herdrdev#4067)

Terminals such as foot send F1-F4 as parameterized CSI-tilde sequences (for
example F3 as `\x1b[13;1:1~`). `parse_xterm_modified_special_sequence` mapped
codes 15-24 but not 11-14, so these presses were dropped before keybindings
saw them. Map 11-14 to F1-F4, matching the unmodified `\x1b[11~`..`\x1b[14~`
aliases already accepted.

refs herdrdev#1809

* fix: clean up failed terminal observer setup (herdrdev#4071)

refs herdrdev#3612

* chore: add jjliebig as approved contributor and maintainer

* feat: install windows remote host packages (herdrdev#3687)

* feat: install windows remote host packages

refs herdrdev#3651

* docs: clarify windows saved reconnect package behavior

refs herdrdev#3651

* fix: include windows installer in nix sources

refs herdrdev#3651

* fix: preserve paths in windows scp transfers

refs herdrdev#3651

* refactor: isolate windows remote package setup

refs herdrdev#3651

* feat: add ctrl-hover highlighting and fix wrapped links (herdrdev#4074)

refs herdrdev#1282

* fix(linux): bound the foreground process-tree scan (herdrdev#3674)

refs herdrdev#3621

Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: keep plugins working after client-only updates (herdrdev#4077)

* ci: migrate remaining pinned actions to node24 (herdrdev#4023)

* ci: migrate remaining pinned actions to node24

actions/cache@v4 and mlugg/setup-zig@v2.2.1 still target Node 20, which
GitHub removes from runners on 2026-09-23. Bump actions/cache to v6.1.0 and
replace mlugg/setup-zig with vercel-labs/setup-zig@v1.0.2.

vercel-labs/setup-zig takes only `version`, so the use-cache/cache-size-limit
tuning and Zig global-cache reuse are dropped. See the PR for alternatives.

* ci: restore zig caching for vercel setup-zig

vercel-labs/setup-zig has no cache controls, so redirect the Zig global cache
into the workspace .zig-cache and run the Zig build cache step on Linux too.

Drop this commit if we switch to a setup action that owns Zig caching, e.g.
step-security/setup-zig or xyzzylabs/setup-zig.

* fix: clarify agent integration settings description

* fix: scope agent views to the selected machine (herdrdev#3784)

* fix: scope agent views to the selected machine

refs herdrdev#3732

* fix: preserve stable agent view ordering

refs herdrdev#3732

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix(windows): restore mouse reporting when reattaching (herdrdev#4080)

* fix(client): re-assert host mouse modes after a terminal reconnect (herdrdev#4079)

Co-authored-by: dengos <dengoswei@gmail.com>

* test(windows): deflake media player invalid media assertion (herdrdev#4082)

* test(windows): deflake media player invalid media assertion

The Windows sound test required MediaPlayer's MediaFailed event for invalid
media. Some runners never raise it, so the script exits through its playback
timer and the test saw "sound playback timed out" instead, panicking on the
MediaFailed assertion.

Accept either terminal error and keep the timer short under test via
HERDR_SOUND_TIMEOUT_SECONDS so a missed MediaFailed no longer waits out the
production 15 second bound.

* test(windows): keep media player timeout override test-only

Generate the short-timer player script only in tests instead of reading a
process environment override. This keeps the production script's fixed 15
second bound unchanged, and the script-content test now proves the test
variant rewrites the timer.

* fix: hide only kitty image placements overlapping overlays (herdrdev#4081)

* fix: clean up idle ssh bridges and back off flapping connections (herdrdev#4083)

* fix: clean up idle ssh bridges and back off flapping connections

* chore: keep ssh liveness smoke tooling local

* fix: preserve agent panel scroll when switching machines (herdrdev#4101)

refs herdrdev#3937

Root cause:
Activating another endpoint applies a snapshot with a different
endpoint-qualified graphics scope. This triggers reset_endpoint_projection,
which clears agent_scroll together with endpoint-local state even though
the agent list aggregates all machines. The selected agent receives focus,
but the list jumps back to the top.

Fix:
Save the client-owned agent scroll offset and restore it only when
activation changes endpoints. Keep same-endpoint reboot resets,
workspace/tab scroll resets, input cleanup and existing list bounds
clamping unchanged. This adds constant-time work on endpoint activation,
with no protocol, persisted-state or pane-scaled rendering changes.

Validation:
The new regression fails on the original implementation with scroll 0
instead of 6. Tests cover repeated bidirectional agent clicks, stable
visible rows, shrinking lists and same-machine reboots. All 242 client
shell tests pass.

Real SSH A/B testing against the same two disposable 0.9.0 servers
reproduces the reset with both the stable and current-master clients.
The patched client preserves the visible rows through six cross-machine
switches and two same-machine control clicks.

Native just ci passes: 3535 Rust tests, six default skips, plus lint,
maintenance, architecture and integration-asset checks. All seven docs
contract tests pass. Windows cross-lint was not run because its SDK is
not installed; native macOS and Windows UI behavior was not tested.

* fix: remove stale maintainer username

* fix: detect codex activity with static titles and queued inputs (herdrdev#4099)

* fix: detect codex activity with static titles and queued inputs

refs herdrdev#4092

Codex 0.154.0 can omit the activity bullet when tui.animations is false
and replace the literal Working label with a task-specific label. Its
terminal title can also remain static during an active turn, so the OSC
spinner rule is not sufficient to identify work.

The screen fallback required a bullet followed by Working in the last
three non-empty lines. It therefore missed both the animation-disabled
status line and dynamic labels. Queued follow-ups add a heading, input
text, and an edit hint below the activity line, moving that line outside
the three-line region. The lower-priority non-empty OSC title rule then
reported idle while Codex still showed an active timer and interrupt
control.

Recognize the elapsed-time and esc-to-interrupt controls in the detection
snapshot before the current composer. Allow an optional bullet, a dynamic
activity label, and queued follow-ups below that line. Require that no
later response, completion, or interruption marker supersedes the live
activity. This excludes composer text and stale output without letting an
earlier interruption suppress a subsequent active turn.

Keep OSC state precedence and transcript-viewer skip behavior unchanged.
Update the bundled and distributed manifests together to 2026.09.14.1.
Add regression coverage for optional bullets, dynamic labels, queued
inputs, resumed work after interruption, and stale or quoted controls.

Avoid discarding Regex search caches on every detection poll. The loader
previously cloned the entire compiled rule vector, and each Regex clone
created a fresh search cache pool despite sharing compiled read-only
state. Share the immutable rules through Arc instead. Reload publishes a
new array while in-flight readers retain a valid old generation, without
holding the global cache lock during matching. Add a cheap contains gate
for the required interrupt text before running the multiline expression.

Add a deterministic regression for compiled-rule sharing, reload
replacement, retained old readers, and concurrent detection. Measure the
baseline and optimized detector kernels in three alternating rounds on
CPU 24 with NUMA node 0 and CPU 25 with NUMA node 1. Real queued detection
falls from about 116 to 22 microseconds, and two evaluations per pane over
15 panes fall from about 3.45 to 0.65 milliseconds on both sockets, about
81 percent less time. No benchmark match results change. The cache
improvement requires the updated binary, not just a manifest reload.

Validate on the official Herdr 0.9.0 release with Codex 0.154.0 by toggling
the old and new manifests during the same active queued task. Confirm
working for active turns and idle after completion or interruption.
Pass 86 captured-screen and semantic checks through the optimized source
binary, 69 manifest/cache tests, CPU-pinned render scaling, and the full
native CI suite: 3535 Rust tests passed, with six default skips. Run CI
serially with inherited Herdr session context removed; no tests were
excluded or expectations relaxed to make CI pass.

* fix: detect codex pending follow-ups as working

refs herdrdev#4092

* fix: handle wrapped codex follow-up hints

refs herdrdev#4092

---------

Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>

* fix: detect pi working border (herdrdev#4113)

refs herdrdev#3629

* ci: restore zig local build cache reuse (herdrdev#4118)

* fix: default pane splits to the calling pane (herdrdev#4123)

* fix: restore saved machine screens after reconnect (herdrdev#4126)

refs herdrdev#3842

* fix: scope window titles to each client view (herdrdev#4130)

refs herdrdev#4091

* fix(windows): avoid lossy native Win32 input mode (herdrdev#4133)

refs herdrdev#3932
refs herdrdev#3702
refs herdrdev#3910

* fix: keep modified enter compatible with legacy panes (herdrdev#4110)

* fix: keep modified enter compatible with legacy panes

refs herdrdev#4103

* fix: synchronize modified key negotiation state

refs herdrdev#4103

* test: cover windows legacy modified enter

refs herdrdev#4103

---------

Co-authored-by: Jonathan Liebig <jonathan.liebig@gmail.com>

* fix: avoid resending terminal cells for metadata updates (herdrdev#4137)

* feat: add cursor-based editing to text inputs (herdrdev#3698)

* feat: add cursor-based editing to text inputs

refs herdrdev#1803

* fix: preserve dialog actions with associated text

refs herdrdev#1803

* refactor: simplify text editor outcomes and tests

refs herdrdev#1803

---------

Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: protect integration configs from failed writes (herdrdev#3973)

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

* fix: atomically write integration configs

refs herdrdev#3970

---------

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: map pixel mouse by integer cell pitch (herdrdev#4140)

refs herdrdev#3295

* fix: reject stale pane surfaces after reconnect (herdrdev#4141)

* test: deflake recent CI failures (herdrdev#4139)

* fix(windows): batch input trace diagnostics (herdrdev#4146)

refs herdrdev#3932

* fix(windows): prefer VT input for SSH clients (herdrdev#4148)

refs herdrdev#3932

* fix: reveal the full last tab in overflowing tab strips (herdrdev#4152)

refs herdrdev#4151

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix: reveal newly focused workspaces in the machines sidebar

* fix: propagate focused pane moves to attached clients (herdrdev#4159)

refs herdrdev#4153

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* test: wait for runtime readiness in integration tests (herdrdev#4168)

Root cause:
The handoff test starts an agent immediately after workspace creation,
before the new shell is necessarily ready. The reconnect test assumes a
fixed sidebar row and searches raw ANSI output for text that can arrive
as separate incremental cell updates.

Fix:
Wait for a shell builtin to write a readiness marker before sending the
single raw agent.start request. Exercise the gap with a delayed shell.
Locate the recovered workspace in the rendered sidebar and check the
reconstructed screen for selection and input, including overlay borders.
Do not extend existing timeouts or add retries that hide failures.

Validation:
All 25 affected group and helper tests pass. Ten repeated rounds of the
two runtime tests and the row helper pass, for 30 test executions without
retries. Standalone native just ci passes 3582 Rust tests with six default
skips, formatting, all-target Clippy, 106 maintenance tests, six architecture
tests and 39 integration-asset tests. Windows cross-lint was not run because
its SDK is not installed.

Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: reconcile client locations after no-focus pane moves (herdrdev#4171)

refs herdrdev#4153

Root cause:
A no-focus pane move can remove the source tab without changing the
server's numeric workspace and tab coordinates. PaneMove is not classified
as a topology change, so a client viewing that tab retains its deleted ID.
Its target lookup falls back to the server default and can follow a later,
unrelated focus change instead of staying in the source workspace.

Fix:
Include PaneMove in the existing client-location reconciliation path.
Extend the existing focus-guard test with a client on the removed tab and
verify that a later server-default change cannot move its view. Preserve
the other client's view and existing error, no-op and zoom assertions.

Validation:
The minimized regression fails five times on PR herdrdev#4159's unchanged head and
passes five times with the one-line fix. All 19 pane-move tests, native
lint, maintenance, architecture and integration-asset checks pass.
Full native just ci on the publication branch, including the unchanged
prerequisites from herdrdev#4159 and herdrdev#4168, passes 3585 Rust tests with six default
skips plus lint, 106 maintenance, six architecture and 39 integration-asset
tests. All seven docs contract tests pass. Local Windows cross-lint was
not run because its SDK is unavailable.

No geometry policy, protocol or periodic rendering behavior is changed.

* fix: preserve live selections during pane output (herdrdev#4193)

refs herdrdev#3841

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>

* fix(windows): preserve sgr mouse encoding after native capture (herdrdev#4195)

refs herdrdev#4155

* fix(windows): normalize pane launch cwd casing (herdrdev#4199)

* fix(windows): reject dynamic msvc runtime in packaging (herdrdev#4200)

The v0.8.2 Windows archive shipped an herdr.exe that imported
VCRUNTIME140.dll and the api-ms-win-crt-* Universal CRT, none of which the
archive provides. Static CRT linkage was restored for x86_64-pc-windows-msvc
in .cargo/config.toml, but nothing verified the packaged executable, so the
dependency could silently regress.

Parse the PE import and delay-import tables and reject herdr.exe when it
depends on the dynamic Microsoft C/C++ runtime, including release and debug
variants. Check the input while staging and re-check the staged executable
before archiving so a direct archive cannot bypass the guard.

refs herdrdev#3129

* fix: preserve unloaded sessions before replacement (herdrdev#4190)

refs herdrdev#4125

* fix: preserve macos services across logout (herdrdev#4202)

refs herdrdev#4100

* chore: approve vinayshah1998 as contributor

* fix: keep remote surface updates incremental (herdrdev#4209)

refs herdrdev#3769

refs herdrdev#3745

* fix: keep local usable when remote machines stall (herdrdev#4234)

refs herdrdev#3903

* fix: let local agent clicks cancel remote handoffs (herdrdev#4236)

refs herdrdev#3903

* fix: require exact session names before deletion (herdrdev#4233)

* fix: require exact session names before deletion

refs herdrdev#3819

* test: isolate session deletion regression tests

refs herdrdev#3819

---------

Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* test: wait for app readiness before late session injection (herdrdev#4224)

Root cause:
The API socket is bound before App evaluates session restore. Waiting only
for that socket lets the late-session test inject its file during startup,
so the server may load it normally instead of exercising late-file recovery.
The missing backup then fails an otherwise correct persistence assertion.

Fix:
Wait for a read-only workspace-list request to pass through App and assert
that the workspace list is still empty before injecting the session file.
Keep the existing autosave deadline and byte-for-byte backup assertion.
Add no sleeps, retries, longer timeouts or production changes.

Validation:
The original assertion fails with unchanged master. With the barrier, five
independent runs pass. Full native just ci passes 3624 Rust tests with six
default skips, formatting, all-target Clippy, 112 maintenance tests, six
architecture tests and 39 integration-asset tests. All seven docs contract
tests pass. Local Windows cross-lint was not run because its SDK is absent.

Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>

* fix: offer forced removal for worktrees with submodules (herdrdev#4238)

refs herdrdev#1797

* ci: promote stable releases from protected previews (herdrdev#4241)

* fix: address release promotion review findings

* fix: avoid workstation sdk requirement in preview checks

* docs: prepare 0.9.1 release documentation

* release: v0.9.1

* ci: fetch upstream release tags for documentation checks

* test: remove obsolete hidden-client help assertion

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Can Celik <ogulcancelik@gmail.com>
Co-authored-by: kangal-bot <285672167+kangal-bot@users.noreply.github.com>
Co-authored-by: akbash <akbash@herdr.dev>
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
Co-authored-by: Mark Jaquith <mark.jaquith@gusto.com>
Co-authored-by: Rhett CfZhuang <dark.momo985@gmail.com>
Co-authored-by: Kataoka Katsuki <49934462+kataokatsuki@users.noreply.github.com>
Co-authored-by: kataokatsuki <kataokatsuki@users.noreply.github.com>
Co-authored-by: Cameron <cameron@pfiffer.org>
Co-authored-by: caner-akca <94343893+caner-akca@users.noreply.github.com>
Co-authored-by: Kakumanu Ashok Reddy <kakumanuashokreddyias2024@gmail.com>
Co-authored-by: dengos <dengoswei@gmail.com>
Co-authored-by: Eric Yue <jiacheng.yue@foxmail.com>
Co-authored-by: Mark Jaquith <mark@jaquith.me>
@leonardschneider

Copy link
Copy Markdown

Implementation and testing were performed with Letta Code.

We tested registry draft 47bd7d61 on macOS arm64 with
Node 26 and our locally patched Letta 0.33.1, using isolated named sessions.

We could still reproduce both Herdr-side cases discussed earlier:

  • With --disable-memory-guard, Letta displayed its interactive UI and emitted its
    startup hook, but Herdr's agent startup timed out.
  • Without that flag, startup was recognized. /new emitted the new conversation ID,
    but Herdr retained the previous default-conversation reference.

Our local Herdr adjustments made both cases pass. Default → new A → new B → resume A
then produced exactly four hook events, with Herdr tracking each identity in the same
Letta process. We repeated this with and without the flag.

The two fixes and regression coverage are isolated in
commit d6ab6f7e
in our fork, based directly on 47bd7d61. Its source tree matches the tested candidate.

maciejul pushed a commit to maciejul/herdr-supercharged that referenced this pull request Oct 28, 2026
refs herdrdev#3106

Co-authored-by: Cameron <cameron@pfiffer.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-review Trigger automated AI reviews for pull requests admitted by the PR gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants