Skip to content

Commit a31a2e0

Browse files
🤖 refactor: use message queue for compact continue messages (#650)
## Stack 1. #685 1. #683 1. #670 1. #650 ⬅ This PR (base) ## Problem Compact continue messages were handled by a frontend hook that watched workspace states and manually sent continue messages after compaction. This was complex, had potential race conditions, and poor separation of concerns. Relates to #651. ## Solution Use the existing message queue system: - Backend queues continue message when compaction starts - Queue auto-sends when stream ends (existing behavior) - Clear queue on error paths **Benefits:** Simpler (-134 lines), more reliable, better UX (continue message visible in queue). _Generated with `mux`_
1 parent 2c700b8 commit a31a2e0

File tree

10 files changed

+14
-149
lines changed

10 files changed

+14
-149
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";
@@ -117,9 +116,6 @@ function AppInner() {
117116
// Auto-resume interrupted streams on app startup and when failures occur
118117
useResumeManager();
119118

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

‎src/browser/api.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ const webApi: IPCApi = {
263263
invokeIPC(IPC_CHANNELS.WORKSPACE_RESUME_STREAM, workspaceId, options),
264264
interruptStream: (workspaceId, options) =>
265265
invokeIPC(IPC_CHANNELS.WORKSPACE_INTERRUPT_STREAM, workspaceId, options),
266-
clearQueue: (workspaceId) => invokeIPC(IPC_CHANNELS.WORKSPACE_QUEUE_CLEAR, workspaceId),
266+
clearQueue: (workspaceId) => invokeIPC(IPC_CHANNELS.WORKSPACE_CLEAR_QUEUE, workspaceId),
267267
truncateHistory: (workspaceId, percentage) =>
268268
invokeIPC(IPC_CHANNELS.WORKSPACE_TRUNCATE_HISTORY, workspaceId, percentage),
269269
replaceChatHistory: (workspaceId, summaryMessage) =>

‎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/ipc-constants.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const IPC_CHANNELS = {
2727
WORKSPACE_SEND_MESSAGE: "workspace:sendMessage",
2828
WORKSPACE_RESUME_STREAM: "workspace:resumeStream",
2929
WORKSPACE_INTERRUPT_STREAM: "workspace:interruptStream",
30-
WORKSPACE_QUEUE_CLEAR: "workspace:queue:clear",
30+
WORKSPACE_CLEAR_QUEUE: "workspace:clearQueue",
3131
WORKSPACE_TRUNCATE_HISTORY: "workspace:truncateHistory",
3232
WORKSPACE_REPLACE_HISTORY: "workspace:replaceHistory",
3333
WORKSPACE_STREAM_HISTORY: "workspace:streamHistory",

‎src/common/constants/storage.ts‎

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -120,15 +120,6 @@ export const PREFERRED_COMPACTION_MODEL_KEY = "preferredCompactionModel";
120120
*/
121121
export const VIM_ENABLED_KEY = "vimEnabled";
122122

123-
/**
124-
* Get the localStorage key for the compact continue message for a workspace
125-
* Temporarily stores the continuation prompt for the current compaction
126-
* Should be deleted immediately after use to prevent bugs
127-
*/
128-
export function getCompactContinueMessageKey(workspaceId: string): string {
129-
return `compactContinueMessage:${workspaceId}`;
130-
}
131-
132123
/**
133124
* Get the localStorage key for hunk expand/collapse state in Review tab
134125
* Stores user's manual expand/collapse preferences per hunk
@@ -158,7 +149,6 @@ export function getReviewSearchStateKey(workspaceId: string): string {
158149

159150
/**
160151
* List of workspace-scoped key functions that should be copied on fork and deleted on removal
161-
* Note: Excludes ephemeral keys like getCompactContinueMessageKey
162152
*/
163153
const PERSISTENT_WORKSPACE_KEY_FUNCTIONS: Array<(workspaceId: string) => string> = [
164154
getModelKey,
@@ -177,7 +167,6 @@ const PERSISTENT_WORKSPACE_KEY_FUNCTIONS: Array<(workspaceId: string) => string>
177167
*/
178168
const EPHEMERAL_WORKSPACE_KEY_FUNCTIONS: Array<(workspaceId: string) => string> = [
179169
getCancelledCompactionKey,
180-
getCompactContinueMessageKey,
181170
];
182171

183172
/**

‎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/desktop/preload.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ const api: IPCApi = {
8282
interruptStream: (workspaceId: string, options?: { abandonPartial?: boolean }) =>
8383
ipcRenderer.invoke(IPC_CHANNELS.WORKSPACE_INTERRUPT_STREAM, workspaceId, options),
8484
clearQueue: (workspaceId: string) =>
85-
ipcRenderer.invoke(IPC_CHANNELS.WORKSPACE_QUEUE_CLEAR, workspaceId),
85+
ipcRenderer.invoke(IPC_CHANNELS.WORKSPACE_CLEAR_QUEUE, workspaceId),
8686
truncateHistory: (workspaceId, percentage) =>
8787
ipcRenderer.invoke(IPC_CHANNELS.WORKSPACE_TRUNCATE_HISTORY, workspaceId, percentage),
8888
replaceChatHistory: (workspaceId, summaryMessage) =>

‎src/node/services/agentSession.ts‎

Lines changed: 9 additions & 0 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 edit-specific and compaction-specific fields so the queued message is a fresh user message
321+
const { muxMetadata, mode, editMessageId, ...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.")

‎src/node/services/ipcMain.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1119,7 +1119,7 @@ export class IpcMain {
11191119
}
11201120
);
11211121

1122-
ipcMain.handle(IPC_CHANNELS.WORKSPACE_QUEUE_CLEAR, (_event, workspaceId: string) => {
1122+
ipcMain.handle(IPC_CHANNELS.WORKSPACE_CLEAR_QUEUE, (_event, workspaceId: string) => {
11231123
try {
11241124
const session = this.getOrCreateSession(workspaceId);
11251125
session.clearQueue();

0 commit comments

Comments
 (0)