feat(cli): prefix-cache-preserving compaction (Reasonix-style) - #13077
Open
miroyong wants to merge 2 commits into
Open
feat(cli): prefix-cache-preserving compaction (Reasonix-style)#13077miroyong wants to merge 2 commits into
miroyong wants to merge 2 commits into
Conversation
Ports Reasonix's prefix-cache strategy to the CLI's auto-compaction: - compactChatHistory now pins the cache-stable prefix (system message, first user turn when small, prior digests) verbatim and keeps a token-budgeted recent tail, splicing the new digest in the middle instead of collapsing history to [system, summary]. The prompt prefix stays byte-identical across turns so providers with automatic prompt caching (DeepSeek, OpenAI) serve cache hits on subsequent requests. - No-op when the conversation already fits within pinned prefix + tail, so the summarizer isn't invoked needlessly. - getHistoryForLLM returns the full history: after compaction the stored history already IS the compacted layout, so trimming before the compaction index would drop the cache-stable prefix. - SystemMessageService memoizes the constructed system message per (mode, additionalRules, format, headless), since it is re-fetched on every streaming iteration and its construction re-reads AGENTS.md / runs git status — any change cold-starts the provider's prompt cache. - Compaction digest prompt rewritten as a structured briefing (Standing facts, Goal, Decisions, Files & code, Commands, Errors, Pending & next step) mirroring Reasonix's summarySystemPrompt. Generated with [Continue](https://continue.dev) Co-Authored-By: Continue <noreply@continue.dev>
Contributor
|
All contributors have signed the CLA ✍️ ✅ |
Generated with [Continue](https://continue.dev) Co-Authored-By: Continue <noreply@continue.dev>
Author
SummaryThis PR ports Reasonix-style prefix-cache-preserving compaction to the CLI. The core idea: after a compaction, the prompt prefix stays byte-identical across turns (system message + first user turn verbatim + prior digests), so providers with automatic prompt caching (DeepSeek, OpenAI) serve cache hits on subsequent requests. Changes
Tests
All CLI tests pass on the CI matrix (Node 18/20/22/24 × ubuntu/macos/windows). |
Author
|
I have read the CLA Document and I hereby sign the CLA |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ports Reasonix's prefix-cache strategy to the Continue CLI's auto-compaction so providers with automatic prompt caching (DeepSeek, OpenAI) can serve cache hits across turns.
Problem: today
compactChatHistorycollapses the history into[system, summary], andgetHistoryForLLMtrims everything before the compaction index. Any turn after that starts with a different prompt prefix than the previous turn, cold-starting the provider's prompt cache. Additionally, the system message is reconstructed on every streaming iteration — re-readingAGENTS.md/CLAUDE.mdand re-runninggit status— so any change invalidates the whole prefix.Changes
compaction.tscompactChatHistorypins the cache-stable prefix verbatim (system message, first user turn when ≤1.5K tokens, and any prior digests) and keeps a token-budgeted recent tail (12K tokens, ≥2 messages), splicing the new digest in the middle instead of replacing everything.getHistoryForLLMreturns the full history — after compaction the stored history is the compacted layout (pinned prefix + digest + tail), so trimming before the compaction index would drop the cache-stable prefix.## Standing facts & constraints,## Goal,## Decisions & rationale,## Files & code,## Commands & outcomes,## Errors & fixes,## Pending & next step), mirroring Reasonix'ssummarySystemPrompt.services/SystemMessageService.ts: memoizes the constructed system message per(mode, additionalRules, format, headless), keeping the prefix byte-identical across turns.services/ChatHistoryService.ts:getHistoryForLLMreturns the full history (the compacted layout is already prefix-preserving).compaction.test.ts,compaction.infiniteLoop.test.ts(new tokenizer mock surface),ChatHistoryService.test.ts,SystemMessageService.test.ts.Test plan
npx vitest run src/compaction.test.ts src/compaction.infiniteLoop.test.ts src/compaction.pruneLastMessage.test.ts— 38/38 passnpx vitest run src/services/ChatHistoryService.test.ts src/services/SystemMessageService.test.ts src/systemMessage.test.ts src/session.test.ts src/messageConversion.test.ts src/services/ServiceContainer.test.ts— 99/99 passnpx tsc --noEmit— cleannpx eslinton changed files — cleanNote: the full CLI suite hangs in the TUIChat UI tests on this Android/Termux environment (unrelated, pre-existing).