feat(coding-agent): add /resume command to switch sessions mid-conversation

- Opens interactive session selector
- Properly aborts in-flight agent turns before switching
- Restores model and thinking level from resumed session
- Clears UI state (queued messages, pending tools, etc.)

closes #117
This commit is contained in:
Mario Zechner 2025-12-05 22:53:51 +01:00
parent 30f63bcaf6
commit 4bd52cf3ed

View file

@ -1489,26 +1489,9 @@ export class TuiRenderer {
// Create session selector
this.sessionSelector = new SessionSelectorComponent(
this.sessionManager,
(sessionPath) => {
// Set the selected session as active
this.sessionManager.setSessionFile(sessionPath);
// Reload the session
const loaded = loadSessionFromEntries(this.sessionManager.loadEntries());
this.agent.replaceMessages(loaded.messages);
// Clear and re-render the chat
this.chatContainer.clear();
this.isFirstUserMessage = true;
this.renderInitialMessages(this.agent.state);
// Show confirmation message
this.chatContainer.addChild(new Spacer(1));
this.chatContainer.addChild(new Text(theme.fg("dim", "Resumed session"), 1, 0));
// Hide selector and show editor again
async (sessionPath) => {
this.hideSessionSelector();
this.ui.requestRender();
await this.handleResumeSession(sessionPath);
},
() => {
// Just hide the selector
@ -1524,6 +1507,65 @@ export class TuiRenderer {
this.ui.requestRender();
}
private async handleResumeSession(sessionPath: string): Promise<void> {
// Unsubscribe first to prevent processing events during transition
this.unsubscribe?.();
// Abort and wait for completion
this.agent.abort();
await this.agent.waitForIdle();
// Stop loading animation
if (this.loadingAnimation) {
this.loadingAnimation.stop();
this.loadingAnimation = null;
}
this.statusContainer.clear();
// Clear UI state
this.queuedMessages = [];
this.pendingMessagesContainer.clear();
this.streamingComponent = null;
this.pendingTools.clear();
// Set the selected session as active
this.sessionManager.setSessionFile(sessionPath);
// Reload the session
const loaded = loadSessionFromEntries(this.sessionManager.loadEntries());
this.agent.replaceMessages(loaded.messages);
// Restore model if saved in session
const savedModel = this.sessionManager.loadModel();
if (savedModel) {
const availableModels = (await getAvailableModels()).models;
const match = availableModels.find((m) => m.provider === savedModel.provider && m.id === savedModel.modelId);
if (match) {
this.agent.setModel(match);
}
}
// Restore thinking level if saved in session
const savedThinking = this.sessionManager.loadThinkingLevel();
if (savedThinking) {
this.agent.setThinkingLevel(savedThinking as ThinkingLevel);
}
// Resubscribe to agent
this.subscribeToAgent();
// Clear and re-render the chat
this.chatContainer.clear();
this.isFirstUserMessage = true;
this.renderInitialMessages(this.agent.state);
// Show confirmation message
this.chatContainer.addChild(new Spacer(1));
this.chatContainer.addChild(new Text(theme.fg("dim", "Resumed session"), 1, 0));
this.ui.requestRender();
}
private hideSessionSelector(): void {
// Replace selector with editor in the container
this.editorContainer.clear();