feat(code): integrate Hooks v2 client lifecycle events - #5104
Merged
Alexander Olsen (aolsenjazz) merged 6 commits intoJul 29, 2026
Conversation
This was referenced Jul 27, 2026
Alexander Olsen (aolsenjazz)
left a comment
Contributor
There was a problem hiding this comment.
Please take a look at app.py and lmk what you think. Left a number of comments re. composition + ownership
Alexander Olsen (aolsenjazz)
requested changes
Jul 28, 2026
Alexander Olsen (aolsenjazz)
left a comment
Contributor
There was a problem hiding this comment.
one more callout I should make here: the way the system is designed, everything is super defensive. I don't see a world where a client hooks service is ever None. Imo it doesn't matter if init fails + the service is unusable; consumers of the hooks system shouldn't ever need to do a defined-check on a service
Johannes du Plessis (johannes117)
requested a review
from Alexander Olsen (aolsenjazz)
July 28, 2026 20:21
Require the `compact_conversation` tool name (not just the content prefix) before firing the compact SessionStart lifecycle in the headless, TUI, and resume paths, so ordinary tool output echoing "Conversation compacted." can't trigger a false compaction boundary. Serialize transcript materialization across processes with an advisory file lock and merge on-disk records into the buffer before rewriting, so two dcode processes resuming the same thread no longer silently drop each other's streamed records. Restore the required-empty-answer warning toast in `ask_user` that the rebase dropped, so blocked submits give feedback again.
Client-side Hooks v2 had no owner: the runtime, hook service, transcript, and session identity were assembled by hand at every consumer, so each one also had to answer whether hooks were available at all. `HooksManager` now owns all four and exposes lifecycle intent instead of plumbing. A manager whose config failed to load stays usable and answers neutrally, removing the availability checks rather than relocating them. Permission resolution moves into `hooks/permissions.py` so the Textual adapter and the headless runner share one `PermissionPlan`. Co-authored-by: Cursor <cursoragent@cursor.com>
Johannes du Plessis (johannes117)
force-pushed
the
johannes/code/dcd-71-client-lifecycle
branch
from
July 28, 2026 21:59
30dc097 to
59e5597
Compare
Alexander Olsen (aolsenjazz)
approved these changes
Jul 29, 2026
Alexander Olsen (aolsenjazz)
left a comment
Contributor
There was a problem hiding this comment.
we ball
Alexander Olsen (aolsenjazz)
deleted the
johannes/code/dcd-71-client-lifecycle
branch
July 29, 2026 15:29
Johannes du Plessis (johannes117)
added a commit
that referenced
this pull request
Jul 29, 2026
Supersedes #5044 (auto-closed when #4997 squash-merged; GitHub seals force-pushed closed PRs). Project-level hooks now require an explicit workspace trust decision before their commands run. Interactive users can allow once, remember the workspace, or skip project hooks; headless runs remain opt-in through `--trust-project-hooks`. --- This closes the execution gap that left interactive project hooks permanently disabled while preserving fail-closed behavior. Trust is keyed to the canonical repository root, saved atomically, and enforced again at runtime if a snapshot is ever constructed inconsistently. `config path` and the threat model now expose the relevant project, user, and trust locations. Re-anchored onto `main` after #5104 squash-merged, so the PR diff contains only this branch's commits. ## Review Guide 1. `hooks/trust.py` — store, keys, atomic write, trust APIs 2. `hooks/loading.py` — project source ingest vs skip 3. `hooks/runtime.py` — fail-closed runtime guard 4. `main.py` — `--trust-project-hooks` + interactive prompt 5. Skim: `app.py`, `client/commands/config.py`, `THREAT_MODEL.md` 6. Tests: `test_trust.py`, then config/app/main touches <details> <summary>Test plan</summary> - Full hooks suite passes on the rebuilt stack tip. `test_trust.py` keeps one case per regressable behavior: canonical persistence, fail-closed corrupt-store handling, project-source provenance, the runtime trust guard, the three prompt outcomes, prompt suppression on persisted trust, and Textual wiring. </details> <!-- branch-stack-start --> ------------------------- - main - **feat(code): add project hooks workspace trust** 👈 - #5045 <sup>[Stack](https://www.git-town.com/how-to/proposal-breadcrumb.html) generated by [Git Town](https://github.com/git-town/git-town)</sup> <!-- branch-stack-end --> --------- 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
…5104) Supersedes langchain-ai#5010 (auto-closed when langchain-ai#4997 squash-merged; GitHub seals force-pushed closed PRs). Related: [DCD-71](https://linear.app/langchain/issue/DCD-71/hooks-v2-integrate-client-owned-lifecycle-events) dcode now runs client-owned Hooks v2 lifecycle events consistently in interactive and headless sessions, including hook-driven approval decisions before permission prompts are shown. --- - Invokes `SessionStart` and `SessionEnd` at startup, resume, clear, compact, switch, and exit boundaries. - Applies `PermissionRequest` allow, deny, ask, interrupt, notice, terminal, and stop effects before client approval resolution. - Routes supported dcode notifications through one typed service with explicit wire mappings while avoiding migrated legacy duplicates. - Preserves session-start context for the next model turn and adds TUI/headless decision-parity coverage. - Ignores generated local transcript state through `.gitignore`. This branch was re-anchored onto `main` after langchain-ai#4997 squash-merged; the diff is identical to the approved langchain-ai#5010 head (`f4e8ee672`), minus a stale resurrection of `todo_list_prompt.md` that langchain-ai#5098 deleted on main. ## Review Guide 1. `hooks/client_lifecycle.py` — `ClientHookService`, context, permission outcomes 2. `app.py` — SessionStart/End boundaries + service wiring 3. `tui/textual_adapter.py`, `client/non_interactive.py` — interactive vs headless parity 4. Skim: `transcript.py`, `server_middleware.py` (PermissionRequest), `runtime.py` / `projection.py` 5. Tests: `test_client_lifecycle.py`, then adapter / non-interactive / server-lifecycle deltas <details> <summary>Test plan</summary> - 266 hooks unit tests pass on the rebuilt tip - 412 non-interactive / Textual adapter / status widget tests pass </details> <!-- branch-stack-start --> ------------------------- - main - **feat(code): integrate Hooks v2 client lifecycle events** :point_left: - langchain-ai#5105 - langchain-ai#5045 <sup>[Stack](https://www.git-town.com/how-to/proposal-breadcrumb.html) generated by [Git Town](https://github.com/git-town/git-town)</sup> <!-- branch-stack-end --> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
Marcelo5444
pushed a commit
to Marcelo5444/deepagents
that referenced
this pull request
Jul 30, 2026
Supersedes langchain-ai#5044 (auto-closed when langchain-ai#4997 squash-merged; GitHub seals force-pushed closed PRs). Project-level hooks now require an explicit workspace trust decision before their commands run. Interactive users can allow once, remember the workspace, or skip project hooks; headless runs remain opt-in through `--trust-project-hooks`. --- This closes the execution gap that left interactive project hooks permanently disabled while preserving fail-closed behavior. Trust is keyed to the canonical repository root, saved atomically, and enforced again at runtime if a snapshot is ever constructed inconsistently. `config path` and the threat model now expose the relevant project, user, and trust locations. Re-anchored onto `main` after langchain-ai#5104 squash-merged, so the PR diff contains only this branch's commits. ## Review Guide 1. `hooks/trust.py` — store, keys, atomic write, trust APIs 2. `hooks/loading.py` — project source ingest vs skip 3. `hooks/runtime.py` — fail-closed runtime guard 4. `main.py` — `--trust-project-hooks` + interactive prompt 5. Skim: `app.py`, `client/commands/config.py`, `THREAT_MODEL.md` 6. Tests: `test_trust.py`, then config/app/main touches <details> <summary>Test plan</summary> - Full hooks suite passes on the rebuilt stack tip. `test_trust.py` keeps one case per regressable behavior: canonical persistence, fail-closed corrupt-store handling, project-source provenance, the runtime trust guard, the three prompt outcomes, prompt suppression on persisted trust, and Textual wiring. </details> <!-- branch-stack-start --> ------------------------- - main - **feat(code): add project hooks workspace trust** 👈 - langchain-ai#5045 <sup>[Stack](https://www.git-town.com/how-to/proposal-breadcrumb.html) generated by [Git Town](https://github.com/git-town/git-town)</sup> <!-- branch-stack-end --> --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Mason Daugherty (mdrxy)
pushed a commit
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`. Publish reads the merged CHANGELOG via `release.yml`, not this PR description — keep them aligned anyway so the PR stays an accurate historical record for reviewers and anyone returning later._ --- ## [0.1.50](deepagents-code==0.1.49...deepagents-code==0.1.50) (2026-07-30) ### Highlights - Added project hooks workspace trust and expanded Hooks v2 support with client and server lifecycle events plus runtime feedback ([#5105](#5105), [#5104](#5104), [#4997](#4997), [#5045](#5045)). - Added an option to mute the “YOLO is active” toast ([#5103](#5103)). - Made the splash screen `thread` ID clickable to copy it ([#5173](#5173)). - Show `ask_user` answers directly on the answered tool row ([#5100](#5100)). - Show a toast when submitting an empty required `ask_user` answer ([#5095](#5095)). - Added thread message counts to the Debug Console ([#5117](#5117)). ### Fixes and improvements - Gated Hooks v2 behind `DEEPAGENTS_CODE_EXPERIMENTAL` and improved hook resume stability across identity and Command tool results ([#5146](#5146), [#5176](#5176)). - Kept server hook state out of task results ([#5164](#5164)). - Stopped duplicate Auto transcript events during interrupt replay ([#5157](#5157)). - Kept `/update` and `/install --package` prompts responsive ([#5127](#5127)). - Refreshed the `/threads` cache after each turn ([#5174](#5174)). - Anchored toasts above the chat input and added a toast when media is dropped into a free-text question ([#5101](#5101), [#5099](#5099)). - Improved thread status message styling and links ([#5118](#5118)). - Made resume hints echo the launched command name ([#5119](#5119)). - Scoped selection copy to the clicked screen ([#5140](#5140)). - Ignored mouse hits on detached widgets ([#5114](#5114)). _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> Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
Mason Daugherty (mdrxy)
pushed a commit
that referenced
this pull request
Jul 30, 2026
Supersedes #5010 (auto-closed when #4997 squash-merged; GitHub seals force-pushed closed PRs). Related: [DCD-71](https://linear.app/langchain/issue/DCD-71/hooks-v2-integrate-client-owned-lifecycle-events) dcode now runs client-owned Hooks v2 lifecycle events consistently in interactive and headless sessions, including hook-driven approval decisions before permission prompts are shown. --- - Invokes `SessionStart` and `SessionEnd` at startup, resume, clear, compact, switch, and exit boundaries. - Applies `PermissionRequest` allow, deny, ask, interrupt, notice, terminal, and stop effects before client approval resolution. - Routes supported dcode notifications through one typed service with explicit wire mappings while avoiding migrated legacy duplicates. - Preserves session-start context for the next model turn and adds TUI/headless decision-parity coverage. - Ignores generated local transcript state through `.gitignore`. This branch was re-anchored onto `main` after #4997 squash-merged; the diff is identical to the approved #5010 head (`f4e8ee672`), minus a stale resurrection of `todo_list_prompt.md` that #5098 deleted on main. 1. `hooks/client_lifecycle.py` — `ClientHookService`, context, permission outcomes 2. `app.py` — SessionStart/End boundaries + service wiring 3. `tui/textual_adapter.py`, `client/non_interactive.py` — interactive vs headless parity 4. Skim: `transcript.py`, `server_middleware.py` (PermissionRequest), `runtime.py` / `projection.py` 5. Tests: `test_client_lifecycle.py`, then adapter / non-interactive / server-lifecycle deltas <details> <summary>Test plan</summary> - 266 hooks unit tests pass on the rebuilt tip - 412 non-interactive / Textual adapter / status widget tests pass </details> <!-- branch-stack-start --> ------------------------- - main - **feat(code): integrate Hooks v2 client lifecycle events** :point_left: - #5105 - #5045 <sup>[Stack](https://www.git-town.com/how-to/proposal-breadcrumb.html) generated by [Git Town](https://github.com/git-town/git-town)</sup> <!-- branch-stack-end --> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
Mason Daugherty (mdrxy)
pushed a commit
that referenced
this pull request
Jul 30, 2026
Supersedes #5044 (auto-closed when #4997 squash-merged; GitHub seals force-pushed closed PRs). Project-level hooks now require an explicit workspace trust decision before their commands run. Interactive users can allow once, remember the workspace, or skip project hooks; headless runs remain opt-in through `--trust-project-hooks`. --- This closes the execution gap that left interactive project hooks permanently disabled while preserving fail-closed behavior. Trust is keyed to the canonical repository root, saved atomically, and enforced again at runtime if a snapshot is ever constructed inconsistently. `config path` and the threat model now expose the relevant project, user, and trust locations. Re-anchored onto `main` after #5104 squash-merged, so the PR diff contains only this branch's commits. ## Review Guide 1. `hooks/trust.py` — store, keys, atomic write, trust APIs 2. `hooks/loading.py` — project source ingest vs skip 3. `hooks/runtime.py` — fail-closed runtime guard 4. `main.py` — `--trust-project-hooks` + interactive prompt 5. Skim: `app.py`, `client/commands/config.py`, `THREAT_MODEL.md` 6. Tests: `test_trust.py`, then config/app/main touches <details> <summary>Test plan</summary> - Full hooks suite passes on the rebuilt stack tip. `test_trust.py` keeps one case per regressable behavior: canonical persistence, fail-closed corrupt-store handling, project-source provenance, the runtime trust guard, the three prompt outcomes, prompt suppression on persisted trust, and Textual wiring. </details> <!-- branch-stack-start --> ------------------------- - main - **feat(code): add project hooks workspace trust** 👈 - #5045 <sup>[Stack](https://www.git-town.com/how-to/proposal-breadcrumb.html) generated by [Git Town](https://github.com/git-town/git-town)</sup> <!-- branch-stack-end --> --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Mason Daugherty (mdrxy)
pushed a commit
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`. Publish reads the merged CHANGELOG via `release.yml`, not this PR description — keep them aligned anyway so the PR stays an accurate historical record for reviewers and anyone returning later._ --- ## [0.1.50](deepagents-code==0.1.49...deepagents-code==0.1.50) (2026-07-30) ### Highlights - Added project hooks workspace trust and expanded Hooks v2 support with client and server lifecycle events plus runtime feedback ([#5105](#5105), [#5104](#5104), [#4997](#4997), [#5045](#5045)). - Added an option to mute the “YOLO is active” toast ([#5103](#5103)). - Made the splash screen `thread` ID clickable to copy it ([#5173](#5173)). - Show `ask_user` answers directly on the answered tool row ([#5100](#5100)). - Show a toast when submitting an empty required `ask_user` answer ([#5095](#5095)). - Added thread message counts to the Debug Console ([#5117](#5117)). ### Fixes and improvements - Gated Hooks v2 behind `DEEPAGENTS_CODE_EXPERIMENTAL` and improved hook resume stability across identity and Command tool results ([#5146](#5146), [#5176](#5176)). - Kept server hook state out of task results ([#5164](#5164)). - Stopped duplicate Auto transcript events during interrupt replay ([#5157](#5157)). - Kept `/update` and `/install --package` prompts responsive ([#5127](#5127)). - Refreshed the `/threads` cache after each turn ([#5174](#5174)). - Anchored toasts above the chat input and added a toast when media is dropped into a free-text question ([#5101](#5101), [#5099](#5099)). - Improved thread status message styling and links ([#5118](#5118)). - Made resume hints echo the launched command name ([#5119](#5119)). - Scoped selection copy to the clicked screen ([#5140](#5140)). - Ignored mouse hits on detached widgets ([#5114](#5114)). _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> Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
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.
Supersedes #5010 (auto-closed when #4997 squash-merged; GitHub seals force-pushed closed PRs).
Related: DCD-71
dcode now runs client-owned Hooks v2 lifecycle events consistently in interactive and headless sessions, including hook-driven approval decisions before permission prompts are shown.
SessionStartandSessionEndat startup, resume, clear, compact, switch, and exit boundaries.PermissionRequestallow, deny, ask, interrupt, notice, terminal, and stop effects before client approval resolution..gitignore.This branch was re-anchored onto
mainafter #4997 squash-merged; the diff is identical to the approved #5010 head (f4e8ee672), minus a stale resurrection oftodo_list_prompt.mdthat #5098 deleted on main.Review Guide
hooks/client_lifecycle.py—ClientHookService, context, permission outcomesapp.py— SessionStart/End boundaries + service wiringtui/textual_adapter.py,client/non_interactive.py— interactive vs headless paritytranscript.py,server_middleware.py(PermissionRequest),runtime.py/projection.pytest_client_lifecycle.py, then adapter / non-interactive / server-lifecycle deltasTest plan
Stack generated by Git Town