mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-04 07:33:18 +00:00
Long agent turns bury the answer under intermediate messages, thoughts and tool calls. A new Chat display preference (Full / Collapse tool calls / Collapse turns) replaces the tool call detail level. Collapse turns folds everything a finished turn did before its final message into one summary line under the prompt; opening it shows those rows inline behind the same indent rail that expanded tool call groups now use. Expanded turns stay real timeline rows so virtualization, chat find and scroll-to-message keep working; find and outline jumps open the turn that holds their target. Opening or closing a turn stops following output so the summary stays under the pointer. Collapse turns is the default for new installs only. Existing settings were materialized on first launch, so stored blobs without the new field map from the legacy tool call detail level and otherwise stay on Full. Co-authored-by: Ready <127541578+akaready@users.noreply.github.com> Co-authored-by: Waishnav <86405648+Waishnav@users.noreply.github.com> Co-authored-by: Shreyan Dey <90114886+Dey11@users.noreply.github.com> Co-authored-by: Sebastian Parfitt <7753735+Sebeb@users.noreply.github.com>
68 lines
2.7 KiB
TypeScript
68 lines
2.7 KiB
TypeScript
import type { Page } from "@playwright/test";
|
|
import { test, expect } from "../support/fixtures";
|
|
import { expectComposerVisible } from "../support/helpers/composer";
|
|
import { openAgentRoute, seedMockAgentWorkspace } from "../support/helpers/mock-agent";
|
|
|
|
type ChatDisplay = "full" | "collapseToolCalls" | "collapseTurns";
|
|
|
|
async function useChatDisplay(page: Page, chatDisplay: ChatDisplay): Promise<void> {
|
|
await page.addInitScript((value) => {
|
|
localStorage.setItem("@paseo:app-settings", JSON.stringify({ chatDisplay: value }));
|
|
}, chatDisplay);
|
|
}
|
|
|
|
async function seedAnsweredAgent(title: string) {
|
|
const agent = await seedMockAgentWorkspace({
|
|
repoPrefix: "turn-collapse-",
|
|
title,
|
|
model: "ten-second-stream",
|
|
initialPrompt: "Why does the chat stop following new output?",
|
|
});
|
|
await agent.client.waitForFinish(agent.agentId, 30_000);
|
|
await agent.client.sendAgentMessage(agent.agentId, "Make the fix and run the burst script.");
|
|
await agent.client.waitForFinish(agent.agentId, 30_000);
|
|
return agent;
|
|
}
|
|
|
|
/** Chat rows between the latest turn's summary line and its reply, in document order. */
|
|
async function rowsBetweenLatestSummaryAndReply(page: Page): Promise<number> {
|
|
return page.evaluate(() => {
|
|
const rows = [...document.querySelectorAll("[data-message-id]")];
|
|
const summaryRow = rows.findLastIndex((row) => row.querySelector("[data-testid=turn-summary]"));
|
|
const replyRow = rows.findLastIndex((row) =>
|
|
row.querySelector("[data-testid=assistant-message]"),
|
|
);
|
|
return replyRow - summaryRow - 1;
|
|
});
|
|
}
|
|
|
|
test.describe("Turn collapse", () => {
|
|
test("folds each answered turn behind a summary line that opens it", async ({ page }) => {
|
|
test.setTimeout(120_000);
|
|
await useChatDisplay(page, "collapseTurns");
|
|
const agent = await seedAnsweredAgent("Turn collapse");
|
|
try {
|
|
await openAgentRoute(page, agent);
|
|
await expectComposerVisible(page);
|
|
|
|
const summary = page.getByTestId("turn-summary").last().getByRole("button");
|
|
await expect(summary).toHaveText(/^Wrote \d+ messages?, edited 1 file/);
|
|
// Text chunks that stream back to back merge into one message, so the reply holds
|
|
// at least the stream's last chunk.
|
|
await expect(page.getByTestId("assistant-message").last()).toHaveText(
|
|
/\(end of synthetic stream\)$/,
|
|
);
|
|
await expect.poll(() => rowsBetweenLatestSummaryAndReply(page)).toBe(0);
|
|
|
|
await summary.click();
|
|
await expect.poll(() => rowsBetweenLatestSummaryAndReply(page)).toBeGreaterThan(0);
|
|
await expect(summary).toBeInViewport();
|
|
|
|
await summary.click();
|
|
await expect.poll(() => rowsBetweenLatestSummaryAndReply(page)).toBe(0);
|
|
} finally {
|
|
await agent.cleanup();
|
|
}
|
|
});
|
|
});
|