Skip to content

perf(code): coordinate async shutdown teardown to cut exit latency - #4831

Merged
Mason Daugherty (mdrxy) merged 9 commits into
mainfrom
mdrxy/code/async-teardown
Jul 21, 2026
Merged

perf(code): coordinate async shutdown teardown to cut exit latency#4831
Mason Daugherty (mdrxy) merged 9 commits into
mainfrom
mdrxy/code/async-teardown

Conversation

@mdrxy

@mdrxy Mason Daugherty (mdrxy) commented Jul 17, 2026

Copy link
Copy Markdown
Member

Interactive shutdown is faster because independent cleanup now overlaps instead of running sequentially, without skipping any cleanup.


session.end hooks run off the Textual event loop. Interrupted-agent cleanup still finishes before server shutdown; afterward, pending hooks drain while the server stops.

Server stop and restart paths are synchronized to prevent duplicate or racing teardown. Existing timeouts and cleanup guarantees are unchanged, and debug logs record phase timings.

Made by Open SWE

Overlap independent shutdown phases instead of serializing them.
`session.end` hooks now dispatch off the Textual event loop, agent
cleanup gates server shutdown, and server stop runs concurrently with
the pending-hook drain. `ServerProcess.stop()` is lock-guarded and
idempotent so the teardown task and outer finally never race. Adds
debug-level phase timings.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
@github-actions github-actions Bot added dcode Related to `deepagents-code` internal User is a member of the `langchain-ai` GitHub organization performance Code change that improves performance size: L 500-999 LOC labels Jul 17, 2026
@mdrxy
Mason Daugherty (mdrxy) marked this pull request as ready for review July 17, 2026 19:56
@mdrxy

This comment was marked as outdated.

@github-actions github-actions Bot added size: XL 1000+ LOC and removed size: L 500-999 LOC labels Jul 21, 2026
@mdrxy
Mason Daugherty (mdrxy) merged commit a2e501b into main Jul 21, 2026
67 checks passed
@mdrxy
Mason Daugherty (mdrxy) deleted the mdrxy/code/async-teardown branch July 21, 2026 18:46
Mason Daugherty (mdrxy) pushed a commit that referenced this pull request Jul 22, 2026
> [!CAUTION]
> Merging this PR will automatically publish to **PyPI** and create a
**GitHub release**.

For the full release process, see
[`.github/RELEASING.md`](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md).

---

_Release notes preview: keep this section in sync with the package
`CHANGELOG.md`. The published GitHub release body is extracted from the
merged `CHANGELOG.md` by `release.yml`, not from this PR description._

---


##
[0.1.45](deepagents-code==0.1.44...deepagents-code==0.1.45)
(2026-07-22)

### Features

- Added the Hooks v2 execution engine and typed hooks data models
([#4880](#4880),
[#4870](#4870))
- Added a filesystem tool allowlist for `dcode` with `--allow-fs-tools`
([#4635](#4635))
- Added a GLM-5.2 harness profile
([#4710](#4710))
- Added a built-in thread inspector skill
([#4769](#4769))
- Replaced Gemini 3.5 Flash with Gemini 3.6 Flash in the model switcher
([#4890](#4890))
- Show experimental mode in the splash screen and debug console
([#4863](#4863))
- Gate debug console click-to-copy behind a checkbox
([#4810](#4810))

### Bug Fixes

- Improved scratch-file handling by allowing cleanup of agent-created
scratch files and safe OS-temp scratch artifacts
([#4860](#4860),
[#4869](#4869))
- Skip grading during `/goal` proposals
([#4941](#4941))
- Improved MCP approval handling: Esc now aborts project MCP approval,
disabled MCP servers are honored for plugins, remembered approvals are
preserved with the env allowlist, and approvals are shared across Git
worktrees
([#4888](#4888),
[#4848](#4848),
[#4889](#4889),
[#4939](#4939))
- Hardened installer downloads and paths
([#4871](#4871))
- Fixed forced `dcode update` checks to bust the CDN cache
([#4862](#4862))
- Prevented failed exit setup from stranding the app
([#4913](#4913))
- Fixed routing so `ctrl+x` goes to the focused `ask_user` input
([#4926](#4926))
- Show `-m` prompts as queued immediately on startup
([#4861](#4861))
- Kept harness-profile diagnostics out of terminal output
([#4943](#4943))
- Avoid tracking inline restart callers
([#4894](#4894))
- Fixed debug console thread ID copying on click
([#4945](#4945))

### Performance Improvements

- Reduced exit latency by coordinating async shutdown teardown
([#4831](#4831))
- Sped up local context detection
([#4922](#4922))

_End release notes preview._

---

> [!NOTE]
> A **New Contributors** section is appended to the GitHub release notes
automatically at publish time (see [Release
Pipeline](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md#release-pipeline),
step 2).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: langchain-oss-automated-triage[bot] <248757908+langchain-oss-automated-triage[bot]@users.noreply.github.com>
Mason Daugherty (mdrxy) added a commit that referenced this pull request Jul 24, 2026
With `DEEPAGENTS_CODE_DEBUG` set, `dcode` again prints `Server log
preserved at: <path>` to stderr after it exits.

---

`perf(code): coordinate async shutdown teardown` (#4831) moved server
`stop()` into the deferred `_graceful_exit()` coroutine, which runs
*before* `super().exit()` restores the terminal. The `Server log
preserved at:` print inside `ServerProcess._stop_process_locked` then
fired while Textual still owned the alternate screen and was discarded
on exit; the outer `run_textual_app` `stop()` is idempotent, so it never
re-emitted the line.

The fix decouples *closing the log file* (must happen during `stop()`)
from *announcing the preserved path* (must happen after the terminal is
restored). `_stop_process_locked` now records `_preserved_log_path`, and
a new `emit_preserved_log_notice()` prints it exactly once. It is called
from the terminal stop sites that run after the terminal is restored or
with no TUI active (`run_textual_app` finally, `server_session` finally,
`start_server_and_get_agent` finally, and `__aexit__`), and deliberately
not from the hidden in-session teardown or the `/restart` path. Because
the emitter clears the stored path, the interactive path's double
`stop()` still yields a single visible line.

Made by [Open
SWE](https://openswe.vercel.app/agents/4ca951ec-5223-29f3-0312-2a0d17e26dcc)

## References
- Plan:
https://openswe.vercel.app/agents/4ca951ec-5223-29f3-0312-2a0d17e26dcc/plan

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Marcelo5444 pushed a commit to Marcelo5444/deepagents that referenced this pull request Jul 30, 2026
…angchain-ai#4831)

Interactive shutdown is faster because independent cleanup now overlaps
instead of running sequentially, without skipping any cleanup.

---

`session.end` hooks run off the Textual event loop. Interrupted-agent
cleanup still finishes before server shutdown; afterward, pending hooks
drain while the server stops.

Server stop and restart paths are synchronized to prevent duplicate or
racing teardown. Existing timeouts and cleanup guarantees are unchanged,
and debug logs record phase timings.

Made by [Open
SWE](https://openswe.vercel.app/agents/ce9a3224-140d-08bc-25d2-202982da0203)

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Marcelo5444 pushed a commit to Marcelo5444/deepagents that referenced this pull request Jul 30, 2026
> [!CAUTION]
> Merging this PR will automatically publish to **PyPI** and create a
**GitHub release**.

For the full release process, see
[`.github/RELEASING.md`](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md).

---

_Release notes preview: keep this section in sync with the package
`CHANGELOG.md`. The published GitHub release body is extracted from the
merged `CHANGELOG.md` by `release.yml`, not from this PR description._

---


##
[0.1.45](langchain-ai/deepagents@deepagents-code==0.1.44...deepagents-code==0.1.45)
(2026-07-22)

### Features

- Added the Hooks v2 execution engine and typed hooks data models
([langchain-ai#4880](langchain-ai#4880),
[langchain-ai#4870](langchain-ai#4870))
- Added a filesystem tool allowlist for `dcode` with `--allow-fs-tools`
([langchain-ai#4635](langchain-ai#4635))
- Added a GLM-5.2 harness profile
([langchain-ai#4710](langchain-ai#4710))
- Added a built-in thread inspector skill
([langchain-ai#4769](langchain-ai#4769))
- Replaced Gemini 3.5 Flash with Gemini 3.6 Flash in the model switcher
([langchain-ai#4890](langchain-ai#4890))
- Show experimental mode in the splash screen and debug console
([langchain-ai#4863](langchain-ai#4863))
- Gate debug console click-to-copy behind a checkbox
([langchain-ai#4810](langchain-ai#4810))

### Bug Fixes

- Improved scratch-file handling by allowing cleanup of agent-created
scratch files and safe OS-temp scratch artifacts
([langchain-ai#4860](langchain-ai#4860),
[langchain-ai#4869](langchain-ai#4869))
- Skip grading during `/goal` proposals
([langchain-ai#4941](langchain-ai#4941))
- Improved MCP approval handling: Esc now aborts project MCP approval,
disabled MCP servers are honored for plugins, remembered approvals are
preserved with the env allowlist, and approvals are shared across Git
worktrees
([langchain-ai#4888](langchain-ai#4888),
[langchain-ai#4848](langchain-ai#4848),
[langchain-ai#4889](langchain-ai#4889),
[langchain-ai#4939](langchain-ai#4939))
- Hardened installer downloads and paths
([langchain-ai#4871](langchain-ai#4871))
- Fixed forced `dcode update` checks to bust the CDN cache
([langchain-ai#4862](langchain-ai#4862))
- Prevented failed exit setup from stranding the app
([langchain-ai#4913](langchain-ai#4913))
- Fixed routing so `ctrl+x` goes to the focused `ask_user` input
([langchain-ai#4926](langchain-ai#4926))
- Show `-m` prompts as queued immediately on startup
([langchain-ai#4861](langchain-ai#4861))
- Kept harness-profile diagnostics out of terminal output
([langchain-ai#4943](langchain-ai#4943))
- Avoid tracking inline restart callers
([langchain-ai#4894](langchain-ai#4894))
- Fixed debug console thread ID copying on click
([langchain-ai#4945](langchain-ai#4945))

### Performance Improvements

- Reduced exit latency by coordinating async shutdown teardown
([langchain-ai#4831](langchain-ai#4831))
- Sped up local context detection
([langchain-ai#4922](langchain-ai#4922))

_End release notes preview._

---

> [!NOTE]
> A **New Contributors** section is appended to the GitHub release notes
automatically at publish time (see [Release
Pipeline](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md#release-pipeline),
step 2).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: langchain-oss-automated-triage[bot] <248757908+langchain-oss-automated-triage[bot]@users.noreply.github.com>
Marcelo5444 pushed a commit to Marcelo5444/deepagents that referenced this pull request Jul 30, 2026
…hain-ai#4999)

With `DEEPAGENTS_CODE_DEBUG` set, `dcode` again prints `Server log
preserved at: <path>` to stderr after it exits.

---

`perf(code): coordinate async shutdown teardown` (langchain-ai#4831) moved server
`stop()` into the deferred `_graceful_exit()` coroutine, which runs
*before* `super().exit()` restores the terminal. The `Server log
preserved at:` print inside `ServerProcess._stop_process_locked` then
fired while Textual still owned the alternate screen and was discarded
on exit; the outer `run_textual_app` `stop()` is idempotent, so it never
re-emitted the line.

The fix decouples *closing the log file* (must happen during `stop()`)
from *announcing the preserved path* (must happen after the terminal is
restored). `_stop_process_locked` now records `_preserved_log_path`, and
a new `emit_preserved_log_notice()` prints it exactly once. It is called
from the terminal stop sites that run after the terminal is restored or
with no TUI active (`run_textual_app` finally, `server_session` finally,
`start_server_and_get_agent` finally, and `__aexit__`), and deliberately
not from the hidden in-session teardown or the `/restart` path. Because
the emitter clears the stored path, the interactive path's double
`stop()` still yields a single visible line.

Made by [Open
SWE](https://openswe.vercel.app/agents/4ca951ec-5223-29f3-0312-2a0d17e26dcc)

## References
- Plan:
https://openswe.vercel.app/agents/4ca951ec-5223-29f3-0312-2a0d17e26dcc/plan

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dcode Related to `deepagents-code` internal User is a member of the `langchain-ai` GitHub organization performance Code change that improves performance size: XL 1000+ LOC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant