From 5026e3483597a9ed8d567474f1dcd0512142b732 Mon Sep 17 00:00:00 2001 From: NarwhalChen Date: Thu, 9 Jul 2026 11:39:32 +0800 Subject: [PATCH] fix: reduce grouped tool refreshes --- .changeset/reduce-group-refreshes.md | 5 +++ .../tui/components/messages/agent-group.ts | 10 +---- .../src/tui/components/messages/read-group.ts | 10 +---- .../components/messages/agent-group.test.ts | 17 ++++++++ .../components/messages/read-group.test.ts | 41 +++++++++++++++++++ 5 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 .changeset/reduce-group-refreshes.md create mode 100644 apps/kimi-code/test/tui/components/messages/read-group.test.ts diff --git a/.changeset/reduce-group-refreshes.md b/.changeset/reduce-group-refreshes.md new file mode 100644 index 0000000000..6385d6e1df --- /dev/null +++ b/.changeset/reduce-group-refreshes.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Reduce redundant transcript refresh work for grouped agent and file-read activity. diff --git a/apps/kimi-code/src/tui/components/messages/agent-group.ts b/apps/kimi-code/src/tui/components/messages/agent-group.ts index 6fd1624d5b..9353760346 100644 --- a/apps/kimi-code/src/tui/components/messages/agent-group.ts +++ b/apps/kimi-code/src/tui/components/messages/agent-group.ts @@ -48,7 +48,6 @@ export class AgentGroupComponent extends Container { private readonly bodyContainer: Container; private throttleTimer: ReturnType | null = null; private lastFlushPhases = new Map(); - private _invalidating = false; constructor(private readonly ui: TUI | undefined) { super(); @@ -85,7 +84,6 @@ export class AgentGroupComponent extends Container { tc.setSnapshotListener(() => { this.scheduleRender(); }); - this.flushRender(); } /** @@ -143,7 +141,7 @@ export class AgentGroupComponent extends Container { if (snap !== undefined) this.lastFlushPhases.set(entry.toolCallId, snap.phase); }); - this.invalidate(); + super.invalidate(); this.ui?.requestRender(); } @@ -225,13 +223,7 @@ export class AgentGroupComponent extends Container { /** Releases throttle timers so destroyed components cannot refresh later. */ override invalidate(): void { - if (this._invalidating) { - super.invalidate(); - return; - } - this._invalidating = true; this.flushRender(); - this._invalidating = false; } dispose(): void { diff --git a/apps/kimi-code/src/tui/components/messages/read-group.ts b/apps/kimi-code/src/tui/components/messages/read-group.ts index 141562e4c0..1df96550d3 100644 --- a/apps/kimi-code/src/tui/components/messages/read-group.ts +++ b/apps/kimi-code/src/tui/components/messages/read-group.ts @@ -41,7 +41,6 @@ export class ReadGroupComponent extends Container { private readonly bodyContainer: Container; private throttleTimer: ReturnType | null = null; private lastFlushPhases = new Map(); - private _invalidating = false; constructor(private readonly ui: TUI | undefined) { super(); @@ -67,7 +66,6 @@ export class ReadGroupComponent extends Container { tc.setSnapshotListener(() => { this.scheduleRender(); }); - this.flushRender(); } /** @@ -126,7 +124,7 @@ export class ReadGroupComponent extends Container { if (snap !== undefined) this.lastFlushPhases.set(entry.toolCallId, snap.phase); }); - this.invalidate(); + super.invalidate(); this.ui?.requestRender(); } @@ -171,13 +169,7 @@ export class ReadGroupComponent extends Container { } override invalidate(): void { - if (this._invalidating) { - super.invalidate(); - return; - } - this._invalidating = true; this.flushRender(); - this._invalidating = false; } /** Releases throttle timers so destroyed components cannot refresh later. */ diff --git a/apps/kimi-code/test/tui/components/messages/agent-group.test.ts b/apps/kimi-code/test/tui/components/messages/agent-group.test.ts index adf4d3b0b9..72235d9487 100644 --- a/apps/kimi-code/test/tui/components/messages/agent-group.test.ts +++ b/apps/kimi-code/test/tui/components/messages/agent-group.test.ts @@ -60,6 +60,23 @@ describe('AgentGroupComponent', () => { vi.useRealTimers(); }); + it('requests one render when attaching an agent', () => { + vi.useFakeTimers(); + vi.setSystemTime(0); + const ui = stubTui(); + const group = new AgentGroupComponent(ui); + const agent = createAgent('call_agent_1', 'inspect project', 'reviewer', ui); + + vi.mocked(ui.requestRender).mockClear(); + + group.attach('call_agent_1', agent); + + expect(ui.requestRender).toHaveBeenCalledTimes(1); + + group.dispose(); + agent.dispose(); + }); + it('shows explicit active breakdown, row state, and waiting fallback', () => { vi.useFakeTimers(); vi.setSystemTime(0); diff --git a/apps/kimi-code/test/tui/components/messages/read-group.test.ts b/apps/kimi-code/test/tui/components/messages/read-group.test.ts new file mode 100644 index 0000000000..9d60c76d1c --- /dev/null +++ b/apps/kimi-code/test/tui/components/messages/read-group.test.ts @@ -0,0 +1,41 @@ +import type { TUI } from '@moonshot-ai/pi-tui'; +import { describe, expect, it, vi } from 'vitest'; + +import { ReadGroupComponent } from '#/tui/components/messages/read-group'; +import { ToolCallComponent } from '#/tui/components/messages/tool-call'; + +function stubTui(): TUI { + return { + terminal: { rows: 40 }, + requestRender: vi.fn(), + } as unknown as TUI; +} + +function createRead(id: string, filePath: string, ui: TUI): ToolCallComponent { + return new ToolCallComponent( + { + id, + name: 'Read', + args: { file_path: filePath }, + }, + undefined, + ui, + ); +} + +describe('ReadGroupComponent', () => { + it('requests one render when attaching a read', () => { + const ui = stubTui(); + const group = new ReadGroupComponent(ui); + const read = createRead('call_read_1', 'src/a.ts', ui); + + vi.mocked(ui.requestRender).mockClear(); + + group.attach('call_read_1', read); + + expect(ui.requestRender).toHaveBeenCalledTimes(1); + + group.dispose(); + read.dispose(); + }); +});