Skip to content
Open
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
4 changes: 2 additions & 2 deletions src/claude/runClaude.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ export async function runClaude(credentials: Credentials, options: StartOptions
disallowedTools: messageDisallowedTools
};
messageQueue.pushIsolateAndClear(specialCommand.originalMessage || message.content.text, enhancedMode);
logger.debugLargeJson('[start] /compact command pushed to queue:', message);
logger.debugLargeJson('[start] /clear command pushed to queue:', message);
return;
}

Expand Down Expand Up @@ -303,7 +303,7 @@ export async function runClaude(credentials: Credentials, options: StartOptions
archivedBy: 'cli',
archiveReason: 'User terminated'
}));

// Send session death message
session.sendSessionDeath();
await session.flush();
Expand Down
47 changes: 39 additions & 8 deletions src/codex/runCodex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { notifyDaemonSessionStarted } from "@/daemon/controlClient";
import { registerKillSessionHandler } from "@/claude/registerKillSessionHandler";
import { delay } from "@/utils/time";
import { stopCaffeinate } from "@/utils/caffeinate";
import { parseSpecialCommand } from '@/parsers/specialCommands';

type ReadyEventOptions = {
pending: unknown;
Expand Down Expand Up @@ -175,7 +176,13 @@ export async function runCodex(opts: {
permissionMode: messagePermissionMode || 'default',
model: messageModel,
};
messageQueue.push(message.content.text, enhancedMode);

const specialCommand = parseSpecialCommand(message.content.text);
if (specialCommand.type === 'clear') {
messageQueue.pushIsolateAndClear(message.content.text, enhancedMode);
} else {
messageQueue.push(message.content.text, enhancedMode);
}
});
let thinking = false;
session.keepAlive(thinking, 'remote');
Expand Down Expand Up @@ -239,7 +246,7 @@ export async function runCodex(opts: {
storedSessionIdForResume = client.storeSessionForResume();
logger.debug('[Codex] Stored session for resume:', storedSessionIdForResume);
}

abortController.abort();
messageQueue.reset();
permissionHandler.reset();
Expand Down Expand Up @@ -274,7 +281,7 @@ export async function runCodex(opts: {
archivedBy: 'cli',
archiveReason: 'User terminated'
}));

// Send session death message
session.sendSessionDeath();
await session.flush();
Expand Down Expand Up @@ -615,6 +622,30 @@ export async function runCodex(opts: {
messageBuffer.addMessage(message.message, 'user');
currentModeHash = message.hash;

const specialCommand = parseSpecialCommand(message.message);
if (specialCommand.type === 'clear') {
logger.debug('[Codex] Handling /clear command - resetting session');
client.clearSession();
wasCreated = false;
currentModeHash = null;

// Reset processors/permissions
permissionHandler.reset();
reasoningProcessor.abort();
diffProcessor.reset();
thinking = false;
session.keepAlive(thinking, 'remote');

messageBuffer.addMessage('Session reset.', 'status');
emitReadyIfIdle({
pending,
queueSize: () => messageQueue.size(),
shouldExit,
sendReady,
});
continue;
}

try {
// Map permission mode to approval policy and sandbox for startSession
const approvalPolicy = (() => {
Expand Down Expand Up @@ -644,10 +675,10 @@ export async function runCodex(opts: {
if (message.mode.model) {
startConfig.model = message.mode.model;
}

// Check for resume file from multiple sources
let resumeFile: string | null = null;

// Priority 1: Explicit resume file from mode change
if (nextExperimentalResume) {
resumeFile = nextExperimentalResume;
Expand All @@ -664,12 +695,12 @@ export async function runCodex(opts: {
}
storedSessionIdForResume = null; // consume once
}

// Apply resume file if found
if (resumeFile) {
(startConfig.config as any).experimental_resume = resumeFile;
}

await client.startSession(
startConfig,
{ signal: abortController.signal }
Expand All @@ -686,7 +717,7 @@ export async function runCodex(opts: {
} catch (error) {
logger.warn('Error in codex session:', error);
const isAbortError = error instanceof Error && error.name === 'AbortError';

if (isAbortError) {
messageBuffer.addMessage('Aborted by user', 'status');
session.sendSessionEvent({ type: 'message', message: 'Aborted by user' });
Expand Down
12 changes: 6 additions & 6 deletions src/parsers/specialCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,21 @@ export interface SpecialCommandResult {
*/
export function parseCompact(message: string): CompactCommandResult {
const trimmed = message.trim();

if (trimmed === '/compact') {
return {
isCompact: true,
originalMessage: trimmed
};
}

if (trimmed.startsWith('/compact ')) {
return {
isCompact: true,
originalMessage: trimmed
};
}

return {
isCompact: false,
originalMessage: message
Expand All @@ -49,7 +49,7 @@ export function parseCompact(message: string): CompactCommandResult {
*/
export function parseClear(message: string): ClearCommandResult {
const trimmed = message.trim();

return {
isClear: trimmed === '/clear'
};
Expand All @@ -67,14 +67,14 @@ export function parseSpecialCommand(message: string): SpecialCommandResult {
originalMessage: compactResult.originalMessage
};
}

const clearResult = parseClear(message);
if (clearResult.isClear) {
return {
type: 'clear'
};
}

return {
type: null
};
Expand Down