2b28816010
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.
64 lines
2.6 KiB
TypeScript
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);
|
|
});
|
|
});
|