Summary
The tmux-backed subagent tool can remain in running forever without ever starting the child Pi process when the user's interactive shell has a background job.
I hit this consistently with Fish and the Hydro prompt, which starts an asynchronous git-status job from its fish_prompt handler. The trigger is shell-specific, but the underlying launch race is general: the extension creates an interactive shell and immediately types an exec ... command into it.
What happens
-
tmux new-session starts the user's interactive shell.
-
Hydro starts an asynchronous prompt job.
-
The extension uses send-keys to submit exec env ... pi ....
-
Fish refuses the first exec while a background job exists and prints:
A second attempt to exit will terminate them.
Use 'disown PID' to remove jobs from the list without terminating them.
-
Fish returns to its prompt, so the pane remains alive, but child Pi never starts and never writes result.json.
-
The parent polling loop sees neither a result nor a dead pane and waits indefinitely.
The failed run directories contain task.md and an empty session/ directory, with no result.json, confirming that this happens before child Pi starts.
Minimal reproduction
The same behavior can be reproduced without Pi by creating a detached tmux session under Fish/Hydro, then immediately using send-keys to submit an exec command. The command is refused and its marker file is not created. Repeating the command a second time succeeds, matching Fish's warning.
Proposed fix
Launch the pane command directly after setting remain-on-exit, rather than injecting it into an interactive shell:
tmux respawn-pane -k -c <cwd> -t <target> <child-command>
This removes the interactive-shell startup race while preserving attachability, remain-on-exit, and dead-pane detection. I have a small tested patch and will open a PR referencing this issue.
Environment
- Pi 0.83.0
- tmux 3.6b
- Fish 4.0.2
- Hydro prompt
- macOS
agent-stuff commit d265b8e
Summary
The tmux-backed
subagenttool can remain inrunningforever without ever starting the child Pi process when the user's interactive shell has a background job.I hit this consistently with Fish and the Hydro prompt, which starts an asynchronous git-status job from its
fish_prompthandler. The trigger is shell-specific, but the underlying launch race is general: the extension creates an interactive shell and immediately types anexec ...command into it.What happens
tmux new-sessionstarts the user's interactive shell.Hydro starts an asynchronous prompt job.
The extension uses
send-keysto submitexec env ... pi ....Fish refuses the first
execwhile a background job exists and prints:Fish returns to its prompt, so the pane remains alive, but child Pi never starts and never writes
result.json.The parent polling loop sees neither a result nor a dead pane and waits indefinitely.
The failed run directories contain
task.mdand an emptysession/directory, with noresult.json, confirming that this happens before child Pi starts.Minimal reproduction
The same behavior can be reproduced without Pi by creating a detached tmux session under Fish/Hydro, then immediately using
send-keysto submit anexeccommand. The command is refused and its marker file is not created. Repeating the command a second time succeeds, matching Fish's warning.Proposed fix
Launch the pane command directly after setting
remain-on-exit, rather than injecting it into an interactive shell:This removes the interactive-shell startup race while preserving attachability,
remain-on-exit, and dead-pane detection. I have a small tested patch and will open a PR referencing this issue.Environment
agent-stuffcommitd265b8e