fix: handle turn.aborted in ProviderRuntimeIngestion to unblock Copilot stop#16
fix: handle turn.aborted in ProviderRuntimeIngestion to unblock Copilot stop#16
Conversation
…ot stop When the user clicks stop during a Copilot turn, CopilotAdapter emits a turn.aborted runtime event. ProviderRuntimeIngestion only handled turn.completed for lifecycle transitions, leaving the session stuck in "running" with a stale activeTurnId — blocking new sends. Add turn.aborted to the lifecycle event handling so it: - Transitions session status to "interrupted" (maps to "ready" in UI) - Clears activeTurnId to null - Finalizes buffered assistant messages and proposed plans Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis change introduces handling for a new 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts (1)
852-863:⚠️ Potential issue | 🟠 MajorGate
turn.abortedon the current active turn.
turn.abortedis now using theturn.completedfallback path, so a late/duplicate abort with no active turn can still move the session to"interrupted"and clearlastError. Also, the new finalization block does not consult the lifecycle guard, so an abort for a different turn can still flush buffered assistant text / proposed plans from that stale turn. Please restrict abort handling to a matchingactiveTurnId, and add a regression for late/different-turn aborts.Possible tightening
- case "turn.completed": - case "turn.aborted": + case "turn.completed": if (conflictsWithActiveTurn || missingTurnForActiveTurn) { return false; } // Only the active turn may close the lifecycle state. if (activeTurnId !== null && eventTurnId !== undefined) { return sameId(activeTurnId, eventTurnId); } // If no active turn is tracked, accept completion scoped to this thread. return true; + case "turn.aborted": + return ( + activeTurnId !== null && + eventTurnId !== undefined && + sameId(activeTurnId, eventTurnId) + );- if (event.type === "turn.completed" || event.type === "turn.aborted") { + const shouldFinalizeTurn = + event.type === "turn.completed" || + (event.type === "turn.aborted" && + activeTurnId !== null && + eventTurnId !== undefined && + sameId(activeTurnId, eventTurnId)); + if (shouldFinalizeTurn) {As per coding guidelines,
apps/server/**: Prioritize correctness, reliability, and predictable behavior under reconnects, session restarts, partial streams, and provider failures.Also applies to: 1044-1075
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts` around lines 852 - 863, The handling for "turn.aborted" currently falls through to the "turn.completed" path allowing late or out-of-thread aborts to clear session state and flush stale assistant content; change the switch so "turn.aborted" is explicitly gated to only succeed when activeTurnId is non-null and sameId(activeTurnId, eventTurnId) is true (i.e., mirror the explicit active-turn check used for completions) and ensure the finalization block consults the lifecycle guard before clearing lastError or flushing buffered assistant text/proposed plans; update ProviderRuntimeIngestion's turn finalization logic (the block referencing activeTurnId, eventTurnId, sameId, and the lifecycle guard) and add a regression test that sends a late/different-turn "turn.aborted" to assert that session state, lastError, and buffered assistant output remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts`:
- Around line 852-863: The handling for "turn.aborted" currently falls through
to the "turn.completed" path allowing late or out-of-thread aborts to clear
session state and flush stale assistant content; change the switch so
"turn.aborted" is explicitly gated to only succeed when activeTurnId is non-null
and sameId(activeTurnId, eventTurnId) is true (i.e., mirror the explicit
active-turn check used for completions) and ensure the finalization block
consults the lifecycle guard before clearing lastError or flushing buffered
assistant text/proposed plans; update ProviderRuntimeIngestion's turn
finalization logic (the block referencing activeTurnId, eventTurnId, sameId, and
the lifecycle guard) and add a regression test that sends a late/different-turn
"turn.aborted" to assert that session state, lastError, and buffered assistant
output remain unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: d5662301-8d6e-451a-9ae8-9080d8f3691e
📒 Files selected for processing (2)
apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.tsapps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts
A late or duplicate turn.aborted for a stale turn could incorrectly transition the session to 'interrupted' and flush buffered assistant messages from the wrong turn. Gate the finalization block with shouldApplyThreadLifecycle (which verifies the event's turnId matches the active turn). Add regression test for stale-turn abort rejection. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Problem
When the user clicks the stop button during a Copilot turn,
CopilotAdaptercallssession.abort()on the Copilot SDK which emits aturn.abortedruntime event. However,ProviderRuntimeIngestiononly handledturn.completedfor lifecycle transitions — leaving the session stuck in"running"with a staleactiveTurnId, which blocked all subsequent sends.Fix
Added
turn.abortedalongsideturn.completedin four lifecycle handling paths withinProviderRuntimeIngestion.ts:shouldApplyThreadLifecycleguard — allows the abort event through the strict lifecycle filteractiveTurnIdclearing — clears the active turn on abortturn.aborted→"interrupted"(which the UI maps to"ready"phase, re-enabling the input)Tests
Two new tests in
ProviderRuntimeIngestion.test.ts:turn.abortedtransitions session to"interrupted"withactiveTurnIdclearedturn.startedsuccessfully transitions back to"running"All 25 tests pass. Typecheck and lint clean.
Summary by CodeRabbit