feat(loop): graduated context trimming, margin calibration, rolling compaction#102
Merged
Merged
Conversation
…ompaction Reworks context-window management in the agent loop: Correctness fixes: - estimateToolDefs now counts JSON parameter schemas (the bulk of every tool definition) instead of ignoring them - estimateMessages now counts ReasoningContent (echoed back to reasoning models, often the largest content in a turn) - trimToSurvival keeps the original task in multi-turn sessions, preserves the compaction digest, and no longer appends a zero-value message when no user message exists Trimming robustness: - budget re-checked after memory/skill/episode injections, right before the LLM call — injected blocks no longer escape the budget for a full iteration - graduated trimming: old tool results >2 KiB (except the 4 most recent) are replaced with a marker before any turn group is dropped - trim warning moved before the most recent user message (cache-stable head preserved), updated in place with cumulative totals, and names up to 5 dropped tools; new headLen protects system prompt + memory block + digest + original task - disk-cap session trimming persists a [Session storage limit: ...] marker into the transcript so resumed sessions know history was removed New features: - self-calibrating safety margin: tightens 0.75 -> 0.65 when provider- reported input tokens exceed the local estimate by >15% (margin_calibrated signal) - optional rolling compaction (compaction / ODEK_COMPACTION / --compaction, default off): dropped turn groups are summarized by the model into a rolling, untrusted-wrapped digest message instead of being lost; summarizer failures never break trimming All new/changed loop, session, and config code is at 100% function coverage. Full unit suite, race detector, golangci-lint, and CLI E2E tests pass.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
odek | 9655091 | Commit Preview URL Branch Preview URL |
Jul 25 2026, 09:40 PM |
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
Reworks context-window management in the agent loop (
internal/loop) and session store (internal/session), plus config/CLI plumbing for a new opt-in compaction feature.Correctness fixes
estimateToolDefsnow counts JSON parameter schemas — the bulk of every tool definition across ~26 built-ins + MCP tools was previously invisible to the budget.estimateMessagesnow countsReasoningContent, which is echoed back to reasoning models and is often the largest content in a turn.trimToSurvivalkept only the last user message despite its doc comment; it now also keeps the original task in multi-turn sessions, preserves the compaction digest, and no longer appends a zero-value message when no user message exists.Trimming robustness
client.Call.[tool output trimmed: N bytes...]marker before any turn group is dropped — preserving the assistant's reasoning and the fact the tool ran.memMsgIdxresync hack), is updated in place with cumulative totals, and names up to 5 dropped tools. NewheadLenprotects system prompt + memory block + digest + original task.[Session storage limit: ...]marker into the transcript so a resumed session knows earlier history was removed (previously only a stderr warning).New features
margin_calibratedsignal).compactionconfig /ODEK_COMPACTION/--compaction, default off): turn groups dropped by trimming are summarized by the model into a rolling[Compacted earlier context: ...]digest message — untrusted-wrapped, updated in place, protected from future trims. Summarizer failure never breaks trimming. Plumbed throughodek.Config,internal/config, and all CLI entry points (run/repl/serve/schedule), mirroringprompt_caching; settable from project-levelodek.jsonunder the same rules asprompt_caching.Tests
internal/loop/loop_trim_test.go; updated trim/session tests; new config tests (TestGlobalOverlay_Compaction,TestEnvVar_Compaction,TestCLIFlags_Compaction).internal/loop,internal/session,internal/configare at 100% function coverage (remaining uncovered blocks in those files are pre-existing unreachable-by-design error returns documented in the test files).go test ./... -count=1✅ ·go test -race ./internal/loop/ ./internal/session/ ./internal/config/✅ ·golangci-lint run ./...— 0 issues ✅ ·ODEK_E2E=true go test ./cmd/odek/ -run TestE2E_✅Docs updated:
AGENTS.md,docs/CONFIG.md,docs/CLI.md.