fix(shell): stop the agent editing local files while the terminal is on a remote host - #242
Merged
Merged
Conversation
…on a remote host
The Shell tab has two tool families pointing at different machines. The PTY
tools (run_command, shell_input, shell_read) act on whatever the terminal is
in; the file tools (fs_read_text, fs_write_text, fs_edit_text, code_grep,
code_glob) always act on the local filesystem. They agree only while the
terminal sits at a local prompt.
The moment the user runs `ssh`, they diverge — and two things hide it. OSC 7
comes from the local shell's prompt hook, so the tracked cwd freezes at the
last local directory and relative paths resolve against it. And `ssh` reads as
an in-flight command, so run_command refuses and the model falls back to
shell_input, which runs remotely. Asked to edit a config, the agent wrote a
local file, reported success, and left the user looking at an unchanged file
on the server.
Classify the in-flight command (ssh/mosh, docker/podman/kubectl exec,
distrobox, lxc, nsenter, chroot, vagrant, virsh, gcloud) so the split can be
named, then act on it:
- refuse fs_write_text / fs_edit_text while the terminal is elsewhere,
pointing at a heredoc or sed -i through shell_input instead — a warning
would arrive after the stray local file already exists
- label reads and searches with the machine they actually came from
- tell run_command's busy message that the terminal is somewhere else, not
merely busy
- state the split in both shell prompts, live when a session is in flight;
the read-only assistant gets its own wording, since its suggestions are
pasted into that same session and run remotely too
Commands that hold the terminal without changing filesystem — a dev server, a
REPL, `su` — are deliberately not flagged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWxTLzKqQXJHaoiHG13wUM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
SSH into a server from the Shell tab, ask the assistant to edit a file, and it writes the file locally while its commands run remotely — then reports success on a file the user isn't looking at.
Why
The Shell tab has two tool families pointing at different machines:
run_command/shell_input/shell_readtype into the PTY, so they act on whatever the terminal is currently in.fs_read_text/fs_write_text/fs_edit_text/code_grep/code_globgo through Tauri to the local filesystem, always.They agree only while the terminal sits at a local prompt. Two things then hide the divergence:
integration.rs:251), which a remote shell never emits — so the tracked cwd freezes at the last local directory, andresolveShellPathresolves the model's barenginx.confagainst it.sshregisters as an in-flight command (133;Cwith noD), sorunInPtyrefuses with "terminal is busy… use shell_input" — and the model dutifully switches toshell_input, which runs remotely, while its file tools stay local. Nothing ever told it those were different machines.What this does
New
src/lib/shell/nestedSession.tsclassifies the in-flight command line into a remote/container session —ssh,mosh,docker/podman exec,kubectl exec,distrobox/toolbox enter,lxc,nsenter,chroot,vagrant ssh,virsh console,gcloud compute ssh— seeing throughsudo/env-var prefixes and pipelines. Commands that hold the terminal without changing filesystem (a dev server, a REPL,su) are deliberately not flagged.On top of that:
fs_write_text,fs_edit_text) while the terminal is elsewhere, pointing at acat > … <<'EOF'heredoc orsed -ithroughshell_inputinstead. Blocking rather than warning: a warning arrives after the stray local file already exists.fs_read_text,fs_list_dir,code_grep,code_glob).run_command's busy message now says the terminal is somewhere else, not merely busy, and explains how to run something over there.SESSION CONTEXT.Testing
nestedSession.test.tsfor the classifier and message wording;agent/tools/nested-session.test.tsfor the tool-level refusal, the read label, and the two non-flagged cases).npm run checkclean,npm run lint0 errors.sshsession.No Rust or IPC-type changes, so no
export-ipc-types.shrun needed.🤖 Generated with Claude Code
https://claude.ai/code/session_01AWxTLzKqQXJHaoiHG13wUM