diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index b283cb5d0..c3b569142 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -29,6 +29,7 @@ import type { CustomToolSessionEvent, LoadedCustomTool } from "../../core/custom import type { HookUIContext } from "../../core/hooks/index"; import { createCompactionSummaryMessage } from "../../core/messages"; import { getRecentSessions, type SessionContext, SessionManager } from "../../core/session-manager"; +import { registerAsyncCleanup } from "../cleanup"; import { generateSessionTitle, setTerminalTitle } from "../../core/title-generator"; import type { TruncationResult } from "../../core/tools/truncate"; import { disableProvider, enableProvider } from "../../discovery"; @@ -117,6 +118,9 @@ export class InteractiveMode { // Agent subscription unsubscribe function private unsubscribe?: () => void; + // Signal cleanup unsubscribe function (for SIGINT/SIGTERM flush) + private cleanupUnsubscribe?: () => void; + // Track if editor is in bash mode (text starts with !) private isBashMode = false; @@ -236,6 +240,9 @@ export class InteractiveMode { async init(): Promise { if (this.isInitialized) return; + // Register session manager flush for signal handlers (SIGINT, SIGTERM, SIGHUP) + this.cleanupUnsubscribe = registerAsyncCleanup(() => this.sessionManager.flush()); + // Get current model info for welcome screen const modelName = this.session.model?.name ?? "Unknown"; const providerName = this.session.model?.provider ?? "Unknown"; @@ -918,9 +925,9 @@ export class InteractiveMode { const registry = this.session.modelRegistry; const smolModel = this.settingsManager.getModelRole("smol"); generateSessionTitle(text, registry, smolModel) - .then((title) => { + .then(async (title) => { if (title) { - this.sessionManager.setSessionTitle(title); + await this.sessionManager.setSessionTitle(title); setTerminalTitle(`omp: ${title}`); } }) @@ -1129,7 +1136,7 @@ export class InteractiveMode { this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), - `${reasonText}Auto-compacting... (esc to cancel)`, + `${reasonText}Auto-compacting${theme.format.ellipsis} (esc to cancel)`, getSymbolTheme().spinnerFrames, ); this.statusContainer.addChild(this.autoCompactionLoader); @@ -1185,7 +1192,7 @@ export class InteractiveMode { this.ui, (spinner) => theme.fg("warning", spinner), (text) => theme.fg("muted", text), - `Retrying (${event.attempt}/${event.maxAttempts}) in ${delaySeconds}s... (esc to cancel)`, + `Retrying (${event.attempt}/${event.maxAttempts}) in ${delaySeconds}s${theme.format.ellipsis} (esc to cancel)`, getSymbolTheme().spinnerFrames, ); this.statusContainer.addChild(this.retryLoader); @@ -2651,6 +2658,9 @@ export class InteractiveMode { if (this.unsubscribe) { this.unsubscribe(); } + if (this.cleanupUnsubscribe) { + this.cleanupUnsubscribe(); + } if (this.isInitialized) { this.ui.stop(); this.isInitialized = false;