Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions bin/backends/herdr.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1652,15 +1652,28 @@ fm_backend_herdr_projection_order_best_effort() { # <session> <created-workspac
# call. The server outlives its launcher and passes its startup environment to
# every later pane, so remove home, harness identity, and supervision selection
# inherited from whichever agent happened to start it. Bounded poll for the
# server to report running.
# server to report running. Where setsid(1) is available, detach into a new
# POSIX session so Herdr advertises detached_server_daemon for saved machines.
# Keep the existing launch on systems without that utility.
fm_backend_herdr_server_ensure() { # <session>
local session=$1 running out i
local session=$1 running out i client_bin
running=$(fm_backend_herdr_cli "$session" status --json 2>/dev/null | jq -r '.server.running // false' 2>/dev/null)
[ "$running" = "true" ] && return 0
(
unset FM_HOME FM_ROOT_OVERRIDE FM_STATE_OVERRIDE FM_DATA_OVERRIDE FM_PROJECTS_OVERRIDE FM_CONFIG_OVERRIDE \
CURSOR_AGENT CURSOR_INVOKED_AS CLAUDECODE PI_CODING_AGENT FM_PI_HARNESS GROK_AGENT FM_SUPERVISION_MODEL
fm_backend_herdr_cli "$session" server >/dev/null 2>&1 &
if command -v setsid >/dev/null 2>&1; then
# Match fm_backend_herdr_cli's session-scoped client selection. setsid
# needs an executable, not that shell function; util-linux forks if the
# launcher is already a process-group leader (setsid(2) would fail).
client_bin=herdr
if [ "${FM_BACKEND_HERDR_CLIENT_SESSION:-}" = "$session" ]; then
client_bin=$(fm_backend_herdr_bin)
fi
HERDR_SESSION="$session" setsid "$client_bin" server --session "$session" >/dev/null 2>&1 &
else
fm_backend_herdr_cli "$session" server >/dev/null 2>&1 &
fi
) || return 1
for i in $(seq 1 20); do
running=$(fm_backend_herdr_cli "$session" status --json 2>/dev/null | jq -r '.server.running // false' 2>/dev/null)
Expand Down
5 changes: 4 additions & 1 deletion docs/herdr-backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,13 @@ Workspace and tab ids support verification and cleanup but are not inferred from
## Current transport behavior

The adapter starts and polls a named server before workspace, tab, pane, or agent calls.
Every Herdr invocation goes through `fm_backend_herdr_cli`, which sets the environment and passes an explicit trailing `--session <name>`.
Every Herdr invocation except the detached server launch below goes through `fm_backend_herdr_cli`, which sets the environment and passes an explicit trailing `--session <name>`.
An environment variable alone is not reliable when another Herdr server is running.
When the selected named server is not running, the adapter launches it without inherited Firstmate home and directory overrides, harness identity markers, or the supervision-model override.
Herdr passes its server startup environment to every later pane, so retaining those values could misroute panes for another Firstmate home or harness.
Where `setsid(1)` is available, that launch detaches the server into its own POSIX session: Herdr computes `capabilities.detached_server_daemon` once at server start from whether the process leads its own session, and a server without it refuses saved machines.
Because `setsid` needs an executable rather than the CLI helper, the detached launch runs the client this session already selected, with the same explicit `--session <name>` routing.
A host without that utility, such as macOS, keeps the plain background launch through the helper.
An already-running server is reused without restart or environment changes.
Explicit named-session routing and unrelated launch environment remain intact.

Expand Down
10 changes: 10 additions & 0 deletions tests/fm-backend-herdr.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ case "${1:-}" in
printf '%s=%s\n' "$name" "$value"
done
printf 'args=%s\n' "$*"
if command -v setsid >/dev/null 2>&1; then
ps -o sid=,pid= -p "$$" | awk '{printf "sid=%s pid=%s\n", $1, $2}'
fi
} > "$FM_HERDR_SERVER_ENV_LOG"
: > "$FM_HERDR_SERVER_MARKER"
;;
Expand Down Expand Up @@ -1101,6 +1104,13 @@ test_server_ensure_scrubs_home_and_harness_identity() {
assert_contains "$output" "FM_HERDR_SENTINEL=kept" "server_ensure removed an unrelated environment variable"
assert_contains "$output" "HERDR_SESSION=fmtest" "server_ensure lost explicit Herdr session routing"
assert_contains "$output" "args=server --session fmtest" "server_ensure lost the trailing Herdr session flag"
if command -v setsid >/dev/null 2>&1; then
assert_contains "$output" "sid=" "server_ensure did not record the server's POSIX session"
local sid pid
sid=$(printf '%s\n' "$output" | sed -n 's/^sid=\([0-9]*\) pid=[0-9]*$/\1/p')
pid=$(printf '%s\n' "$output" | sed -n 's/^sid=[0-9]* pid=\([0-9]*\)$/\1/p')
[ -n "$pid" ] && [ "$sid" = "$pid" ] || fail "server_ensure launched the server outside its own POSIX session (sid=$sid pid=$pid)"
fi
pass "fm_backend_herdr_server_ensure: scrubs home and harness identity without disturbing unrelated environment or session routing"
}

Expand Down
Loading