* 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>
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 #3187
* feat: make ui.pane_borders a three-state auto/always/off mode
Legacy booleans keep parsing (true = auto, false = off); "always" frames
a lone pane while pane_outer_borders is enabled.
* fix: align pane border reload test and documentation
---------
Co-authored-by: Rudolf Schmidt <me@rudionrails.com>
Co-authored-by: Jonathan Liebig <jonathan.liebig@gmail.com>
* feat(detect): add muse agent with generic Pick blocked detection
Add Muse agent manifests and runtime detection for idle/working/blocked
states. Blocked Pick is generic on footer 'Enter to select' so any
question title/message triggers, fixing missed waiting answer where
previous AND-gated rule required specific hint combos. Cover process
names muse, muse-bin, muse-code variants and YOLO footer.
refs #herdr
* fix(detect): tighten muse manifest and agent lookup
- manifests: change working_esc_interrupt and blocked_approval
from whole_recent to bottom_non_empty_lines(8) to use captured
bottom-buffer controls instead of incidental whole-pane text
- manifests: anchor idle_prompt line_regex to ^\s*⟩\s*$ so working
chrome "⟩ ..." does not match idle
- runtime: restrict muse lookup to explicit aliases
(muse|muse-bin|muse-code|muse-cli), remove starts_with("muse")
fallback that misclassified museum/muse-helper/muser; add
positive/negative identification tests
- keep website mirror identical; verified with agent_detection
manifest check --require-website
refs #2489
refs #2456
* fix(detect): address mjdouglas vendor findings for muse
- manifests: widen pick_request_blocked to bottom_non_empty_lines(8) and add
Enter to toggle for multi-select; add menu_request_blocked for user-opened
pickers (enter confirm/esc go back/esc close/space toggle); add
workspace_trust_blocked for Do you trust dialog; tighten blocked_approval
to footer chrome Allow once/Always allow/Yes proceed; split idle_prompt
into any gate for empty and typed draft and not-gate pick/menu anchors;
change idle_status_fallback from YOLO to model \u00b7 effort \u00b7 cwd shape
- runtime: replace bare muse-bin alias with muse-bin-<version> digit check
to match versioned launcher binary and avoid museum/muse-binary false
positives
- cross-checked with vendor-side captures reported on PR #2489 against
strings in shipped muse-bin binary
refs #2489
* fix(detect): handle path-qualified muse-bin versioned binary
lookup_agent now uses path_basename before matching so a launcher
exec with absolute argv0 like /usr/local/lib/muse/muse-bin-0.1.0-R708.1
is correctly identified. is_muse_versioned_binary also checks basename
so direct path-qualified names succeed without relying on the
argv0_argv fallback path. Prevents missed Muse detection when screen
state and sound overrides are not applied.
refs #2489
* fix(detect): add enter save to muse menu detection
Evidence documents /theme with enter save footer which was not
matched. Add enter save to menu_request_blocked any list and to
idle_prompt not-gate (highlighted row ⟩ <choice> shares glyph) and
keep website mirror in sync. Update evidence comment counts from
four to five.
refs #2489
* docs: add Muse to supported agents table
Muse detection (manifest, process identification, sound)
was added earlier but docs/next agents table omitted the row.
Add Muse as screen manifest / none (no integration hook,
consistent with Amp/Kiro/Maki) to en/ja/zh-cn translations.
* fix(detect): resize agent arrays for new upstream variant
Agent::ALL and SCREEN_MANIFEST_AGENTS gained an entry from master
but kept stale size annotations, breaking compilation after rebase.
refs #2489
* docs: add muse detection entry to next changelog
refs #2489
* docs: add muse sound override to config reference
refs #2489
* fix(detect): prevent false muse blocked states
refs #2489
---------
Co-authored-by: Omer Hamid Kamisli <ohkamisli@poikus.com>
Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>
* feat: add per-mode theme overrides
allow [theme.custom.light] and [theme.custom.dark] blocks so custom
overrides can differ per resolved appearance when auto_switch flips.
precedence: theme defaults, then [theme.custom], then the block for
the active mode. configs without the new blocks resolve unchanged.
refs #837
* docs: complete per-mode theme documentation
---------
Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>
* feat: keep the outer terminal window title in sync with the session
Herdr emulates the terminals in its panes, so an OSC 0/OSC 2 title
written inside a pane stops at Herdr and never reaches the terminal
Herdr itself runs in. That outer title is what window managers read for
title bars, tab bars, and group bars, so it kept showing whatever the
shell or ssh happened to leave behind.
Add `ui.window_title`, rendered from {hostname}, {workspace}, {tab},
{pane}, and {terminal_title}, and push it to the foreground client
whenever it changes. It renders on the server, so {hostname} names the
machine the panes actually run on rather than the machine a thin remote
client runs on, and it is gated on a pending render so an idle loop
never pays for it.
A title is only remembered as delivered once a foreground client takes
it, so the first client to attach is written to rather than skipped.
`client.window_title.set` still wins over the configured title, and
clearing it now hands the title back to `ui.window_title` instead of
only "herdr".
* fix: deliver the outer window title to a newly attached client
ClientConnected assigns the foreground client directly rather than going
through promote_client_to_foreground, so clearing the sent-title cache
there missed the case that matters most: attaching a second terminal to a
running session. The title was usually unchanged, so the sync returned
early and the new terminal kept whatever its shell or ssh had left.
Key the cache on the client that received the title instead of relying on
every foreground assignment to invalidate it.
* perf: keep hidden pane output off the window title path
Output from a hidden or background pane sets needs_render without setting
needs_full_render, and the retained render plan then skips presentation
for it entirely. Syncing the title on needs_render meant every coalesced
hidden-output tick still formatted, sanitized, and allocated a title that
could not have changed, against the hidden-source early exit AGENTS.md
requires.
Every input to the title is app state, which always requests a full
render, so gate on that instead. The one exception is the focused pane's
own terminal title, which arrives through PTY parsing, so ask for a full
render when that changes and the configured title uses it.
* fix: only cache a window title a client writer received
* fix: carry an api set window title across a live handoff
* fix: make outer window title updates event-driven
---------
Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>
* feat: add move tab keybind actions
Add optional keys.move_tab_previous/move_tab_next actions that reorder
the active tab one position, wrapping at either end. Reuses the
existing tab.move runtime path that mouse drag reordering already
drives.
* fix: exit navigate mode on single-tab move attempts
* feat: show a zoom indicator in the desktop tab bar
Reserve the right edge of the tab row for a ZOOM pill while the
focused pane is zoomed, matching the accent style of the mode bars.
The per-tab Z suffix stays; the pill makes the zoomed state visible
at a glance like tmux's status-right flag.
* feat: optionally show the hostname in the desktop tab bar
Add ui.tab_bar_hostname to display the machine's hostname at the right
edge of the tab row, like tmux's #h in status-right. The value resolves
where the server renders, so remote sessions show the remote host. Off
by default.
* fix: strip control characters from the tab bar hostname
* fix: hide the hostname when it would squeeze out the tab strip
* feat: add configurable tab bar status
* fix: harden tab bar status updates
* fix: terminate tab bar status process trees
* fix: disable status commands on unsupported platforms
* fix: skip unchanged status command renders
* fix: keep tab bar status opt-in by default
---------
Co-authored-by: David Heinemeier Hansson <david@hey.com>