Skip to content

feat(code): integrate Hooks v2 server lifecycle events - #4997

Merged
Johannes du Plessis (johannes117) merged 17 commits into
mainfrom
johannes/code/dcd-69-server-lifecycle-f6bb
Jul 27, 2026
Merged

feat(code): integrate Hooks v2 server lifecycle events#4997
Johannes du Plessis (johannes117) merged 17 commits into
mainfrom
johannes/code/dcd-69-server-lifecycle-f6bb

Conversation

@johannes117

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

Copy link
Copy Markdown
Contributor

Depends on #4971
Closes DCD-69

Server-owned Hooks v2 events now pause the agent over LangGraph interrupts, run on the client HooksRuntime, and resume with typed decisions.


This starts the DCD-69 stack on top of the DCD-70 leaf (#4971).

  • Adds a versioned hook_invocation interrupt/resume transport around HookInvocationRequest / HookInvocationResponse.
  • Installs ServerHooksMiddleware for PreToolUse, PostToolUse, Stop, SubagentStart, and SubagentStop, gated by the session's configured server events so idle sessions skip the round-trip.
  • Creates a session-scoped client HooksRuntime and fulfills server interrupts from both the Textual adapter and headless runner.
  • Applies deny/ask as blocking tool errors for now; ask approval UX remains for DCD-71.

Review Guide

  1. hooks/server_middleware.py — middleware, gating, interrupt raise, decision apply
  2. hooks/interrupt.pyhook_invocation pause/resume contract
  3. hooks/client.py — client fulfillment + resume ledger
  4. agent.py — middleware install (main + subagents)
  5. tui/textual_adapter.py, client/non_interactive.py — interrupt fulfillment call sites
  6. Skim: envelope.py, context.py, runtime.py, _cli_context.py, small app/auto_mode/main/snapshot/engine touches
  7. Tests: test_server_lifecycle.py, then test_agent / test_non_interactive / test_engine
Test plan
  • uv run --group test pytest tests/unit_tests/hooks — 133 passed
  • make lint in libs/code

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 23, 2026
@johannes117
Johannes du Plessis (johannes117) marked this pull request as ready for review July 23, 2026 20:36
@johannes117
Johannes du Plessis (johannes117) marked this pull request as draft July 23, 2026 20:36

@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/agent.py Outdated
Comment thread libs/code/deepagents_code/hooks/server_middleware.py Outdated
Comment thread libs/code/deepagents_code/hooks/server_middleware.py Outdated
@johannes117
Johannes du Plessis (johannes117) marked this pull request as ready for review July 23, 2026 22:10

@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/server_middleware.py
Comment thread libs/code/deepagents_code/agent.py Outdated

@corridor-security corridor-security 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.

Non-interactive mode unconditionally trusts project hook configuration, enabling arbitrary command execution from a committed .deepagents/hooks.json when running against an untrusted repository in CI. A SubagentStart hook denial is also not enforced: the middleware only rewrites the task description and still calls the tool handler, allowing a hook policy intended to block subagent spawning to be bypassed.

Comment thread libs/code/deepagents_code/client/non_interactive.py Outdated
Comment thread libs/code/deepagents_code/hooks/server_middleware.py Outdated
Base automatically changed from johannes117/code/dcd-70-hooks-legacy-migration to main July 24, 2026 15:02
Wire PreToolUse, PostToolUse, Stop, SubagentStart, and SubagentStop
through LangGraph interrupts so the client HooksRuntime can execute
handlers and return typed decisions.

Co-authored-by: Johannes du Plessis <johannesduplessis117@gmail.com>
Co-authored-by: Johannes du Plessis <johannesduplessis117@gmail.com>
Co-authored-by: Johannes du Plessis <johannesduplessis117@gmail.com>
Co-authored-by: Johannes du Plessis <johannesduplessis117@gmail.com>
Apply PreToolUse ask/context/continue, surface notices, wire workspace trust,
mount middleware on subagents, and harden Stop/SubagentStop decision handling.

Co-authored-by: Cursor <cursoragent@cursor.com>
@johannes117
Johannes du Plessis (johannes117) force-pushed the johannes/code/dcd-69-server-lifecycle-f6bb branch from 2028620 to 54c2175 Compare July 24, 2026 15:10
Enforce SubagentStart denies, skip Stop on subagent graphs, and require
`--trust-project-hooks` before loading project hook commands headlessly.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Preserve server hook identity across graph replay, run pre-tool policy before HITL, and deduplicate client fulfillment side effects.

Co-authored-by: Cursor <cursoragent@cursor.com>

@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 2 potential issues.

Open in WebView Open SWE trace

Comment thread libs/code/deepagents_code/agent.py Outdated
Comment thread libs/code/deepagents_code/hooks/server_middleware.py Outdated

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.

Started reviewing this over the weekend, apparently I didn't press submit sorry.

These may just be noise by now

Comment thread libs/code/deepagents_code/app.py
Comment thread libs/code/deepagents_code/app.py
Comment thread libs/code/deepagents_code/hooks/server_middleware.py Outdated
fields = _context_mapping(runtime_context)
snapshot_id = fields.get("hooks_snapshot_id")
events = fields.get("hooks_server_events")
if not isinstance(snapshot_id, str) or not snapshot_id:

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.

I mentioned this in a comment on _context_mapping but we're not making life easier for ourselves by returning dict[str, Any] from the context mapping fn - case in point this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

addressed

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

Stamping, but left a few questions which are worth ensuring we know the answer to

Didn't get too into depth but checked out the core files + systems

Comment thread libs/code/deepagents_code/hooks/server_middleware.py
Comment thread libs/code/deepagents_code/hooks/interrupt.py
Comment thread libs/code/deepagents_code/hooks/interrupt.py Outdated
Comment thread libs/code/deepagents_code/hooks/interrupt.py
Comment thread libs/code/deepagents_code/hooks/client.py
Comment thread libs/code/deepagents_code/agent.py
A hook resume against a stale configuration snapshot previously surfaced as
an untyped ValueError traceback from the stream loop. The client fulfillment
now raises HooksSnapshotChangedError, and both fulfillment call sites
(Textual adapter and non-interactive runner) re-raise server-side resume
validation failures as the same typed error so callers can distinguish the
stale-checkpoint case and restart the turn.
The hook interrupt/resume payloads are produced by pydantic dump in JSON
mode, so the shared JsonObject alias describes them more precisely than
dict[str, Any] / dict[str, object]. Also drops the unused _ResumePayload
alias.
@johannes117
Johannes du Plessis (johannes117) merged commit 2487c4b into main Jul 27, 2026
55 checks passed
@johannes117
Johannes du Plessis (johannes117) deleted the johannes/code/dcd-69-server-lifecycle-f6bb branch July 27, 2026 23:20
Johannes du Plessis (johannes117) added a commit that referenced this pull request Jul 27, 2026
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.

Re-anchored onto the rebuilt dcd-71 branch after #4997 squash-merged;
carries the full PR #5044 delta.
Johannes du Plessis (johannes117) added a commit that referenced this pull request Jul 27, 2026
Hook execution now reports progress, warnings, notices, and permission
decisions consistently in the TUI and non-interactive client. Configured
statusMessage values appear while handlers run, and server-owned hook output
is no longer confined to logs.

Re-anchored onto the rebuilt project-trust branch after #4997 squash-merged;
carries the full PR #5045 delta.
Johannes du Plessis (johannes117) added a commit that referenced this pull request Jul 28, 2026
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.
Johannes du Plessis (johannes117) added a commit that referenced this pull request Jul 28, 2026
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.

Re-anchored onto the rebuilt dcd-71 branch after #4997 squash-merged;
carries the full PR #5044 delta.
Johannes du Plessis (johannes117) added a commit that referenced this pull request Jul 28, 2026
Hook execution now reports progress, warnings, notices, and permission
decisions consistently in the TUI and non-interactive client. Configured
statusMessage values appear while handlers run, and server-owned hook output
is no longer confined to logs.

Re-anchored onto the rebuilt project-trust branch after #4997 squash-merged;
carries the full PR #5045 delta.
Alexander Olsen (aolsenjazz) added a commit that referenced this pull request Jul 29, 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.

## 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:
    - #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>
Johannes du Plessis (johannes117) added a commit that referenced this pull request Jul 29, 2026
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.

Re-anchored onto the rebuilt dcd-71 branch after #4997 squash-merged;
carries the full PR #5044 delta.
Johannes du Plessis (johannes117) added a commit that referenced this pull request Jul 29, 2026
Hook execution now reports progress, warnings, notices, and permission
decisions consistently in the TUI and non-interactive client. Configured
statusMessage values appear while handlers run, and server-owned hook output
is no longer confined to logs.

Re-anchored onto the rebuilt project-trust branch after #4997 squash-merged;
carries the full PR #5045 delta.
Johannes du Plessis (johannes117) added a commit that referenced this pull request Jul 29, 2026
Hook execution now reports progress, warnings, notices, and permission
decisions consistently in the TUI and non-interactive client. Configured
statusMessage values appear while handlers run, and server-owned hook output
is no longer confined to logs.

Re-anchored onto the rebuilt project-trust branch after #4997 squash-merged;
carries the full PR #5045 delta.
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>
Johannes du Plessis (johannes117) added a commit that referenced this pull request Jul 29, 2026
Hook execution now reports progress, warnings, notices, and permission
decisions consistently in the TUI and non-interactive client. Configured
statusMessage values appear while handlers run, and server-owned hook output
is no longer confined to logs.

Re-anchored onto the rebuilt project-trust branch after #4997 squash-merged;
carries the full PR #5045 delta.
Marcelo5444 pushed a commit to Marcelo5444/deepagents that referenced this pull request Jul 30, 2026
…4997)

<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
Depends on langchain-ai#4971
Closes DCD-69

Server-owned Hooks v2 events now pause the agent over LangGraph
interrupts, run on the client `HooksRuntime`, and resume with typed
decisions.

---

This starts the DCD-69 stack on top of the DCD-70 leaf (langchain-ai#4971).

- Adds a versioned `hook_invocation` interrupt/resume transport around
`HookInvocationRequest` / `HookInvocationResponse`.
- Installs `ServerHooksMiddleware` for `PreToolUse`, `PostToolUse`,
`Stop`, `SubagentStart`, and `SubagentStop`, gated by the session's
configured server events so idle sessions skip the round-trip.
- Creates a session-scoped client `HooksRuntime` and fulfills server
interrupts from both the Textual adapter and headless runner.
- Applies `deny`/`ask` as blocking tool errors for now; `ask` approval
UX remains for DCD-71.

## Review Guide

1. `hooks/server_middleware.py` — middleware, gating, interrupt raise,
decision apply
2. `hooks/interrupt.py` — `hook_invocation` pause/resume contract
3. `hooks/client.py` — client fulfillment + resume ledger
4. `agent.py` — middleware install (main + subagents)
5. `tui/textual_adapter.py`, `client/non_interactive.py` — interrupt
fulfillment call sites
6. Skim: `envelope.py`, `context.py`, `runtime.py`, `_cli_context.py`,
small `app`/`auto_mode`/`main`/`snapshot`/`engine` touches
7. Tests: `test_server_lifecycle.py`, then `test_agent` /
`test_non_interactive` / `test_engine`

<details>
<summary>Test plan</summary>

- `uv run --group test pytest tests/unit_tests/hooks` — 133 passed
- `make lint` in `libs/code`

</details>


<!-- branch-stack-start -->

-------------------------
- main
- **feat(code): integrate Hooks v2 server lifecycle events**
:point_left:
    - langchain-ai#5010
      - langchain-ai#5044
        - 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 Agent <cursoragent@cursor.com>
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.

4 participants