Skip to content

Commit a492d77

Browse files
committed
refactor: use message queue for compact continue messages
Previously, compact continue messages were handled by a frontend hook (useAutoCompactContinue) that watched for completed compactions and then sent the continue message. This introduced complexity, race conditions, and required tracking processed message IDs. Now leverages the existing message queue system: - Backend queues continue message when compaction starts - Queue auto-sends when compaction stream ends (existing behavior) - Continue message shown in queue UI during compaction - Proper cleanup on all error paths Net reduction of 124 lines. Simpler, more reliable, better UX.
1 parent 61dd396 commit a492d77

File tree

6 files changed

+14
-148
lines changed

6 files changed

+14
-148
lines changed

src/browser/App.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { usePersistedState, updatePersistedState } from "./hooks/usePersistedSta
1212
import { matchesKeybind, KEYBINDS } from "./utils/ui/keybinds";
1313
import { useResumeManager } from "./hooks/useResumeManager";
1414
import { useUnreadTracking } from "./hooks/useUnreadTracking";
15-
import { useAutoCompactContinue } from "./hooks/useAutoCompactContinue";
1615
import { useWorkspaceStoreRaw, useWorkspaceRecency } from "./stores/WorkspaceStore";
1716
import { ChatInput } from "./components/ChatInput/index";
1817
import type { ChatInputAPI } from "./components/ChatInput/types";
@@ -116,9 +115,6 @@ function AppInner() {
116115
// Auto-resume interrupted streams on app startup and when failures occur
117116
useResumeManager();
118117

119-
// Handle auto-continue after compaction (when user uses /compact -c)
120-
useAutoCompactContinue();
121-
122118
// Sync selectedWorkspace with URL hash
123119
useEffect(() => {
124120
if (selectedWorkspace) {

src/browser/hooks/useAutoCompactContinue.ts

Lines changed: 0 additions & 115 deletions
This file was deleted.

src/browser/stores/WorkspaceStore.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -672,12 +672,6 @@ export class WorkspaceStore {
672672
const historicalUsage =
673673
currentUsage.usageHistory.length > 0 ? sumUsageHistory(currentUsage.usageHistory) : undefined;
674674

675-
// Extract continueMessage from compaction-request before history gets replaced
676-
const compactRequestMsg = findCompactionRequestMessage(aggregator);
677-
const muxMeta = compactRequestMsg?.metadata?.muxMetadata;
678-
const continueMessage =
679-
muxMeta?.type === "compaction-request" ? muxMeta.parsed.continueMessage : undefined;
680-
681675
const summaryMessage = createMuxMessage(
682676
`summary-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
683677
"assistant",
@@ -697,10 +691,7 @@ export class WorkspaceStore {
697691
metadata && "systemMessageTokens" in metadata
698692
? (metadata.systemMessageTokens as number | undefined)
699693
: undefined,
700-
// Store continueMessage in summary so it survives history replacement
701-
muxMetadata: continueMessage
702-
? { type: "compaction-result", continueMessage, requestId: compactRequestMsg?.id }
703-
: { type: "normal" },
694+
muxMetadata: { type: "normal" },
704695
}
705696
);
706697

src/common/constants/storage.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -126,15 +126,6 @@ export const PREFERRED_COMPACTION_MODEL_KEY = "preferredCompactionModel";
126126
*/
127127
export const VIM_ENABLED_KEY = "vimEnabled";
128128

129-
/**
130-
* Get the localStorage key for the compact continue message for a workspace
131-
* Temporarily stores the continuation prompt for the current compaction
132-
* Should be deleted immediately after use to prevent bugs
133-
*/
134-
export function getCompactContinueMessageKey(workspaceId: string): string {
135-
return `compactContinueMessage:${workspaceId}`;
136-
}
137-
138129
/**
139130
* Get the localStorage key for hunk expand/collapse state in Review tab
140131
* Stores user's manual expand/collapse preferences per hunk
@@ -164,7 +155,6 @@ export function getReviewSearchStateKey(workspaceId: string): string {
164155

165156
/**
166157
* List of workspace-scoped key functions that should be copied on fork and deleted on removal
167-
* Note: Excludes ephemeral keys like getCompactContinueMessageKey
168158
*/
169159
const PERSISTENT_WORKSPACE_KEY_FUNCTIONS: Array<(workspaceId: string) => string> = [
170160
getModelKey,
@@ -183,7 +173,6 @@ const PERSISTENT_WORKSPACE_KEY_FUNCTIONS: Array<(workspaceId: string) => string>
183173
*/
184174
const EPHEMERAL_WORKSPACE_KEY_FUNCTIONS: Array<(workspaceId: string) => string> = [
185175
getCancelledCompactionKey,
186-
getCompactContinueMessageKey,
187176
];
188177

189178
/**

src/common/types/message.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,6 @@ export type MuxFrontendMetadata =
2020
rawCommand: string; // The original /compact command as typed by user (for display)
2121
parsed: CompactionRequestData;
2222
}
23-
| {
24-
type: "compaction-result";
25-
continueMessage: string; // Message to send after compaction completes
26-
requestId?: string; // ID of the compaction-request user message that produced this summary (for idempotency)
27-
}
2823
| {
2924
type: "normal"; // Regular messages
3025
};

src/node/services/agentSession.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,15 @@ export class AgentSession {
314314

315315
this.emitChatEvent(userMessage);
316316

317+
// If this is a compaction request with a continue message, queue it for auto-send after compaction
318+
const muxMeta = options?.muxMetadata;
319+
if (muxMeta?.type === "compaction-request" && muxMeta.parsed.continueMessage && options) {
320+
// Strip out muxMetadata & mode so the queued message doesn't compact
321+
const { muxMetadata, mode, ...continueOptions } = options;
322+
this.messageQueue.add(muxMeta.parsed.continueMessage, continueOptions);
323+
this.emitQueuedMessageChanged();
324+
}
325+
317326
if (!options?.model || options.model.trim().length === 0) {
318327
return Err(
319328
createUnknownSendMessageError("No model specified. Please select a model using /model.")
@@ -412,16 +421,17 @@ export class AgentSession {
412421
forward("reasoning-delta", (payload) => this.emitChatEvent(payload));
413422
forward("reasoning-end", (payload) => this.emitChatEvent(payload));
414423

415-
forward("stream-end", (payload) => {
416-
this.emitChatEvent(payload);
424+
forward("stream-end", async (payload) => {
417425
// Stream end: auto-send queued messages
418426
if (!this.messageQueue.isEmpty()) {
419427
const { message, options } = this.messageQueue.produceMessage();
420428
this.messageQueue.clear();
421429
this.emitQueuedMessageChanged();
422430

423-
void this.sendMessage(message, options);
431+
await this.sendMessage(message, options);
424432
}
433+
// Don't update the frontend until the message is sent
434+
this.emitChatEvent(payload);
425435
});
426436

427437
forward("stream-abort", (payload) => {

0 commit comments

Comments
 (0)