Skip to content

fix(core): ensure chat compression summary persists across session resumes#21345

Open
Abhijit-2592 wants to merge 3 commits intomainfrom
abhijit-2592/fix-compress-persistence
Open

fix(core): ensure chat compression summary persists across session resumes#21345
Abhijit-2592 wants to merge 3 commits intomainfrom
abhijit-2592/fix-compress-persistence

Conversation

@Abhijit-2592
Copy link
Copy Markdown
Contributor

Summary

This PR fixes a bug where the /compress command's summary was not persistent across session exits and resumes.

Details

The issue was that while /compress updated the in-memory chat history, it failed to sync this change to the session file on disk during the transition to a new chat session.

Key changes:

  • ChatRecordingService.initialize: Updated to accept an optional initialHistory parameter. When provided during session resumption (which happens internally after compression), it overwrites the messages array in the session file with the new history.
  • GeminiChat Constructor: Updated to pass its history to the ChatRecordingService.initialize call, ensuring immediate persistence of the starting history (e.g., the compression summary).
  • apiContentToMessageRecords: Added a private helper in ChatRecordingService to convert API Content objects into storage-compatible MessageRecord objects.
  • Type Safety: Addressed a lint error regarding an unsafe type assertion in the new helper.

Related Issues

Fixes #21335

How to Validate

  1. Start a chat with gemini.
  2. Build up some history with several messages.
  3. Run /compress and wait for the "Chat history compressed" message.
  4. Exit the CLI (ctrl+d or /exit).
  5. Resume the session with gemini --resume latest.
  6. Verify that the history correctly starts with the Chat summary.

Alternatively, run the new unit test:
npm test -w @google/gemini-cli-core -- src/services/chatRecordingService.test.ts

Pre-Merge Checklist

  • Updated relevant documentation and README (if needed)
  • Added/updated tests (if needed)
  • Noted breaking changes (if any)
  • Validated on required platforms/methods:
    • MacOS
      • npm run

…sumes

- Modified ChatRecordingService.initialize to accept an optional initialHistory parameter.
- When initialHistory is provided during session resumption (e.g., after chat compression), it now overwrites the messages in the session file on disk.
- Updated GeminiChat constructor to pass the history to ChatRecordingService.initialize.
- Implemented apiContentToMessageRecords helper to convert API Content objects to storage-compatible MessageRecord objects.
- This ensures that the compressed chat history (the summary) is immediately synced to disk, preventing it from being lost when the session is closed and resumed.
- Added a unit test in chatRecordingService.test.ts to verify the new overwrite behavior.

Fixes #21335
@Abhijit-2592 Abhijit-2592 requested a review from a team as a code owner March 5, 2026 23:55
@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 critical issue where chat compression summaries were not being properly persisted across user sessions. By modifying the chat recording service to accept and save an initial history upon session initialization, it ensures that compressed chat states are correctly maintained, significantly improving the reliability of the /compress feature. The changes involve updating method signatures, adding new utility functions for data conversion, and enhancing session data handling to reflect the current chat state accurately on disk.

Highlights

  • Chat Compression Persistence: Fixed a bug where the /compress command's summary was not persistent across session exits and resumes, ensuring the compressed history is correctly saved and loaded.
  • ChatRecordingService Update: The ChatRecordingService.initialize method was updated to accept an optional initialHistory parameter, allowing it to overwrite the session's messages on disk when resuming with a new history (e.g., after compression).
  • GeminiChat Constructor Modification: The GeminiChat constructor now passes its history to ChatRecordingService.initialize, ensuring immediate persistence of the starting chat history.
  • API Content Conversion Utility: A new private helper method, apiContentToMessageRecords, was added to ChatRecordingService to convert API Content objects into storage-compatible MessageRecord objects.
  • New Unit Test: A new unit test was added to chatRecordingService.test.ts to validate that existing messages are correctly overwritten when initialHistory is provided during initialization.
Changelog
  • packages/core/src/core/geminiChat.ts
    • Modified the GeminiChat constructor to pass the current history to this.chatRecordingService.initialize.
  • packages/core/src/services/chatRecordingService.test.ts
    • Added a new test case should overwrite existing messages if initialHistory is provided to verify the persistence of compressed chat summaries.
  • packages/core/src/services/chatRecordingService.ts
    • Updated the initialize method signature to include an optional initialHistory parameter.
    • Implemented logic within initialize to overwrite existing session messages with initialHistory when resuming a session.
    • Created a new private helper method apiContentToMessageRecords to convert Content[] to MessageRecord[].
    • Applied apiContentToMessageRecords when initializing a new session or overwriting messages in an existing one.
    • Added JSDoc comments for the new initialHistory parameter.
    • Added an eslint-disable-next-line comment for an unsafe type assertion.
Activity
  • The author has provided detailed steps on how to validate the fix, including starting a chat, compressing history, exiting, resuming, and verifying the summary.
  • A new unit test was added to cover the scenario of overwriting messages with initial history, indicating test coverage for the fix.
  • The pre-merge checklist indicates that tests were added/updated and validation was performed on MacOS using npm run.
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 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 counter productive. 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.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

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.

Copy link
Copy Markdown
Contributor

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

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 addresses a bug where chat history compression summaries were not being persisted across sessions. The fix involves updating the ChatRecordingService to accept an initial history, which is then used to overwrite the session file on disk. The changes are logical, and a new unit test has been added to validate the fix. My review includes one suggestion to improve type safety in a new helper function by removing an unsafe type assertion.

Comment thread packages/core/src/services/chatRecordingService.ts
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 5, 2026

Size Change: +1.59 kB (+0.01%)

Total Size: 26 MB

Filename Size Change
./bundle/gemini.js 25.5 MB +1.59 kB (+0.01%)
ℹ️ View Unchanged
Filename Size
./bundle/node_modules/@google/gemini-cli-devtools/dist/client/main.js 221 kB
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/_client-assets.js 227 kB
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/index.js 11.5 kB
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/types.js 132 B
./bundle/sandbox-macos-permissive-open.sb 890 B
./bundle/sandbox-macos-permissive-proxied.sb 1.31 kB
./bundle/sandbox-macos-restrictive-open.sb 3.36 kB
./bundle/sandbox-macos-restrictive-proxied.sb 3.56 kB
./bundle/sandbox-macos-strict-open.sb 4.82 kB
./bundle/sandbox-macos-strict-proxied.sb 5.02 kB

compressed-size-action

joshualitt
joshualitt previously approved these changes Mar 6, 2026
@gemini-cli gemini-cli bot added area/agent Issues related to Core Agent, Tools, Memory, Sub-Agents, Hooks, Agent Quality 🔒 maintainer only ⛔ Do not contribute. Internal roadmap item. labels Mar 6, 2026
@joshualitt joshualitt dismissed their stale review March 6, 2026 00:51

More offline discussion required, will revisit soon.

… data loss

- Modified ChatRecordingService.initialize to accept an explicit overwriteHistory flag.
- When overwriteHistory is true (e.g., after chat compression), it overwrites disk messages; otherwise, it preserves existing historical metadata (IDs/timestamps).
- Updated GeminiChat and GeminiClient.startChat to propagate this flag, ensuring it is only true during the compression flow.
- Refactored apiContentToMessageRecords for full type safety by removing unsafe type assertions and adding part fallbacks, as suggested in PR review.
- Updated unit tests in chatRecordingService.test.ts and client.test.ts to verify the new behavior and fix regression.
- Verified all workspace and integration tests pass via preflight.

Fixes #21335
Copy link
Copy Markdown
Contributor

@jacob314 jacob314 left a comment

Choose a reason for hiding this comment

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

From /review-frontend:

Summary

While this PR successfully fixes the bug where chat compression summaries weren't persisting across session resumes (Fixes #21335), the current implementation introduces a critical data-loss regression that severely breaks the CLI frontend UI.

By passing overwriteHistory=true, the ChatRecordingService re-synthesizes the entire preserved history from raw Gemini Content[] objects back into MessageRecord[]s via the newly added apiContentToMessageRecords function. This approach is fundamentally lossy and strips critical metadata required by the React frontend.

Findings

🔴 Critical UX/Frontend Regressions

  • Breaks the Expandable Tool UI (HistoryItemToolGroup): The apiContentToMessageRecords function entirely strips the toolCalls array (and its metadata like displayName, renderOutputAsMarkdown, etc.) from the preserved portion of the history (historyToKeepTruncated).
    • Frontend Impact: When the CLI UI resumes the session, convertSessionToHistoryFormats relies on msg.toolCalls to render the beautiful, expandable HistoryItemToolGroup components. Without this metadata, tool invocations will fall back to rendering literal raw strings (e.g., [Function Call: list_directory] and [Function Response: list_directory]) in standard message bubbles.
  • Breaks the "Thinking..." and Token UI: Similar to toolCalls, the thoughts array and tokens object are dropped from the preserved history.
    • Frontend Impact: The frontend will no longer be able to render the "Thinking..." dropdown or token usage stats for any historical messages preserved after compression.
  • Destroys the Session Timeline: apiContentToMessageRecords loops through the retained history and assigns new Date().toISOString() to every single message.
    • Frontend Impact: The entire preserved history will incorrectly appear in the UI as having occurred at the exact moment the compression happened, breaking the display of elapsed time and chronological flow.
  • Role Mapping Mismatch: If a Content object contains a functionResponse (which uses role: 'function' or sometimes role: 'user' depending on normalization), it falls into the else block and is mapped to type: 'user'. The frontend specifically checks that tool calls are msg.type !== 'user', further breaking the rendering pipeline.

Conclusion: Request Changes

The approach of rebuilding the session file from Content[] is fundamentally lossy because the Gemini SDK's Content objects do not carry the rich metadata that ChatRecordingService and the CLI UI require.

Instead of ChatRecordingService blindly overwriting the entire file using apiContentToMessageRecords, I recommend one of the following approaches:

  1. Slice the existing MessageRecord[] (Recommended): Have ChatCompressionService calculate how many messages were truncated/compressed. Then, pass that split index to ChatRecordingService, which can slice its existing conversation.messages array (preserving all IDs, timestamps, and tool/thought metadata) and prepend a single, newly constructed MessageRecord for the summary text.
  2. Pass a Summary Event: Instead of a full overwrite, emit a "HistoryCompressed" event to the ChatRecordingService containing just the summary text and the number of messages to retain, allowing it to modify the JSON safely in place without losing the tail end of the history.

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

Labels

area/agent Issues related to Core Agent, Tools, Memory, Sub-Agents, Hooks, Agent Quality 🔒 maintainer only ⛔ Do not contribute. Internal roadmap item.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

/compress command is not persistent across session resume

3 participants