Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/coding-agent/src/modes/interactive/changes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# changes

## bounded compaction progress row (2026-07-24)

### What changed

- `components/status-indicator.ts`: the combined compaction spinner, status, and streamed preview is truncated to the
actual terminal width.
- `../../../test/interactive-mode-compaction.test.ts`: pins a hostile multiline, 600-column progress update to one
rendered row no wider than the requested terminal width.

### Why

- Long streamed summaries could wrap the otherwise single-row lifecycle indicator, push the composer upward, and make
previous output appear erased as the terminal viewport remapped.

### Expected merge conflict zones

- LOW: `components/status-indicator.ts` compaction progress rendering.

## accepted-only compaction queue transfer (2026-07-24)

### What changed
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { type Component, Loader, type LoaderIndicatorOptions, type TUI } from "@earendil-works/pi-tui";
import {
type Component,
Loader,
type LoaderIndicatorOptions,
type TUI,
truncateToWidth,
visibleWidth,
} from "@earendil-works/pi-tui";
import { theme } from "../theme/theme.ts";
import { CountdownTimer } from "./countdown-timer.ts";
import { keyText } from "./keybinding-hints.ts";
Expand Down Expand Up @@ -105,9 +112,12 @@ export class CompactionStatusIndicator extends StatusIndicator {
}

override render(width: number): string[] {
const status = super.render(width).join(" ");
if (!this.progressText) return [status];
return [`${status} ${theme.fg("muted", this.progressText)}`];
if (!this.progressText) return super.render(width);
const progressWidth = Math.min(visibleWidth(this.progressText), Math.max(1, Math.floor(width / 2)));
const statusWidth = Math.max(1, width - progressWidth - 1);
const status = truncateToWidth(super.render(statusWidth).join(" "), statusWidth, "");
const progress = truncateToWidth(theme.fg("muted", this.progressText), progressWidth);
return [truncateToWidth(`${status} ${progress}`, width)];
}
}

Expand Down
15 changes: 9 additions & 6 deletions packages/coding-agent/test/interactive-mode-compaction.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { Container, sanitizeTerminalLabel } from "@earendil-works/pi-tui";
import { Container, sanitizeTerminalLabel, visibleWidth } from "@earendil-works/pi-tui";
import { beforeAll, describe, expect, test, vi } from "vitest";
import { SessionManager } from "../src/core/session-manager.ts";
import { CompactionSummaryMessageComponent } from "../src/modes/interactive/components/compaction-summary-message.ts";
Expand Down Expand Up @@ -97,7 +97,7 @@ describe("InteractiveMode compaction events", () => {
fakeThis.autoCompactionLoader?.stop();
});

test("renders streamed compaction progress below the active loader", async () => {
test("bounds streamed compaction progress to the active status row", async () => {
const statusContainer = new Container();
const fakeThis = {
isInitialized: true,
Expand Down Expand Up @@ -133,12 +133,15 @@ describe("InteractiveMode compaction events", () => {
await handleEvent.call(fakeThis, {
type: "compaction_progress",
reason: "extension",
delta: "live summary chunk",
delta: `live\n${"summary chunk ".repeat(40)}`,
});

const rendered = stripAnsi(statusContainer.children.flatMap((child) => child.render(120)).join("\n"));
expect(rendered).toContain("Compacting context");
expect(rendered).toContain("live summary chunk");
const renderedLines = statusContainer.children.flatMap((child) => child.render(40));
const rendered = stripAnsi(renderedLines.join("\n"));
expect(renderedLines).toHaveLength(1);
expect(visibleWidth(renderedLines[0] ?? "")).toBeLessThanOrEqual(40);
expect(rendered).toContain("Compacting");
expect(rendered).toContain("live summary");

fakeThis.autoCompactionLoader?.stop();
});
Expand Down
Loading