Skip to content

fix(agent): agent notifications and Herdr state in the Docker sandbox - #63

Merged
radim10 merged 4 commits into
masterfrom
fix/agent-sandbox-terminal-notifications
Oct 8, 2026
Merged

radim10 merged 4 commits into
masterfrom
fix/agent-sandbox-terminal-notifications

Conversation

@radim10

@radim10 radim10 commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Agents in a Docker-sandboxed run sent no desktop notifications. Claude Code and Codex pick a notification method (OSC 9/777/99 or the bell) from TERM_PROGRAM, but only TERM and COLORTERM reached the container. TERM_PROGRAM, TERM_PROGRAM_VERSION and LC_TERMINAL are now forwarded too. The escape sequences already pass through docker run -t, so whatever wraps the pane (Ghostty, iTerm2, kitty, cmux, herdr, tmux) gets the same signals as outside the sandbox. Profile env vars still win.
  • Herdr showed no agent state for a sandboxed run. Herdr identifies an agent by the pane's foreground process, which here is the host docker CLI. Inside a Herdr pane (HERDR_ENV=1) the docker process now gets HERDR_AGENT=<agent>, Herdr's documented hint for wrappers, so it applies that agent's screen rules. A HERDR_AGENT the user already exported is kept.
  • Docs. The README's Docker section and a new docs/sandboxing.md section ("Notifications, herdr and cmux") say this works with no setup, list the forwarded vars and the HERDR_AGENT override, and explain why the apps' own hook integrations don't run in the container.

Safety

  • Only the terminal's name and version reach the container. No Herdr socket, pane ID or other host state is forwarded; the Herdr socket would let the agent drive other panes.
  • HERDR_AGENT is set on the host docker process after the container's -e args are built, so it never reaches the container.
  • The agent already had a direct tty, so it could send any escape sequence before this change. Knowing the terminal's name adds little.

Test plan

  • Unit tests for the forwarded terminal vars (including profile override) and for the HERDR_AGENT hint (inside/outside Herdr, user-exported value kept)
  • cargo fmt --check passes
  • Full cargo test passes (533 + 25 + 33)
  • Checked by hand: herdr shows the sandboxed agent's state; cmux shows notifications for both Claude Code and Codex

Agents in a Docker-sandboxed run sent no desktop notifications when a
turn finished or they needed input: only TERM and COLORTERM reached the
container, and Claude Code and Codex choose OSC 9/777/99 or the bell from
TERM_PROGRAM. TERM_PROGRAM, TERM_PROGRAM_VERSION and LC_TERMINAL are now
forwarded too, so the terminal or multiplexer wrapping the pane gets the
same signals as outside the sandbox. Profile env vars still win.
Herdr identifies an agent by the pane's foreground process, which for a
Docker-sandboxed run is the host docker CLI, so it showed no agent state.
Inside a Herdr pane the docker process now gets HERDR_AGENT=<agent>,
Herdr's documented hint for wrappers. It is set only on the host process,
never in the container, and a HERDR_AGENT the user exported is kept.
A HERDR_AGENT set by the profile (an env var or binding) is applied to
the host docker process, but the inferred hint replaced it, so Herdr
could use the wrong agent's detection rules. The hint is now skipped when
env_vars already has HERDR_AGENT, as it already was for one the user
exported. Also moves herdr_agent_hint above the doc comment of
codex_args_forcing_full_access, which it had split off.
…r sandbox

The README's Docker section and a new docs/sandboxing.md section say
that cmux, herdr and other terminals get agent notifications in the
sandbox with no setup, and that herdr also shows the agent's state.
The section lists the forwarded terminal vars, the HERDR_AGENT
override, and why the apps' own hook integrations don't run in the
container.
@radim10 radim10 self-assigned this Oct 8, 2026
@radim10
radim10 merged commit f4d6820 into master Oct 8, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant