import { expect, type Page } from "@playwright/test"; import type { installDaemonWebSocketGate } from "./daemon-websocket-gate"; import type { LongTimelineAgent } from "./timeline-pagination"; import { expectReconnectingToastGone, expectReconnectingToastVisible } from "./workspace-ui"; type DaemonWebSocketGate = Awaited>; export interface TimelineRequestCounts { after: number; tail: number; } export interface BackgroundTimelineTurns { firstPrompt: string; newestPrompt: string; } function backgroundPrompt(index: number): string { return `timeline-resume-background-${index}: emit 1 coalesced agent stream updates`; } export async function commitTimelineTurnsWhileDisconnected( agent: LongTimelineAgent, count: number, ): Promise { for (let index = 0; index < count; index += 1) { await agent.client.sendAgentMessage(agent.agentId, backgroundPrompt(index)); await agent.client.waitForFinish(agent.agentId, 15_000); } return { firstPrompt: backgroundPrompt(0), newestPrompt: backgroundPrompt(count - 1), }; } export function rememberTimelineRequestCounts(gate: DaemonWebSocketGate): TimelineRequestCounts { return { after: gate.getTimelineRequestCount("after"), tail: gate.getTimelineRequestCount("tail"), }; } export function expectOneTailCheckWithoutCatchUpPages( gate: DaemonWebSocketGate, before: TimelineRequestCounts, ): void { expect(gate.getTimelineRequestCount("tail") - before.tail).toBe(1); expect(gate.getTimelineRequestCount("after") - before.after).toBe(0); } export async function disconnectViewedTimeline( page: Page, gate: DaemonWebSocketGate, ): Promise { await gate.drop(); await expectReconnectingToastVisible(page); } export async function restoreViewedTimelineWithHeldTail( page: Page, gate: DaemonWebSocketGate, ): Promise { gate.holdNextServerMessage("fetch_agent_timeline_response"); gate.restore(); await gate.waitForHeldServerMessage(); gate.releaseHeldServerMessage(); await expectReconnectingToastGone(page); }