Skip to content

agent: Release completed terminal PTY resources - #62978

Merged
SomeoneToIgnore merged 3 commits into
zed-industries:mainfrom
nertzy:fix-agent-terminal-kqueue-leak
Aug 26, 2026
Merged

SomeoneToIgnore merged 3 commits into
zed-industries:mainfrom
nertzy:fix-agent-terminal-kqueue-leak

Conversation

@nertzy

@nertzy nertzy commented Aug 21, 2026 •

Copy link
Copy Markdown
Contributor

Closes #62972

Completed Agent terminal cards remain alive so conversation history can render their output. Each retained terminal also kept its Alacritty PTY sender alive, which retained a poller and one kqueue descriptor after the command exited.

Terminal lifecycle state is now more strongly typed to prevent this class of regression. Task terminals are created through a sealed TerminalMode API that pairs their running state with the correct completion channel, while PTY resources use explicit active and released states. This makes invalid lifecycle combinations unrepresentable instead of relying on callers to coordinate independent Option values correctly.

  • Release the live PTY sender after final terminal output is captured.
  • Preserve PTY process metadata and working-directory behavior for historical terminal cards.
  • Represent PTY resources as explicit active or released lifecycle states.
  • Construct interactive and task terminals through typed modes so a task cannot omit its running state or use a mismatched completion channel.
  • Keep resource release idempotent for later ACP release and entity destruction.
  • Add a macOS regression test that retains the historical terminal entity while exercising the complete sandboxed terminal tool path.

Release Notes:

  • Fixed Agent terminal tool calls leaking file descriptors on macOS.

@cla-bot cla-bot Bot added the cla-signed The user has signed the Contributor License Agreement label Aug 21, 2026
@zed-community-bot zed-community-bot Bot added the first contribution the author's first pull request to Zed. NOTE: the label application is automated via github actions label Aug 21, 2026
@nertzy
nertzy force-pushed the fix-agent-terminal-kqueue-leak branch from 6501548 to b83d95a Compare August 21, 2026 02:09
@SomeoneToIgnore SomeoneToIgnore added the area:ai/agent thread Feedback for Zed's Agent Thread label Aug 21, 2026
@nertzy
nertzy force-pushed the fix-agent-terminal-kqueue-leak branch 6 times, most recently from 93f7cb1 to c012a0a Compare August 21, 2026 17:28
@nertzy

nertzy commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

FYI this is taking down Zed (and my entire OS) pretty hard every 1-2 hours on a normal working day. It would be great to close up this hole. Happy to iterate on the solution or do feel free to take this in a completely different direction.

@nertzy
nertzy force-pushed the fix-agent-terminal-kqueue-leak branch from c012a0a to 58255ba Compare August 25, 2026 16:54
Completed agent terminal entities remain alive to render conversation
history, which also retained the PTY sender and its kqueue descriptor.

Release live PTY resources after capturing final output while preserving
the terminal buffer, process metadata, and working-directory behavior.

Strengthen terminal lifecycle types so task terminals always have paired
completion state and released PTY resources cannot be treated as live.
This makes the invalid states behind the regression unrepresentable.

Closes zed-industries#62972
@nertzy
nertzy force-pushed the fix-agent-terminal-kqueue-leak branch from 58255ba to 3e32407 Compare August 26, 2026 14:36
@SomeoneToIgnore SomeoneToIgnore self-assigned this Aug 26, 2026

@SomeoneToIgnore SomeoneToIgnore left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the nice find!

If I understand correctly, this change also moves the killpg(SIGTERM) from entity drop to the moment the command completes: background children left in the shell's process group (e.g. nohup server &) are now terminated as soon as the foreground command exits, where before they survived for the whole session.
Seems legit — it matches the terminal tool's "no indefinite commands" contract and the existing timeout/cancel kill paths — but wanted to raise it in the comments in case we need to come back to this decision.

@SomeoneToIgnore
SomeoneToIgnore added this pull request to the merge queue Aug 26, 2026
Merged via the queue into zed-industries:main with commit 3b90a96 Aug 26, 2026
34 checks passed
playdohface pushed a commit to playdohface/zed that referenced this pull request Aug 29, 2026
Closes zed-industries#62972

Completed Agent terminal cards remain alive so conversation history can
render their output. Each retained terminal also kept its Alacritty PTY
sender alive, which retained a poller and one kqueue descriptor after
the command exited.

Terminal lifecycle state is now more strongly typed to prevent this
class of regression. Task terminals are created through a sealed
`TerminalMode` API that pairs their running state with the correct
completion channel, while PTY resources use explicit active and released
states. This makes invalid lifecycle combinations unrepresentable
instead of relying on callers to coordinate independent `Option` values
correctly.

- Release the live PTY sender after final terminal output is captured.
- Preserve PTY process metadata and working-directory behavior for
historical terminal cards.
- Represent PTY resources as explicit active or released lifecycle
states.
- Construct interactive and task terminals through typed modes so a task
cannot omit its running state or use a mismatched completion channel.
- Keep resource release idempotent for later ACP release and entity
destruction.
- Add a macOS regression test that retains the historical terminal
entity while exercising the complete sandboxed terminal tool path.

Release Notes:

- Fixed Agent terminal tool calls leaking file descriptors on macOS.

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
@nertzy
nertzy deleted the fix-agent-terminal-kqueue-leak branch August 31, 2026 16:11
m-altaifi pushed a commit to m-altaifi/zed that referenced this pull request Sep 2, 2026
Closes zed-industries#62972

Completed Agent terminal cards remain alive so conversation history can
render their output. Each retained terminal also kept its Alacritty PTY
sender alive, which retained a poller and one kqueue descriptor after
the command exited.

Terminal lifecycle state is now more strongly typed to prevent this
class of regression. Task terminals are created through a sealed
`TerminalMode` API that pairs their running state with the correct
completion channel, while PTY resources use explicit active and released
states. This makes invalid lifecycle combinations unrepresentable
instead of relying on callers to coordinate independent `Option` values
correctly.

- Release the live PTY sender after final terminal output is captured.
- Preserve PTY process metadata and working-directory behavior for
historical terminal cards.
- Represent PTY resources as explicit active or released lifecycle
states.
- Construct interactive and task terminals through typed modes so a task
cannot omit its running state or use a mismatched completion channel.
- Keep resource release idempotent for later ACP release and entity
destruction.
- Add a macOS regression test that retains the historical terminal
entity while exercising the complete sandboxed terminal tool path.

Release Notes:

- Fixed Agent terminal tool calls leaking file descriptors on macOS.

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ai/agent thread Feedback for Zed's Agent Thread cla-signed The user has signed the Contributor License Agreement first contribution the author's first pull request to Zed. NOTE: the label application is automated via github actions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Agent terminal tool calls leak kqueue descriptors and eventually hit EMFILE on macOS

2 participants