feat: add turn_end observer hook#2578
Open
AresNing wants to merge 5 commits into
Open
Conversation
Contributor
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
Thanks @AresNing for taking the time to contribute. This repository is currently observing a maintainer-managed contribution gate in dry-run mode, so this pull request is staying open. When enforcement is enabled, pull requests from contributors who are not listed in Please read |
774217e to
74d69c3
Compare
9 tasks
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.
Summary
Adds the Phase 2
turn_endlifecycle hook from #1364 as an observer-only post-turn event.Scope:
HookEvent::TurnEnd/event = "turn_end"discovery via/hooks events.turn_endfrom the TUITurnCompletebranch after post-turn state updates and before queued-message dispatch.continue_on_errorobserver semantics, and updates the Hooks need mutation rights on user submit and a turn-end event #1364 RFC baseline.web/package-lock.jsonso Web Frontend CI can runnpm ciafter the web docs change.Not in this slice:
turn_end.Builds on: #2434
Issues: Refs #1364 (partial)
Testing
cargo fmt --checkcargo checkcargo clippy --workspace --all-targets --all-featurescargo test -p codewhale-tui hooks::env HOME=/private/tmp/codewhale-test-home-turnend CARGO_HOME=/Users/aresning/.cargo RUSTUP_HOME=/Users/aresning/.rustup cargo test -p codewhale-tui mcp::tests::legacy_sse_closed_stream_reconnects_and_retries_tool_call --all-featuresenv HOME=/private/tmp/codewhale-test-home-turnend CARGO_HOME=/Users/aresning/.cargo RUSTUP_HOME=/Users/aresning/.rustup RUST_TEST_THREADS=1 cargo test --workspace --all-featuresnpm install --package-lock-only --ignore-scripts --registry=https://registry.npmjs.org/inweb/npm ci --ignore-scripts --registry=https://registry.npmjs.org/inweb/npm run lintinweb/npx tsc --noEmitinweb/git diff --checkNotes:
~/.deepseek/state.db; final full run used isolatedHOME.connection closed before message completed); the exact test passed, and the final single-thread full workspace run passed.npm ciwithout--ignore-scriptshung in a macOS postinstall path, so dependency/lockfile validation used--ignore-scripts; GitHub Actions runs fullnpm cion Ubuntu.continue_on_errordocumentation feedback was addressed and the review thread is resolved.Checklist
Greptile Summary
This PR implements the Phase 2
turn_endobserver hook from RFC #1364, adding a fire-and-forget post-turn lifecycle event that delivers a structured JSON payload to user-configured shell hooks after every terminal turn outcome.HookEvent::TurnEndwith a newexecute_structured_observerhelper that iterates all matching hooks unconditionally (ignoringcontinue_on_errorfor sequencing), logs failures as warnings, and never blocks the UI — behaviour explicitly documented and covered by a dedicated test.ui.rsviatokio::task::spawn_blockingafter all post-turn state (token counters, cost, receipts, persistence scheduling) has been updated and before any queued-message dispatch, so the payload reflects the fully settled turn state./hooks events,docs/CONFIGURATION.md, the web docs, and the RFC baseline; syncsweb/package-lock.jsonfor CI.Confidence Score: 5/5
Safe to merge — the hook is purely observer-only, dispatched off the UI thread, and can never affect turn state or block the next user action.
The turn_end observer is entirely fire-and-forget: the JoinHandle from spawn_blocking is dropped, hook failures are logged and discarded, and the payload is built from already-settled app state. The intentional deviation from continue_on_error sequencing is tested and explicitly called out in the documentation. No existing hook contract is changed.
No files require special attention.
Important Files Changed
Sequence Diagram
sequenceDiagram participant Engine participant UI as run_event_loop (ui.rs) participant App as App State participant Blocking as spawn_blocking thread participant Hook as turn_end shell hook Engine->>UI: "EngineEvent::TurnComplete { status, usage, error }" UI->>App: Clear loading/streaming state UI->>App: Set runtime_turn_status UI->>App: Update token counters and cost UI->>App: Update notifications and receipts UI->>App: Schedule session persistence alt has_hooks_for_event(TurnEnd) UI->>UI: Build turn_end_payload(TurnEndPayloadInput) UI->>UI: Clone HookExecutor UI->>Blocking: spawn_blocking (fire-and-forget) Note over UI,Blocking: JoinHandle dropped immediately end UI->>App: pop_queued_message() dispatch next queued message Blocking->>Hook: stdin JSON payload Hook-->>Blocking: stdout ignored stderr logged on failure Blocking-->>Blocking: warn on non-zero exit no caller impactReviews (4): Last reviewed commit: "fix: gate hook test fs import" | Re-trigger Greptile