Repository navigation
fix(loop): cancelled runs skip unstarted tools, every message gets events, every TurnStart is paired (#243) - #244
Merged
Merged
Conversation
PR 0) Records each hook with the number of events sent when it fired, so the timeline is exact. Covers a plain answer, a tool call, a refusal, a provider error, a cancel while streaming, a cancel while tools run, a turn limit, and a rejected input. No behaviour change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T7iq5hpndSiHQcnAsywKuG
… notes, continue_loop and errors before output Adds the cases the review found missing: the turn hook runs once per provider attempt; filters and middleware run in installation order (a Modify feeds the next, a Deny stops the chain, a Warn passes); a sourced tool goes through middleware; a turn hook's note lands on the latest user message; continue_loop runs no input filters; a provider error or cancel before any output leaves a message with no MessageStart/MessageEnd; a cancel after streaming started ends the message as Aborted. Renames cancelled_while_tools_run to cancelled_before_tools_start and corrects the notes on current behaviour. The original eight timelines are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T7iq5hpndSiHQcnAsywKuG
…d message, pair every TurnStart (#243) - A tool call reached after the run was cancelled (before middleware, or while middleware awaited) is answered with an error result and never runs. It used to run, with the cancel seen only at the next turn. - A final failure or cancel before any output is announced with MessageStart/MessageEnd, so every message in the history has its events (including a run aborted during a retry's backoff). - A turn stopped by an execution limit or by on_before_turn returning false sends TurnEnd. Updates the pinned timelines in tests/hook_order_test.rs (each change is a diff there), adds cases for on_before_turn stopping a run and for a provider that streams nothing, and updates two retry_safe tests and the docs that described the old behaviour. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T7iq5hpndSiHQcnAsywKuG
- Done without Start: the forwarder now sends MessageStart first, so every message in the history has both events. - Doc comments of push_stop_marker and unexecuted_tool_call were split from their functions by the new close_turn / cancelled_tool_call. - retry_safe_events rustdoc still described the old abort behaviour. - Comments on the forwarder JoinError fallback and on close_turn with an empty history. - Tests: a cancel while middleware decides (covers the post-middleware check), a cancel between Sequential tools and between Batched tools (second call answered, marker once), and Done without Start. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T7iq5hpndSiHQcnAsywKuG
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T7iq5hpndSiHQcnAsywKuG # Conflicts: # tests/hook_order_test.rs
This was referenced Oct 6, 2026
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.
Closes #243. Stacked on #242, which adds the hook-order test that this PR updates. Merge #242 first; this PR's base then moves to
main.The three fixes
execute_single_toolchecks the run's cancel token before middleware, and again after middleware (which can await). An unstarted call is answered throughunexecuted_tool_callwithTool call not run: the run was cancelled., soToolExecutionStart/Endstay paired and the transcript stays valid. The run then ends with[Agent stopped: cancelled]as before. A tool already running still gets the cancel via itsToolContext.MessageStart+MessageEnd(AttemptEvents::is_silent→announce). This includes a run aborted during a retry's backoff. Retried attempts that failed before output still get onlyProviderRetry. A provider that returnsOkwithout streaming anything is announced the same way.TurnStarthas aTurnEnd. A turn stopped by an execution limit or byon_before_turnreturningfalsenow sendsTurnEnd: the history's last message (the stop marker, for a limit) and no tool results.Evidence
tests/hook_order_test.rspins the timelines, so each behaviour change shows as a diff in this PR:turn_limit: +TurnEnd.provider_error_before_any_output/cancelled_before_any_output: +MessageStart/MessageEnd.cancelled_before_tools_start: the tool no longer runs, and its result is an error.stopped_by_on_before_turnandprovider_streams_nothing.Other tests and docs updated for the new behaviour:
agent_loop_test: cancelling during the backoff now shows the announcedAbortedmessage afterretry:1/4.retry_safe_test: the terminal-abort pattern now ends with[Error, Aborted]. The filter releases the aborted message (renamedthe_filter_and_an_abort_release_only_the_aborted_message).docs/concepts/retry.mdandmessages-events.mddescribed the old behaviour and are corrected. CHANGELOGUnreleased→ Fixed.For downstream consumers
These are new events, not changed ones:
MessageStart/MessageEndpair for failures and aborts that streamed nothing;TurnEndfor turns stopped early.yoyo's
prompt.rsusesMessageEndonly to flush text and countsTurnStart(unchanged). yoyo's full suite runs against the release as usual.Checks
cargo test --all-features: all 56 test binaries pass.--all-featuresand--no-default-features.cargo doc -Dwarnings.🤖 Generated with Claude Code
https://claude.ai/code/session_01T7iq5hpndSiHQcnAsywKuG