Skip to content

feat(code): integrate Hooks v2 client lifecycle events - #5104

Merged
Alexander Olsen (aolsenjazz) merged 6 commits into
mainfrom
johannes/code/dcd-71-client-lifecycle
Jul 29, 2026
Merged

feat(code): integrate Hooks v2 client lifecycle events#5104
Alexander Olsen (aolsenjazz) merged 6 commits into
mainfrom
johannes/code/dcd-71-client-lifecycle

Conversation

@johannes117

@johannes117 Johannes du Plessis (johannes117) commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

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.


  • 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.

Review Guide

  1. hooks/client_lifecycle.pyClientHookService, 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
Test plan
  • 266 hooks unit tests pass on the rebuilt tip
  • 412 non-interactive / Textual adapter / status widget tests pass

Stack generated by Git Town

@github-actions github-actions Bot added dcode Related to `deepagents-code` feature New feature/enhancement or request for one internal User is a member of the `langchain-ai` GitHub organization size: XL 1000+ LOC labels Jul 27, 2026

@open-swe open-swe Bot 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.

Open SWE Review found 3 potential issues.

Open in WebView Open SWE trace

Comment thread libs/code/deepagents_code/client/non_interactive.py
Comment thread libs/code/deepagents_code/hooks/transcript.py
Comment thread libs/code/deepagents_code/tui/widgets/ask_user.py

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.

Please take a look at app.py and lmk what you think. Left a number of comments re. composition + ownership

Comment thread libs/code/deepagents_code/hooks/client_lifecycle.py Outdated
Comment thread libs/code/deepagents_code/hooks/client_lifecycle.py Outdated
Comment thread libs/code/deepagents_code/app.py Outdated
Comment thread libs/code/deepagents_code/app.py Outdated
Comment thread libs/code/deepagents_code/app.py Outdated
Comment thread libs/code/deepagents_code/app.py

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.

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

Comment thread libs/code/deepagents_code/tui/textual_adapter.py
Comment thread libs/code/deepagents_code/tui/textual_adapter.py Outdated
Comment thread libs/code/deepagents_code/tui/textual_adapter.py Outdated
Comment thread libs/code/deepagents_code/tui/textual_adapter.py Outdated
Comment thread libs/code/deepagents_code/tui/textual_adapter.py Outdated
Comment thread libs/code/deepagents_code/tui/textual_adapter.py Outdated
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.

Re-anchored onto main after #4997 squash-merged; this commit carries the
full PR #5010 delta.
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>
@johannes117
Johannes du Plessis (johannes117) force-pushed the johannes/code/dcd-71-client-lifecycle branch from 30dc097 to 59e5597 Compare July 28, 2026 21:59

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.

we ball

@aolsenjazz
Alexander Olsen (aolsenjazz) merged commit e8702df into main Jul 29, 2026
55 checks passed
@aolsenjazz
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dcode Related to `deepagents-code` feature New feature/enhancement or request for one internal User is a member of the `langchain-ai` GitHub organization size: XL 1000+ LOC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants