Skip to content

fix(core): ignore stale update_topic calls after a session reset#28153

Open
abhay-codes07 wants to merge 1 commit into
google-gemini:mainfrom
abhay-codes07:fix/update-topic-session-guard-26402
Open

fix(core): ignore stale update_topic calls after a session reset#28153
abhay-codes07 wants to merge 1 commit into
google-gemini:mainfrom
abhay-codes07:fix/update-topic-session-guard-26402

Conversation

@abhay-codes07

Copy link
Copy Markdown

Summary

update_topic writes to the shared topicState singleton on Config unconditionally (topicTool.ts):

this.config.topicState.setTopic(title, strategicIntent);

If the model emits a final update_topic tool call around the moment the user runs /clear, that orphaned call can execute after the session has been reset (/clearresetNewSessionState(newId) resets topicState and assigns a new session id). The late write re-populates topicState, 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:

  • the call's abortSignal is aborted, or
  • the current session id no longer matches (the session was reset, e.g. via /clear).
this.scheduledSessionId = config.getSessionId();
// ...
if (options.abortSignal?.aborted ||
    this.config.getSessionId() !== this.scheduledSessionId) {
  return { llmContent: 'Topic update skipped: the session was reset before it could be applied.', returnDisplay: '' };
}

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:

  • a call scheduled in one session that executes after the session id changes does not write to topicState (the re-poisoning scenario);
  • an aborted call does not write to topicState;
  • existing same-session behavior is unchanged (topic/intent still applied).

All 11 tests pass; eslint and tsc --noEmit are 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 /clear to 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

`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
@abhay-codes07 abhay-codes07 requested a review from a team as a code owner June 25, 2026 20:17
@github-actions github-actions Bot added the size/m A medium sized PR label Jun 25, 2026
@github-actions

Copy link
Copy Markdown

📊 PR Size: size/M

  • Lines changed: 61
  • Additions: +60
  • Deletions: -1
  • Files changed: 2

@github-actions

Copy link
Copy Markdown

🛑 Action Required: Evaluation Approval

Steering changes have been detected in this PR. To prevent regressions, a maintainer must approve the evaluation run before this PR can be merged.

Maintainers:

  1. Go to the Workflow Run Summary.
  2. Click the yellow 'Review deployments' button.
  3. Select the 'eval-gate' environment and click 'Approve'.

Once approved, the evaluation results will be posted here automatically.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, 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

  • Session Guard Implementation: Introduced a session ID check in the UpdateTopicTool to ensure that topic updates are only applied if the current session matches the session in which the tool call was originally scheduled.
  • Abort Signal Handling: Added an explicit check for abort signals during tool execution to prevent orphaned calls from modifying the shared topic state.
  • Regression Testing: Added unit tests to verify that topic updates are skipped when the session is reset or the call is aborted, preventing cross-session state leakage.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +67 to +74
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: '' };
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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.

Suggested change
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: '',
};
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core Issues related to User Interface, OS Support, Core Functionality priority/p1 Important and should be addressed in the near term. size/m A medium sized PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

/clear does not abort in-flight stream; update_topic leaks Active Topic into next session

1 participant