diff --git a/packages/coding-agent/src/modes/components/tool-execution.ts b/packages/coding-agent/src/modes/components/tool-execution.ts index e7ebc6c7c..2aca3309f 100644 --- a/packages/coding-agent/src/modes/components/tool-execution.ts +++ b/packages/coding-agent/src/modes/components/tool-execution.ts @@ -551,14 +551,13 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac */ #updateSpinnerAnimation(): void { // Live partial tool blocks stay repaintable until a terminal result seals - // them. Todo snapshots and detached background task progress are deliberate + // them. Todo snapshots and detached background tool progress are deliberate // static exceptions because their rows can be superseded or committed to // scrollback while later updates continue elsewhere. const isStreamingArgs = !this.#argsComplete && (isEditLikeToolName(this.#toolName) || this.#toolName === "write"); const isBackgroundAsyncRunning = (this.#result?.details as { async?: { state?: string } } | undefined)?.async?.state === "running"; - const isBackgroundAsyncTask = this.#toolName === "task" && isBackgroundAsyncRunning; - const isLivePartialTool = this.#isPartial && this.#toolName !== "todo" && !isBackgroundAsyncTask; + const isLivePartialTool = this.#isPartial && this.#toolName !== "todo" && !isBackgroundAsyncRunning; const needsSpinner = isStreamingArgs || isLivePartialTool || this.#displaceableByToolName === "job"; if (needsSpinner && !this.#spinnerInterval) { const frameCount = theme.spinnerFrames.length; diff --git a/packages/coding-agent/test/modes/components/tool-execution-spinner.test.ts b/packages/coding-agent/test/modes/components/tool-execution-spinner.test.ts index d667214ce..4fe760d61 100644 --- a/packages/coding-agent/test/modes/components/tool-execution-spinner.test.ts +++ b/packages/coding-agent/test/modes/components/tool-execution-spinner.test.ts @@ -69,4 +69,35 @@ describe("ToolExecutionComponent live preview spinners", () => { component.stopAnimation(); } }); + + it("does not tick detached async bash result snapshots", () => { + vi.useFakeTimers(); + const requestRender = vi.fn(); + const component = new ToolExecutionComponent( + "bash", + { command: "sleep 600", async: true }, + {}, + undefined, + { requestRender } as unknown as TUI, + process.cwd(), + ); + + try { + component.updateResult( + { + content: [{ type: "text", text: "started background job" }], + details: { + command: "sleep 600", + async: { state: "running", jobId: "job-1", type: "bash" }, + }, + }, + true, + ); + requestRender.mockClear(); + vi.advanceTimersByTime(500); + expect(requestRender).not.toHaveBeenCalled(); + } finally { + component.stopAnimation(); + } + }); });