fix(core): ignore stale update_topic calls after a session reset#28153
fix(core): ignore stale update_topic calls after a session reset#28153abhay-codes07 wants to merge 1 commit into
Conversation
`update_topic` wrote to the shared `topicState` singleton unconditionally. If the model emitted an `update_topic` call around the moment the user ran /clear, that orphaned tool call could execute after the session was reset and re-populate `topicState`, causing the previous session's `[Active Topic: …]` to be injected into the fresh session's system prompt — so the model would pivot back to the cleared session's goal. Capture the session id the call was scheduled in and skip the write when the call has been aborted or the session id has since changed (e.g. via /clear). This is the abort/session guard the tool was missing. Fixes google-gemini#26402
|
📊 PR Size: size/M
|
🛑 Action Required: Evaluation ApprovalSteering changes have been detected in this PR. To prevent regressions, a maintainer must approve the evaluation run before this PR can be merged. Maintainers:
Once approved, the evaluation results will be posted here automatically. |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses a state leakage issue where stale 'update_topic' tool calls could modify the topic state after a user resets their session (e.g., via '/clear'). By capturing the session ID at the time of tool invocation and validating it against the current session during execution, the fix ensures that orphaned calls do not inadvertently re-inject previous session data into the new session's system prompt. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces guards in UpdateTopicInvocation to prevent writing stale topic states when a session is reset or when the execution is aborted, accompanied by corresponding unit tests. The review feedback suggests separating the error messages for aborted executions versus reset sessions to avoid misleading the LLM with incorrect context.
| if ( | ||
| options.abortSignal?.aborted || | ||
| this.config.getSessionId() !== this.scheduledSessionId | ||
| ) { | ||
| const message = | ||
| 'Topic update skipped: the session was reset before it could be applied.'; | ||
| return { llmContent: message, returnDisplay: '' }; | ||
| } |
There was a problem hiding this comment.
The current implementation returns the same message ('Topic update skipped: the session was reset before it could be applied.') regardless of whether the execution was aborted/cancelled or the session was actually reset. Misleading the LLM with incorrect context (e.g., telling it the session was reset when it was just a normal abort/cancellation) can cause severe logic or behavioral issues in subsequent turns, as the model might think the user ran /clear or reset the session. Distinguishing between these two cases ensures the LLM receives accurate feedback.
| if ( | |
| options.abortSignal?.aborted || | |
| this.config.getSessionId() !== this.scheduledSessionId | |
| ) { | |
| const message = | |
| 'Topic update skipped: the session was reset before it could be applied.'; | |
| return { llmContent: message, returnDisplay: '' }; | |
| } | |
| if (options.abortSignal?.aborted) { | |
| return { | |
| llmContent: 'Topic update skipped: the execution was aborted.', | |
| returnDisplay: '', | |
| }; | |
| } | |
| if (this.config.getSessionId() !== this.scheduledSessionId) { | |
| return { | |
| llmContent: 'Topic update skipped: the session was reset before it could be applied.', | |
| returnDisplay: '', | |
| }; | |
| } |
Summary
update_topicwrites to the sharedtopicStatesingleton onConfigunconditionally (topicTool.ts):If the model emits a final
update_topictool call around the moment the user runs/clear, that orphaned call can execute after the session has been reset (/clear→resetNewSessionState(newId)resetstopicStateand assigns a new session id). The late write re-populatestopicState, so every subsequent prompt re-injects the previous session's[Active Topic: …]into the system prompt and the model pivots back to the cleared session's goal. (This is distinct from #26237/#26238, which only changed the marker formatting — the stale state still gets injected.)The tool had no check that the call was still valid: no abort check, and no check that the current session id still matches the session the call was scheduled in.
Fix
Capture the session id the call was scheduled in (at invocation construction) and skip the write at execute time when:
abortSignalis aborted, or/clear).This is exactly the abort/session guard the issue identifies as missing, and it leaves normal same-session behavior untouched.
Testing
Added unit tests in
topicTool.test.ts:topicState(the re-poisoning scenario);topicState;All 11 tests pass;
eslintandtsc --noEmitare clean for the package.Note on scope
This addresses root cause #1 from the issue (the unguarded write) directly and verifiably. It does not change
/clearto proactively abort the in-flight stream (a separate, cross-cutting concern); the guard makes the orphaned write a no-op regardless, which is what prevents the cross-session topic leak.Fixes #26402