Skip to content

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

Closed
Johannes du Plessis (johannes117) wants to merge 23 commits into
mainfrom
johannes/code/dcd-71-client-lifecycle
Closed

feat(code): integrate Hooks v2 client lifecycle events#5010
Johannes du Plessis (johannes117) wants to merge 23 commits into
mainfrom
johannes/code/dcd-71-client-lifecycle

Conversation

@johannes117

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

Copy link
Copy Markdown
Contributor

Depends on #4997
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.

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
  • make lint
  • Hooks lifecycle, engine, headless, Textual adapter, and app unit suites
  • Full make test: 10,618 passed; three unrelated terminal-width rendering assertions fail in this local terminal environment

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
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>
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>
@johannes117
Johannes du Plessis (johannes117) force-pushed the johannes/code/dcd-71-client-lifecycle branch from 20ad754 to c2492e3 Compare July 24, 2026 15:30
Co-authored-by: Cursor <cursoragent@cursor.com>
Wire SessionStart/End, Notification, PermissionRequest, and compact
SessionStart through the client HooksRuntime, and keep project hooks
behind `--trust-project-hooks` after rebasing onto the server-lifecycle
hardening.

Co-authored-by: Cursor <cursoragent@cursor.com>
@johannes117
Johannes du Plessis (johannes117) force-pushed the johannes/code/dcd-71-client-lifecycle branch from c2492e3 to 8a15a16 Compare July 24, 2026 15:32
Initialize session state inline before SessionStart instead of waiting
on a Textual worker Event that never progresses under that wait.

Co-authored-by: Cursor <cursoragent@cursor.com>
Stop deriving the transcript store from `config_dir`, which let tests and
project-local overrides create `.deepagents/transcripts` under the package
tree. Transcripts now always default to `~/.deepagents/transcripts`.

Co-authored-by: Cursor <cursoragent@cursor.com>
Avoid `to_thread` during session start so server-ready startup completes
within the same event-loop turns that tests and the UI expect.

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 1 potential issue.

Open in WebView Open SWE trace

Comment thread libs/code/deepagents_code/app.py
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>
Populate production transcripts and place prompt and compaction compatibility events at replay-safe lifecycle boundaries across interactive and headless clients.

Co-authored-by: Cursor <cursoragent@cursor.com>
Johannes du Plessis (johannes117) added a commit that referenced this pull request Jul 27, 2026
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
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.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:
    - #5010
      - #5044
        - #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>
Base automatically changed from johannes/code/dcd-69-server-lifecycle-f6bb to main July 27, 2026 23:20
@johannes117
Johannes du Plessis (johannes117) deleted the johannes/code/dcd-71-client-lifecycle branch July 27, 2026 23:22
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.
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>
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>
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>
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.

3 participants