mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-21 00:05:49 +02:00
feat: make chat-style tasks the default experience (#11101)
This commit is contained in:
1 parent
2da6a248c3
commit
815e49bb7c
80 files changed
+3858
-1497
No files matched your search
@@ -515,4 +515,112 @@ describe("useLiveRunTranscripts", () => {
|
||||
});
|
||||
container.remove();
|
||||
});
|
||||
|
||||
it("retains an accumulated buffer through a transient empty poll (PAP-462 B3)", async () => {
|
||||
const ts = "2026-08-08T00:00:00.000Z";
|
||||
const row = JSON.stringify({
|
||||
ts,
|
||||
stream: "stdout",
|
||||
chunk: '{"type":"acpx.text_delta","text":"hello"}\n',
|
||||
seq: 1,
|
||||
});
|
||||
// Serve the buffered chunk on the FIRST persisted-log read only; every later
|
||||
// read (including after the run reappears) returns nothing new. So the chunk
|
||||
// can only be present the second time if the buffer survived the gap.
|
||||
logMock.mockResolvedValue({ runId: "run-1", store: "memory", logRef: "log-1", content: "", nextOffset: 100 });
|
||||
logMock.mockResolvedValueOnce({ runId: "run-1", store: "memory", logRef: "log-1", content: `${row}\n`, nextOffset: 100 });
|
||||
|
||||
const captured: { value: ReturnType<typeof useLiveRunTranscripts> | null } = { value: null };
|
||||
function Harness({ runs }: { runs: Array<{ id: string; status: string; adapterType: string }> }) {
|
||||
captured.value = useLiveRunTranscripts({ companyId: "company-1", runs, enableRealtimeUpdates: false });
|
||||
return null;
|
||||
}
|
||||
|
||||
const container = document.createElement("div");
|
||||
document.body.appendChild(container);
|
||||
const root = createRoot(container);
|
||||
const runList = [{ id: "run-1", status: "running", adapterType: "gemini_local" }];
|
||||
|
||||
await act(async () => {
|
||||
root.render(<Harness runs={runList} />);
|
||||
await Promise.resolve();
|
||||
});
|
||||
expect(captured.value?.transcriptByRun.get("run-1")).toHaveLength(1);
|
||||
|
||||
// Transient empty poll: run momentarily absent from the list.
|
||||
await act(async () => {
|
||||
root.render(<Harness runs={[]} />);
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
// Run reappears within the grace window — the buffer must survive rather than
|
||||
// re-hydrate from a (now empty) truncated read.
|
||||
await act(async () => {
|
||||
root.render(<Harness runs={runList} />);
|
||||
await Promise.resolve();
|
||||
});
|
||||
expect(captured.value?.transcriptByRun.get("run-1")).toHaveLength(1);
|
||||
|
||||
act(() => {
|
||||
root.unmount();
|
||||
});
|
||||
container.remove();
|
||||
});
|
||||
|
||||
it("prunes a buffer once a run stays absent past the grace window (PAP-462 B3)", async () => {
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
const ts = "2026-08-08T00:00:00.000Z";
|
||||
const row = JSON.stringify({
|
||||
ts,
|
||||
stream: "stdout",
|
||||
chunk: '{"type":"acpx.text_delta","text":"hello"}\n',
|
||||
seq: 1,
|
||||
});
|
||||
logMock.mockResolvedValue({ runId: "run-1", store: "memory", logRef: "log-1", content: "", nextOffset: 100 });
|
||||
logMock.mockResolvedValueOnce({ runId: "run-1", store: "memory", logRef: "log-1", content: `${row}\n`, nextOffset: 100 });
|
||||
|
||||
const captured: { value: ReturnType<typeof useLiveRunTranscripts> | null } = { value: null };
|
||||
function Harness({ runs }: { runs: Array<{ id: string; status: string; adapterType: string }> }) {
|
||||
captured.value = useLiveRunTranscripts({ companyId: "company-1", runs, enableRealtimeUpdates: false });
|
||||
return null;
|
||||
}
|
||||
|
||||
const container = document.createElement("div");
|
||||
document.body.appendChild(container);
|
||||
const root = createRoot(container);
|
||||
const runList = [{ id: "run-1", status: "running", adapterType: "gemini_local" }];
|
||||
|
||||
await act(async () => {
|
||||
root.render(<Harness runs={runList} />);
|
||||
await Promise.resolve();
|
||||
});
|
||||
expect(captured.value?.transcriptByRun.get("run-1")).toHaveLength(1);
|
||||
|
||||
await act(async () => {
|
||||
root.render(<Harness runs={[]} />);
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
// Stay absent long enough for the grace window to lapse and the deferred
|
||||
// prune to fire.
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(25_000);
|
||||
});
|
||||
|
||||
// On reappear the buffer is gone, so the (now empty) read rebuilds nothing.
|
||||
await act(async () => {
|
||||
root.render(<Harness runs={runList} />);
|
||||
await Promise.resolve();
|
||||
});
|
||||
expect(captured.value?.transcriptByRun.get("run-1") ?? []).toHaveLength(0);
|
||||
|
||||
act(() => {
|
||||
root.unmount();
|
||||
});
|
||||
container.remove();
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user