Skip to content

Let agent_stop close visible terminal windows it spawned#27

Merged
luckeyfaraday merged 2 commits into
mainfrom
feat/close-visible-terminal
Jun 16, 2026
Merged

Let agent_stop close visible terminal windows it spawned#27
luckeyfaraday merged 2 commits into
mainfrom
feat/close-visible-terminal

Conversation

@luckeyfaraday

@luckeyfaraday luckeyfaraday commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Problem

When Athena Code spawns an agent in a visible terminal (agent_spawn visible=true, or agent_takeover where="terminal"), it cannot close that window again. The emulator was launched detached and no part of the system held a handle to the agent running inside it — agent_stop only ever killed record.process, which visible agents don't have.

Fix

The emulator launcher PID is no good for this: server-style terminals (gnome-terminal, konsole, kitty) hand the work to a daemon and the launcher exits immediately, so its PID dies and signalling it does nothing. Instead we track the PID of the command running inside the window:

  • terminal.ts — on Linux the visible command is wrapped in sh -c 'printf "$$" > pidfile; exec "$@"'. Because exec preserves the PID, the value written to a tmp pid file is the agent CLI itself. openVisibleTerminal returns that pidFile (and a pid directly when one is already known).
  • local.ts — the record stores terminalPid / terminalPidFile. New closeVisibleTerminal resolves the PID (reading the pid file, polling briefly if it hasn't been written yet), sends SIGTERM to the process group (process.kill(-pid), falling back to the bare PID), waits for the process to actually exit, then marks the record exited and reusable. stopLocalAgent routes visible agents through it and is now async. The tmp pid file is unlinked after a successful read and on respawn so it doesn't linger in tmpdir.
  • agent-local.tslaunch.pid / launch.pidFile are threaded into registerVisibleAgent and the takeover path; result messages tell the user they can agent_stop <handle> to close the window. The agent_stop tool description now covers closing windows.

Caveat

macOS (Terminal.app via osascript) and Windows (start) don't yield a killable PID, so both pid and pidFile are undefined there and close degrades gracefully — agent_stop returns false, the record stays visible, and the "close the window" hint is omitted. Linux is fully functional; macOS would need to track the do script tab reference and close it via a second AppleScript, left as a follow-up.

Note that killing the in-window command only closes the window when the emulator is configured to close on child exit (the default for the common emulators).

Tests

test/agent-local.test.ts adds coverage for: a tracked-PID visible agent being closed (process group torn down, record marked exited), an untracked visible agent staying blocked, and the Linux pid-file round-trip through a fake terminal on PATH.

🤖 Generated with Claude Code

luckeyfaraday and others added 2 commits June 16, 2026 00:06
Visible agent terminals were launched detached with their PID discarded,
so nothing held a handle to the window and agent_stop only ever killed
headless processes. Capture the emulator PID through openVisibleTerminal,
store it on the visible agent record, and route stopLocalAgent through a
process-group kill (SIGTERM to -pid) that tears down the window and the
agent CLI inside it on Linux/BSD.

macOS (Terminal.app via osascript) and Windows (start) don't yield a
killable window PID, so launch.pid is undefined there and close degrades
gracefully without offering the option.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The emulator launcher PID is useless for closing the window: server-style
terminals (gnome-terminal, konsole, kitty) hand work to a daemon and exit,
so child.pid dies immediately and signalling it does nothing.

Instead, wrap the visible command in `sh -c 'printf $$ > pidfile; exec "$@"'`
so the PID written is the agent CLI itself (exec preserves the PID). The
record tracks the pid file; closeVisibleTerminal resolves the PID from it,
SIGTERMs the process group (falling back to the bare PID), waits for exit,
then marks the record reusable. stopLocalAgent is now async.

Also clean up the tmp pid file on respawn and after a successful read so it
doesn't linger in tmpdir. macOS/Windows still yield no PID and degrade
gracefully. Adds tests for tracked-pid close, untracked-stays-blocked, and
the Linux pid-file round-trip through a fake terminal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@luckeyfaraday luckeyfaraday merged commit 8a6d638 into main Jun 16, 2026
2 checks passed
@luckeyfaraday luckeyfaraday deleted the feat/close-visible-terminal branch June 16, 2026 15:37
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