diff --git a/packages/coding-agent/src/core/agent-session.ts b/packages/coding-agent/src/core/agent-session.ts index c2e55f55..db27d8e0 100644 --- a/packages/coding-agent/src/core/agent-session.ts +++ b/packages/coding-agent/src/core/agent-session.ts @@ -142,11 +142,6 @@ export class AgentSession { await this.checkAutoCompaction(); } } - - // Flush pending bash messages after agent turn completes - if (event.type === "agent_end") { - this._flushPendingBashMessages(); - } }; /** @@ -266,6 +261,9 @@ export class AgentSession { * @throws Error if no model selected or no API key available */ async prompt(text: string, options?: PromptOptions): Promise { + // Flush any pending bash messages before the new prompt + this._flushPendingBashMessages(); + const expandCommands = options?.expandSlashCommands ?? true; // Validate model @@ -699,6 +697,11 @@ export class AgentSession { return this._bashAbortController !== null; } + /** Whether there are pending bash messages waiting to be flushed */ + get hasPendingBashMessages(): boolean { + return this._pendingBashMessages.length > 0; + } + /** * Flush pending bash messages to agent state and session. * Called after agent turn completes to maintain proper message ordering. diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index 7f317041..b1aa8f7d 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -91,6 +91,9 @@ export class InteractiveMode { // Track current bash execution component private bashComponent: BashExecutionComponent | null = null; + // Track pending bash components (shown in pending area, moved to chat on submit) + private pendingBashComponents: BashExecutionComponent[] = []; + // Convenience accessors private get agent() { return this.session.agent; @@ -411,6 +414,9 @@ export class InteractiveMode { } // Normal message submission + // First, move any pending bash components to chat + this.flushPendingBashComponents(); + if (this.onInputCallback) { this.onInputCallback(text); } @@ -826,6 +832,15 @@ export class InteractiveMode { } } + /** Move pending bash components from pending area to chat */ + private flushPendingBashComponents(): void { + for (const component of this.pendingBashComponents) { + this.pendingMessagesContainer.removeChild(component); + this.chatContainer.addChild(component); + } + this.pendingBashComponents = []; + } + // ========================================================================= // Selectors // ========================================================================= @@ -1242,8 +1257,17 @@ export class InteractiveMode { } private async handleBashCommand(command: string): Promise { + const isDeferred = this.session.isStreaming; this.bashComponent = new BashExecutionComponent(command, this.ui); - this.chatContainer.addChild(this.bashComponent); + + if (isDeferred) { + // Show in pending area when agent is streaming + this.pendingMessagesContainer.addChild(this.bashComponent); + this.pendingBashComponents.push(this.bashComponent); + } else { + // Show in chat immediately when agent is idle + this.chatContainer.addChild(this.bashComponent); + } this.ui.requestRender(); try {