Skip to content
This repository was archived by the owner on Feb 14, 2026. It is now read-only.

Commit 2b5e6bd

Browse files
committed
codex: make abort graceful; force-close MCP on kill/exit
1 parent f9cb121 commit 2b5e6bd

2 files changed

Lines changed: 30 additions & 15 deletions

File tree

src/codex/codexMcpClient.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,21 @@ export class CodexMcpClient {
295295
return this.sessionId;
296296
}
297297

298+
/**
299+
* Force close the Codex MCP transport and clear all session identifiers.
300+
* Use this for permanent shutdown (e.g. kill/exit). Prefer `disconnect()` for
301+
* transient connection resets where you may want to keep the session id.
302+
*/
303+
async forceCloseSession(): Promise<void> {
304+
logger.debug('[CodexMCP] Force closing session');
305+
try {
306+
await this.disconnect();
307+
} finally {
308+
this.clearSession();
309+
}
310+
logger.debug('[CodexMCP] Session force-closed');
311+
}
312+
298313
async disconnect(): Promise<void> {
299314
if (!this.connected) return;
300315

@@ -327,9 +342,7 @@ export class CodexMcpClient {
327342

328343
this.transport = null;
329344
this.connected = false;
330-
this.sessionId = null;
331-
this.conversationId = null;
332-
333-
logger.debug('[CodexMCP] Disconnected');
345+
// Preserve session/conversation identifiers for potential reconnection / recovery flows.
346+
logger.debug(`[CodexMCP] Disconnected; session ${this.sessionId ?? 'none'} preserved`);
334347
}
335348
}

src/codex/runCodex.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,7 @@ export async function runCodex(opts: {
242242
}
243243

244244
abortController.abort();
245-
messageQueue.reset();
246-
permissionHandler.reset();
247245
reasoningProcessor.abort();
248-
diffProcessor.reset();
249246
logger.debug('[Codex] Abort completed - session remains active');
250247
} catch (error) {
251248
logger.debug('[Codex] Error during abort:', error);
@@ -282,6 +279,13 @@ export async function runCodex(opts: {
282279
await session.close();
283280
}
284281

282+
// Force close Codex transport (best-effort) so we don't leave stray processes
283+
try {
284+
await client.forceCloseSession();
285+
} catch (e) {
286+
logger.debug('[Codex] Error while force closing Codex session during termination', e);
287+
}
288+
285289
// Stop caffeinate
286290
stopCaffeinate();
287291

@@ -691,11 +695,9 @@ export async function runCodex(opts: {
691695
if (isAbortError) {
692696
messageBuffer.addMessage('Aborted by user', 'status');
693697
session.sendSessionEvent({ type: 'message', message: 'Aborted by user' });
694-
// Session was already stored in handleAbort(), no need to store again
695-
// Mark session as not created to force proper resume on next message
696-
wasCreated = false;
697-
currentModeHash = null;
698-
logger.debug('[Codex] Marked session as not created after abort for proper resume');
698+
// Abort cancels the current task/inference but keeps the Codex session alive.
699+
// Do not clear session state here; the next user message should continue on the
700+
// existing session if possible.
699701
} else {
700702
messageBuffer.addMessage('Process exited unexpectedly', 'status');
701703
session.sendSessionEvent({ type: 'message', message: 'Process exited unexpectedly' });
@@ -738,9 +740,9 @@ export async function runCodex(opts: {
738740
} catch (e) {
739741
logger.debug('[codex]: Error while closing session', e);
740742
}
741-
logger.debug('[codex]: client.disconnect begin');
742-
await client.disconnect();
743-
logger.debug('[codex]: client.disconnect done');
743+
logger.debug('[codex]: client.forceCloseSession begin');
744+
await client.forceCloseSession();
745+
logger.debug('[codex]: client.forceCloseSession done');
744746
// Stop Happy MCP server
745747
logger.debug('[codex]: happyServer.stop');
746748
happyServer.stop();

0 commit comments

Comments
 (0)