Files
oh-my-pi/packages/coding-agent/test/modes/components/compaction-divider.test.ts
T
can1357 2b28816010 feat(coding-agent): replaced session observer with Agent Hub and inline compaction divider
The session-observer overlay is gone. The Agent Hub (ctrl+s, alt+a, or double-tap left arrow on an empty editor) presents one overlay with two views: a live registry table (status, unread irc count, current task, last activity; j/k to navigate, r to revive, x to abort/release) and per-agent chat (transcript + input line) — submitting revives a parked agent and steers it via the normal prompt path. Main is the ambient chat and stays out of the table.\n\nrenderInitialMessages no longer takes a prebuilt context: every redraw now reaches for AgentSession.buildTranscriptSessionContext() (full-history transcript with each compaction emitted inline at the point it fired, snapcompact frames re-attached on rebuild). UiHelpers drops the deferred-compaction render and the IRC autoreply branch that the new mailbox bus deprecated. CompactionSummaryMessage renders as a slim divider (── 📷 compacted · ctrl+o ──), expanding to the summary + snapcompact frame count. session-manager.buildSessionContext gains a { transcript: true } mode; an exported buildSessionContextFromFile() reads a session file without taking the writer lock so the hub chat view can tail any agent (parked or live). Theme picks up icon.camera + tool.irc symbol entries.
2026-06-10 17:51:42 +02:00

64 lines
2.6 KiB
TypeScript

/**
* Contract: the compaction point renders as a slim horizontal divider —
* `── 📷 compacted · ctrl+o ──` — instead of a full summary box, keeping the
* transcript visually continuous. Expansion (ctrl+o) reveals the summary.
* The render cache must honor the pi-tui same-reference contract: unchanged
* components return the identical array so containers can memoize.
*/
import { beforeAll, describe, expect, it } from "bun:test";
import { createCompactionSummaryMessage } from "@oh-my-pi/pi-agent-core/compaction";
import type { ImageContent } from "@oh-my-pi/pi-ai";
import { CompactionSummaryMessageComponent } from "@oh-my-pi/pi-coding-agent/modes/components/compaction-summary-message";
import { initTheme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
beforeAll(() => {
initTheme();
});
const SUMMARY = "Earlier the user fixed the login TTL bug.";
function makeComponent(images?: ImageContent[]): CompactionSummaryMessageComponent {
return new CompactionSummaryMessageComponent(
createCompactionSummaryMessage(SUMMARY, 84000, new Date().toISOString(), undefined, undefined, images),
);
}
describe("CompactionSummaryMessageComponent", () => {
it("collapsed: a single full-width divider carrying the expand affordance", () => {
const lines = makeComponent().render(80);
expect(lines.length).toBe(3); // breathing room above and below the rule
const rule = Bun.stripANSI(lines[1]);
expect(rule).toContain("compacted");
expect(rule).toContain("ctrl+o");
// The rule spans the full width and hides the summary body.
expect(Bun.stringWidth(rule)).toBe(80);
expect(rule).not.toContain(SUMMARY);
});
it("expanded: reveals the summary (and snapcompact frame count) below the divider", () => {
const component = makeComponent([{ type: "image", data: "ZmFrZQ==", mimeType: "image/png" }]);
component.setExpanded(true);
const text = Bun.stripANSI(component.render(80).join("\n"));
expect(text).toContain("compacted");
expect(text).toContain(SUMMARY);
expect(text).toContain("tokens");
expect(text).toContain("1 snapcompact frame attached");
});
it("degrades to a bare label when the viewport is too narrow for a framed rule", () => {
const lines = makeComponent().render(10);
expect(Bun.stripANSI(lines[1])).toContain("compacted");
});
it("honors the same-reference render cache and busts it on expansion toggle", () => {
const component = makeComponent();
const first = component.render(80);
expect(component.render(80)).toBe(first);
component.setExpanded(true);
const expanded = component.render(80);
expect(expanded).not.toBe(first);
expect(component.render(80)).toBe(expanded);
});
});