Skip to content

fix(sessions): settle interrupted tool calls#395

Open
rluisr wants to merge 1 commit into
anthropics:mainfrom
rluisr:fix/session-interrupt-tool-calls
Open

fix(sessions): settle interrupted tool calls#395
rluisr wants to merge 1 commit into
anthropics:mainfrom
rluisr:fix/session-interrupt-tool-calls

Conversation

@rluisr

@rluisr rluisr commented Jul 15, 2026

Copy link
Copy Markdown

Fixes #393.

Summary

Prevent SessionToolRunner from repeatedly dispatching tool calls invalidated by a processed session-wide user.interrupt.

  • track a high-water interrupt cutoff using processed_at, rather than relying on created_at list order
  • settle builtin and custom tool calls at or before that cutoff across live events, reconnect history, queued dispatches, confirmation holds, and active execution
  • cancel the active tool context when its call is interrupted
  • ignore queued interrupts and interrupts targeted at another session thread
  • re-check settlement history after a permanent result rejection, covering the race where the server processes an interrupt or another runner's result before the local SSE stream observes it
  • preserve reconcile retries for transient send failures, but terminate the runner for an unresolved permanent 4xx so an unrecoverable call cannot be dispatched forever
  • preserve completed/rejected dispatch records when runner cancellation races with result buffering

Session event history is sorted by created_at. Because a queued user interrupt receives processed_at later, an invalidated tool call can appear after the interrupt in listed history. The runner therefore stores each tool call's processing timestamp and applies the latest processed global interrupt as a cutoff.

The history refresh on permanent 4xx only runs on the exceptional path. It records all tool results and the latest processed global interrupt before deciding whether the rejected call was settled elsewhere.

The empty-output 400 described in #377 is independent and is handled separately in #394.

Verification

  • go test -count=1 -run '^(TestSessionToolRunner_|TestIdleClock_)' .
  • go test -race -count=1 -run '^(TestSessionToolRunner_|TestIdleClock_)' .
  • go test -count=50 -run 'TestSessionToolRunner_(GlobalInterrupt|PermanentRejectionRefreshesSettlementHistory|ExternalResultReleasesConfirmationHold|DrainsRejectedCall|SurfaceCall)' .
  • ./scripts/lint
  • go vet . ./tools/agenttoolset
  • git diff --check

@rluisr
rluisr requested a review from a team as a code owner July 15, 2026 20:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SessionToolRunner redispatches stale tool calls after a processed session-wide interrupt

1 participant