Feature Description
Three related improvements to the compaction system (internal/compaction/compaction.go, pkg/kit/compaction.go), based on research comparing Kit against opencode's compaction implementation:
1. Count all message parts in token estimation (and prefer real provider usage)
EstimateSingleMessageTokens currently only counts fantasy.TextPart. Tool-call JSON arguments, tool results, reasoning blocks, and images are invisible to the estimator. In agentic sessions, tool traffic often dominates token usage, so Kit systematically undercounts and triggers compaction too late — or hits hard context overflows.
- Estimate over the serialized full message including tool calls, tool results, and reasoning parts, plus system prompt and tool definition overhead.
- Prefer real usage numbers from the provider's last response (
Usage.TotalTokens) when available, falling back to the char/4 estimate. (opencode does exactly this: tokens.total || input + output + cache.read + cache.write.)
2. Adaptive reserve and keep-recent budgets
ReserveTokens (16384) and KeepRecentTokens (20000) are fixed defaults regardless of model. A fixed 20k keep budget is wasteful on small-context models and stingy on 1M-context ones. opencode computes:
reserved = min(20000, maxOutputTokens(model))
- recent-tail budget =
clamp(2000, usable_context * 0.25, 8000)
Kit should scale both with the model's context/output limits (available via models.ModelInfo.Limit), keeping the existing constants as explicit overrides via CompactionOptions.
3. Anchored/incremental summaries on re-compaction
When a session compacts multiple times, each compaction re-summarizes from scratch, losing detail across generations. opencode feeds the previous summary back into the summarization prompt: "Update the anchored summary below using the conversation history above. Preserve still-true details, remove stale details, and merge in the new facts."
Kit already threads PreviousCompaction through for file tracking — extend it to pass the prior summary text into the summarization prompt the same way, so summaries are incrementally updated rather than regenerated.
Motivation / Use Case
Long agentic sessions are where compaction matters most, and they are exactly where the current estimator is weakest (tool-heavy traffic is uncounted). Users on large-context models waste budget with fixed constants; users on small-context models get late compaction and overflows. Repeated compactions progressively lose task context without anchored summaries.
Proposed Implementation
internal/compaction/compaction.go: extend EstimateMessageTokens / EstimateSingleMessageTokens to serialize all part types; add helpers to compute adaptive reserve/keep budgets from ModelInfo; accept previous summary text in the summarization prompt builder.
pkg/kit/compaction.go: wire last-known Usage from the previous assistant response into ShouldCompact; pass previous compaction summary into compactInternal.
- All three changes are localized to these two files plus small plumbing, and can ship as one PR.
Checklist
Feature Description
Three related improvements to the compaction system (
internal/compaction/compaction.go,pkg/kit/compaction.go), based on research comparing Kit against opencode's compaction implementation:1. Count all message parts in token estimation (and prefer real provider usage)
EstimateSingleMessageTokenscurrently only countsfantasy.TextPart. Tool-call JSON arguments, tool results, reasoning blocks, and images are invisible to the estimator. In agentic sessions, tool traffic often dominates token usage, so Kit systematically undercounts and triggers compaction too late — or hits hard context overflows.Usage.TotalTokens) when available, falling back to the char/4 estimate. (opencode does exactly this:tokens.total || input + output + cache.read + cache.write.)2. Adaptive reserve and keep-recent budgets
ReserveTokens(16384) andKeepRecentTokens(20000) are fixed defaults regardless of model. A fixed 20k keep budget is wasteful on small-context models and stingy on 1M-context ones. opencode computes:reserved = min(20000, maxOutputTokens(model))clamp(2000, usable_context * 0.25, 8000)Kit should scale both with the model's context/output limits (available via
models.ModelInfo.Limit), keeping the existing constants as explicit overrides viaCompactionOptions.3. Anchored/incremental summaries on re-compaction
When a session compacts multiple times, each compaction re-summarizes from scratch, losing detail across generations. opencode feeds the previous summary back into the summarization prompt: "Update the anchored summary below using the conversation history above. Preserve still-true details, remove stale details, and merge in the new facts."
Kit already threads
PreviousCompactionthrough for file tracking — extend it to pass the prior summary text into the summarization prompt the same way, so summaries are incrementally updated rather than regenerated.Motivation / Use Case
Long agentic sessions are where compaction matters most, and they are exactly where the current estimator is weakest (tool-heavy traffic is uncounted). Users on large-context models waste budget with fixed constants; users on small-context models get late compaction and overflows. Repeated compactions progressively lose task context without anchored summaries.
Proposed Implementation
internal/compaction/compaction.go: extendEstimateMessageTokens/EstimateSingleMessageTokensto serialize all part types; add helpers to compute adaptive reserve/keep budgets fromModelInfo; accept previous summary text in the summarization prompt builder.pkg/kit/compaction.go: wire last-knownUsagefrom the previous assistant response intoShouldCompact; pass previous compaction summary intocompactInternal.Checklist