Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/services/aiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ export class AIService extends EventEmitter {
log.debug("Keeping reasoning parts for OpenAI (fetch wrapper handles item_references)");
}

// Add [INTERRUPTED] sentinel to partial messages (for model context)
// Add [CONTINUE] sentinel to partial messages (for model context)
const messagesWithSentinel = addInterruptedSentinel(filteredMessages);

// Convert CmuxMessage to ModelMessage format using Vercel AI SDK utility
Expand Down
2 changes: 1 addition & 1 deletion src/services/ipcMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1055,7 +1055,7 @@ export class IpcMain {
// so that stream-end can properly clean up the streaming indicator
this.aiService.replayStream(workspaceId);
} else if (partial) {
// No active stream but there's a partial - send as regular message (shows INTERRUPTED)
// No active stream but there's a partial - send as regular message (shows CONTINUE)
this.mainWindow?.webContents.send(chatChannel, partial);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/types/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface CmuxMetadata {
providerMetadata?: Record<string, unknown>; // Raw AI SDK provider data
systemMessageTokens?: number; // Token count for system message sent with this request (calculated by AIService)
partial?: boolean; // Whether this message was interrupted and is incomplete
synthetic?: boolean; // Whether this message was synthetically generated (e.g., [INTERRUPTED] sentinel)
synthetic?: boolean; // Whether this message was synthetically generated (e.g., [CONTINUE] sentinel)
error?: string; // Error message if stream failed
errorType?: StreamErrorType; // Error type/category if stream failed
compacted?: boolean; // Whether this message is a compacted summary of previous history
Expand Down
8 changes: 4 additions & 4 deletions src/utils/messages/modelMessageTransform.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,13 +420,13 @@ describe("modelMessageTransform", () => {

const result = addInterruptedSentinel(messages);

// Should have 3 messages: user, assistant, [INTERRUPTED] user
// Should have 3 messages: user, assistant, [CONTINUE] user
expect(result).toHaveLength(3);
expect(result[0].id).toBe("user-1");
expect(result[1].id).toBe("assistant-1");
expect(result[2].id).toBe("interrupted-assistant-1");
expect(result[2].role).toBe("user");
expect(result[2].parts).toEqual([{ type: "text", text: "[INTERRUPTED]" }]);
expect(result[2].parts).toEqual([{ type: "text", text: "[CONTINUE]" }]);
expect(result[2].metadata?.synthetic).toBe(true);
expect(result[2].metadata?.timestamp).toBe(2000);
});
Expand Down Expand Up @@ -472,10 +472,10 @@ describe("modelMessageTransform", () => {

const result = addInterruptedSentinel(messages);

// Should have 3 messages: user, assistant (reasoning only), [INTERRUPTED] user
// Should have 3 messages: user, assistant (reasoning only), [CONTINUE] user
expect(result).toHaveLength(3);
expect(result[2].role).toBe("user");
expect(result[2].parts).toEqual([{ type: "text", text: "[INTERRUPTED]" }]);
expect(result[2].parts).toEqual([{ type: "text", text: "[CONTINUE]" }]);
});

it("should handle multiple partial messages", () => {
Expand Down
8 changes: 4 additions & 4 deletions src/utils/messages/modelMessageTransform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ export function stripReasoningForOpenAI(messages: CmuxMessage[]): CmuxMessage[]
}

/**
* Add [INTERRUPTED] sentinel to partial messages by inserting a user message.
* This helps the model understand that a message was interrupted and incomplete.
* Add [CONTINUE] sentinel to partial messages by inserting a user message.
* This helps the model understand that a message was interrupted and to continue.
* The sentinel is ONLY for model context, not shown in UI.
*
* We insert a separate user message instead of modifying the assistant message
Expand All @@ -77,12 +77,12 @@ export function addInterruptedSentinel(messages: CmuxMessage[]): CmuxMessage[] {
for (const msg of messages) {
result.push(msg);

// If this is a partial assistant message, insert [INTERRUPTED] user message after it
// If this is a partial assistant message, insert [CONTINUE] user message after it
if (msg.role === "assistant" && msg.metadata?.partial) {
result.push({
id: `interrupted-${msg.id}`,
role: "user",
parts: [{ type: "text", text: "[INTERRUPTED]" }],
parts: [{ type: "text", text: "[CONTINUE]" }],
metadata: {
timestamp: msg.metadata.timestamp,
// Mark as synthetic so it can be identified if needed
Expand Down