mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-16 15:02:32 +00:00
Fix auto-compaction TUI integration and cut point logic
- Trigger auto-compaction after agent_end instead of during message_end - Show CompactionComponent after auto-compaction (same as manual /compact) - Fix cut point to include bash executions before kept user message - Stop backward scan at compaction, assistant, user, or toolResult boundaries
This commit is contained in:
parent
75c2eea151
commit
4227fd5996
3 changed files with 46 additions and 20 deletions
|
|
@ -135,6 +135,9 @@ export class AgentSession {
|
|||
}
|
||||
}
|
||||
|
||||
// Track last assistant message for auto-compaction check
|
||||
private _lastAssistantMessage: AssistantMessage | null = null;
|
||||
|
||||
/** Internal handler for agent events - shared by subscribe and reconnect */
|
||||
private _handleAgentEvent = async (event: AgentEvent): Promise<void> => {
|
||||
// Notify all listeners
|
||||
|
|
@ -149,11 +152,18 @@ export class AgentSession {
|
|||
this.sessionManager.startSession(this.agent.state);
|
||||
}
|
||||
|
||||
// Check auto-compaction after assistant messages
|
||||
// Track assistant message for auto-compaction (checked on agent_end)
|
||||
if (event.message.role === "assistant") {
|
||||
await this._runAutoCompaction();
|
||||
this._lastAssistantMessage = event.message as AssistantMessage;
|
||||
}
|
||||
}
|
||||
|
||||
// Check auto-compaction after agent completes (after agent_end clears UI)
|
||||
if (event.type === "agent_end" && this._lastAssistantMessage) {
|
||||
const msg = this._lastAssistantMessage;
|
||||
this._lastAssistantMessage = null;
|
||||
this._runAutoCompaction(msg).catch(() => {});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -584,26 +594,14 @@ export class AgentSession {
|
|||
* Internal: Run auto-compaction with events.
|
||||
* Called after assistant messages complete.
|
||||
*/
|
||||
private async _runAutoCompaction(): Promise<void> {
|
||||
private async _runAutoCompaction(assistantMessage: AssistantMessage): Promise<void> {
|
||||
const settings = this.settingsManager.getCompactionSettings();
|
||||
if (!settings.enabled) return;
|
||||
|
||||
// Get last non-aborted assistant message
|
||||
const messages = this.messages;
|
||||
let lastAssistant: AssistantMessage | null = null;
|
||||
for (let i = messages.length - 1; i >= 0; i--) {
|
||||
const msg = messages[i];
|
||||
if (msg.role === "assistant") {
|
||||
const assistantMsg = msg as AssistantMessage;
|
||||
if (assistantMsg.stopReason !== "aborted") {
|
||||
lastAssistant = assistantMsg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!lastAssistant) return;
|
||||
// Skip if message was aborted
|
||||
if (assistantMessage.stopReason === "aborted") return;
|
||||
|
||||
const contextTokens = calculateContextTokens(lastAssistant.usage);
|
||||
const contextTokens = calculateContextTokens(assistantMessage.usage);
|
||||
const contextWindow = this.model?.contextWindow ?? 0;
|
||||
|
||||
if (!shouldCompact(contextTokens, contextWindow, settings)) return;
|
||||
|
|
@ -624,6 +622,7 @@ export class AgentSession {
|
|||
return;
|
||||
}
|
||||
|
||||
// Load entries (sync file read) then yield to let UI render
|
||||
const entries = this.sessionManager.loadEntries();
|
||||
const compactionEntry = await compact(
|
||||
entries,
|
||||
|
|
|
|||
|
|
@ -94,7 +94,11 @@ function findTurnBoundaries(entries: SessionEntry[], startIndex: number, endInde
|
|||
|
||||
/**
|
||||
* Find the cut point in session entries that keeps approximately `keepRecentTokens`.
|
||||
* Returns the entry index of the first message to keep (a user message for turn integrity).
|
||||
* Returns the entry index of the first entry to keep.
|
||||
*
|
||||
* The cut point targets a user message (turn boundary), but then scans backwards
|
||||
* to include any preceding non-turn entries (bash executions, settings changes, etc.)
|
||||
* that should logically be part of the kept context.
|
||||
*
|
||||
* Only considers entries between `startIndex` and `endIndex` (exclusive).
|
||||
*/
|
||||
|
|
@ -150,6 +154,25 @@ export function findCutPoint(
|
|||
}
|
||||
}
|
||||
|
||||
// Scan backwards from cutIndex to include any non-turn entries (bash, settings, etc.)
|
||||
// that should logically be part of the kept context
|
||||
while (cutIndex > startIndex) {
|
||||
const prevEntry = entries[cutIndex - 1];
|
||||
// Stop at compaction boundaries
|
||||
if (prevEntry.type === "compaction") {
|
||||
break;
|
||||
}
|
||||
if (prevEntry.type === "message") {
|
||||
const role = prevEntry.message.role;
|
||||
// Stop if we hit an assistant, user, or tool result (all part of previous turn)
|
||||
if (role === "assistant" || role === "user" || role === "toolResult") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Include this non-turn entry (bash, settings change, etc.)
|
||||
cutIndex--;
|
||||
}
|
||||
|
||||
return cutIndex;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -597,7 +597,11 @@ export class InteractiveMode {
|
|||
// Rebuild chat to show compacted state
|
||||
this.chatContainer.clear();
|
||||
this.rebuildChatFromMessages();
|
||||
this.showStatus(`Auto-compacted: ${event.result.tokensBefore.toLocaleString()} tokens`);
|
||||
// Add compaction component (same as manual /compact)
|
||||
const compactionComponent = new CompactionComponent(event.result.tokensBefore, event.result.summary);
|
||||
compactionComponent.setExpanded(this.toolOutputExpanded);
|
||||
this.chatContainer.addChild(compactionComponent);
|
||||
this.footer.updateState(this.session.state);
|
||||
}
|
||||
this.ui.requestRender();
|
||||
break;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue