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 14 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? |
Gonna answer my own question here based on my experience working with this workflow: I think it's better to keep it as-is (with permissions being reset on each new session) as it is a bit safer that way. I don't think it's a good idea for the developer to completely disengage from monitoring what the agent is doing, as I've found myself frequently having to intervene at key points, and not necessarily just because the agent goes off the rails, but because I suddenly realize that I want to do something different. Having the permissions be reset on each new session forces me to pay attention and keeps things from going off the rails. |
Resolve merge conflicts between the new_session_tool feature branch and main's ConfigStore refactor, notification system, and UI changes. Conflict resolutions: - internal/config/config.go: Merged Options struct to include both CompactionMethod (HEAD) and DisableNotifications (main). Removed HEAD's Config mutation methods, keeping main's ConfigStore pattern. - internal/agent/coordinator.go: Resolved 3 conflict regions combining HEAD's compactionFlags() logic with main's c.cfg.Config().Options access pattern. Added Notify field from main. Updated all remaining c.cfg.Options/Providers references to c.cfg.Config().Options/Providers. - internal/agent/common_test.go: Kept HEAD's DisableContextStatus: true in SessionAgentOptions, required by the new_session_tool feature. Additional fixes required by the merge: - internal/config/store.go: Added SetCompactionMethod(scope, method) to ConfigStore, following the SetCompactMode pattern. - internal/config/compaction_method_test.go: Rewrote tests to use ConfigStore/testStoreWithPath pattern instead of direct Config access. - internal/ui/model/ui.go: Changed SetCompactionMethod call from cfg.SetCompactionMethod(method) to m.com.Store().SetCompactionMethod(ScopeGlobal, method). - internal/ui/chat/new_session.go: Added missing 'nested' bool argument to pendingTool() call (main added this parameter).
|
Note: one test is failing after merging |
…tool Merge the agent model selection feature (PR charmbracelet#2365) with the LLM-driven compaction feature (PR charmbracelet#2333). The two branches independently modified `buildAgent()` in coordinator.go: - issue-2358 introduced per-agent model selection via a `primary` variable that picks the large or small model based on each agent's config, allowing the `agent` tool to default to the small (cheaper/faster) model. - new_session_tool added compaction flag derivation via `compactionFlags()`, which reads the configured `CompactionMethod` to control auto-summarize and context-status behavior for LLM-driven compaction. The resolution combines both: the primary model is selected per agent config, its provider config is used for the system prompt prefix, and the compaction flags are derived from the compaction method — preserving the full intent of both branches. Also retains `.tldr/` and `.tldrignore` gitignore entries from new_session_tool that were absent in issue-2358.
Merge changes from taoeffect2 which includes: - feat: LLM and user-controlled compaction via `new_session` tool (charmbracelet#2333) Adds a `new_session` tool for customizable compaction, a compaction method switching menu in the UI, and context status injection into the system prompt when using LLM/user-driven compaction. - fix(agent): change agent tool model to use the small model (charmbracelet#2365) Switches the `agent` tool from the large task model to the small task model for cost savings and improved speed. Additionally merges Docker MCP support (enable/disable toggle in the commands menu, config persistence, and MCP client lifecycle management), session continuation on startup via --continue/--session flags, and RefreshTools on the Coordinator interface. Conflicts resolved (preferring taoeffect2): - internal/agent/coordinator.go: removed extra blank line before NewSessionAgent (cosmetic only) - internal/ui/model/ui.go: removed redundant cfg nil-check in compaction dialog handler, now uses m.com.Store() directly
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.NOTE: Written by Opus 4.6 using Crush.