feat: LLM and User-controlled compaction via new_session tool#2333
feat: LLM and User-controlled compaction via new_session tool#2333taoeffect wants to merge 11 commits intocharmbracelet:mainfrom
new_session tool#2333Conversation
Introduce a new agent tool that allows the LLM to create a fresh session when working on long-running tasks, carrying forward a summary of progress and remaining work. This prevents context exhaustion during complex multi-step tasks. The implementation spans four layers: - Tool definition (`internal/agent/tools/new_session.go`): Defines the tool with a `summary` parameter and returns a sentinel `NewSessionError` that propagates up through the agent coordinator. - Coordinator integration (`internal/agent/coordinator.go`): Registers the tool in `buildTools()` alongside existing tools. - UI handling (`internal/ui/model/ui.go`, `internal/ui/chat/`): Catches `NewSessionError` in `sendMessage`, emits a `newSessionMsg` that creates a new session and auto-sends the summary as the first message. Adds a renderer for the tool's pending/completed states. - Config allowlist (`internal/config/config.go`): Adds `"new_session"` to `allToolNames()` so the tool is not filtered out before being sent to the LLM provider. Updates corresponding test expectations.
Introduce a compaction method toggle (auto vs LLM/user-driven) that controls how context window management works. In LLM mode, a <context_status> block is injected each turn so the model can track usage and invoke the new_session tool proactively. The setting is selectable via the command palette and persists across sessions in the data config.
- Use system role (not user) for context status message to avoid provider alternation violations and LLM misdirection - Validate empty/whitespace summaries in new_session tool, returning a tool error so the LLM can retry instead of propagating an agent error - Clamp used_pct to 100 and remaining_tokens to 0 consistently - Document one-step token lag in contextStatusMessage - Register new_session tool only when compaction method is LLM - Normalize CompactionMethod zero value "" to "auto" in setDefaults - Add trailing newline to new_session.md Assisted-by: Claude via Crush <crush@charm.land>
|
All contributors have signed the CLA ✍️ ✅ |
|
I have read the Contributor License Agreement (CLA) and hereby sign the CLA. |
There was a problem hiding this comment.
Pull request overview
This PR adds LLM/user-controlled context compaction by introducing a new_session tool, a new CompactionLLM mode, context-status injection into system prompts, and a UI dialog for switching between compaction modes. It closes issue #2331.
Changes:
- Added
new_sessionagent tool (returns a sentinel error to trigger UI-side session reset with a summary) - Added
CompactionLLMcompaction mode with context-status injection, andcompactionFlagshelper to derivedisableAutoSummarize/disableContextStatusfrom config - Added
Compactiondialog for selecting the compaction method, wired into the command palette
Reviewed changes
Copilot reviewed 20 out of 21 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
internal/agent/tools/new_session.go |
New sentinel tool that signals session reset via NewSessionError |
internal/agent/tools/new_session.md |
Tool description embedded in the tool for the LLM |
internal/agent/tools/new_session_test.go |
Tests for the new tool |
internal/agent/agent.go |
Migrated disableAutoSummarize to thread-safe csync.Value; added disableContextStatus field and SetCompactionFlags interface method; injects context-status system message per turn |
internal/agent/coordinator.go |
Added compactionFlags helper, named field initialization, new_session tool conditional registration, and SetCompactionFlags call in UpdateModels |
internal/agent/coordinator_test.go |
Updated mock to implement new SetCompactionFlags interface method |
internal/agent/compaction_flags_test.go |
Tests for compactionFlags helper function |
internal/agent/set_compaction_flags_test.go |
Tests for SetCompactionFlags and round-trip behavior |
internal/agent/context_status_test.go |
Tests for contextStatusMessage method |
internal/agent/common_test.go |
Updated testSessionAgent to use named struct fields |
internal/config/config.go |
Added CompactionMethod type, CompactionAuto/CompactionLLM constants, Options.CompactionMethod field, SetCompactionMethod method, new_session in allToolNames |
internal/config/load.go |
Added CompactionMethod default in setDefaults |
internal/config/load_test.go |
Updated tool list assertions and added compaction method default test |
internal/config/compaction_method_test.go |
Tests for SetCompactionMethod persistence and defaults |
internal/ui/dialog/compaction.go |
New dialog for selecting compaction method |
internal/ui/dialog/actions.go |
Added ActionSelectCompactionMethod action |
internal/ui/dialog/commands.go |
Added "Select Compaction Method" to the command palette |
internal/ui/model/ui.go |
Handles newSessionMsg, ActionSelectCompactionMethod, and opens compaction dialog |
internal/ui/chat/new_session.go |
New chat UI renderer for the new_session tool call |
internal/ui/chat/tools.go |
Wires NewSessionToolName to NewNewSessionToolMessageItem |
.gitignore |
Added project-local working files |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Handle NewSessionError specially in agent.Run() error dispatch to avoid persisting a spurious "Provider Error" finish entry. Update new_session.md to document the context_window field in the context_status payload.
new_session toolnew_session tool
…ously loaded skills
…g files across sessions The file history system hit a UNIQUE constraint violation on (path, session_id, version) when the same file was edited in multiple sessions — particularly after the new_session tool created a fresh session that re-edited files from a prior session. Two bugs were identified: 1. CreateVersion() used ListFilesByPath which queried ALL sessions for a file path, then picked max(version)+1 globally. But the UNIQUE constraint is per-session, so version numbers from session A leaked into session B, causing sparse versions and eventual collisions. 2. Create() always hardcoded version 0 (InitialVersion). The retry loop only attempted 3 increments (0, 1, 2), so if those versions already existed for (path, session_id), it exhausted retries and failed. The fix: - Add a ListFilesByPathAndSession SQL query scoped to a single session. - Change CreateVersion() to use the session-scoped query so version numbers are independent per session. - Make Create() delegate to CreateVersion() so it always picks the correct next version instead of hardcoding 0. - Add regression tests covering both bugs plus basic version semantics.
Wrap goose.SetBaseFS and goose.SetDialect in sync.Once to prevent concurrent writes to package-level globals when parallel tests each call db.Connect. Assisted-by: Opus 4.6 via Crush <crush@charm.land>
compactionFlags() was returning disableAutoSummarize=true for CompactionLLM mode, which completely disabled the StopWhen auto-compaction check. This meant that if the LLM failed to call new_session (expected at ~75% context usage), there was no fallback — context would silently overflow. Now auto-summarize stays enabled in LLM mode so it kicks in at 80% as a safety net. Assisted-by: Claude via Crush <crush@charm.land>
|
Updated behavior and edited the PR description to add:
|
|
One question I have for @andreynering: should it keep/remember the permissions granted from the current session when the new_session tool is called, or reset them as it currently does? |
This PR closes #2331 by adding:
new_sessiontool that allows customizable compactionnew_sessiontool is instructed to trigger at 75% context remaining. As a fallback, the auto-compaction remains enabled in case the LLM fails to trigger it.I think this PR also closes #2240 because
new_sessioncan handle custom compaction instructions.CONTRIBUTING.md.Co-authored by Opus 4.6 using Crush.