feat(code): integrate Hooks v2 client lifecycle events - #5010
Closed
Johannes du Plessis (johannes117) wants to merge 23 commits into
Closed
feat(code): integrate Hooks v2 client lifecycle events#5010Johannes du Plessis (johannes117) wants to merge 23 commits into
Johannes du Plessis (johannes117) wants to merge 23 commits into
Conversation
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>
Johannes du Plessis (johannes117)
force-pushed
the
johannes/code/dcd-69-server-lifecycle-f6bb
branch
from
July 24, 2026 15:10
2028620 to
54c2175
Compare
Johannes du Plessis (johannes117)
force-pushed
the
johannes/code/dcd-71-client-lifecycle
branch
from
July 24, 2026 15:10
228267c to
20ad754
Compare
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>
Johannes du Plessis (johannes117)
force-pushed
the
johannes/code/dcd-71-client-lifecycle
branch
from
July 24, 2026 15:30
20ad754 to
c2492e3
Compare
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>
Johannes du Plessis (johannes117)
force-pushed
the
johannes/code/dcd-71-client-lifecycle
branch
from
July 24, 2026 15:32
c2492e3 to
8a15a16
Compare
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>
Johannes du Plessis (johannes117)
marked this pull request as ready for review
July 24, 2026 16:32
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>
…es/code/dcd-71-client-lifecycle
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>
…es/code/dcd-71-client-lifecycle
…es/code/dcd-71-client-lifecycle
…es/code/dcd-71-client-lifecycle
…es/code/dcd-71-client-lifecycle
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
Johannes du Plessis (johannes117)
deleted the
johannes/code/dcd-71-client-lifecycle
branch
July 27, 2026 23:22
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>
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.
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.
SessionStartandSessionEndat startup, resume, clear, compact, switch, and exit boundaries.PermissionRequestallow, deny, ask, interrupt, notice, terminal, and stop effects before client approval resolution..gitignore.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
make lintmake test: 10,618 passed; three unrelated terminal-width rendering assertions fail in this local terminal environmentStack generated by Git Town