Keep older chat history and image previews stable (#2490)
* fix(app): keep timeline history and previews stable Treat persisted timeline replicas as display-only so authoritative pagination always comes from the daemon. Require renewed user intent between older-history pages unless the viewport genuinely remains at the history edge. Model assistant image acquisition as loading, loaded, or failed so recreating a preview URL cannot be rendered as an error. * fix(app): keep file image previews current * fix(app): stabilize history pagination edges * fix(app): recover transient timeline loads * fix(app): retain assistant image previews * Stabilize history pagination lifecycle * Stabilize history settlement and image previews * Retry file images after reconnect * Keep active previews and pagination requests alive * Replace image hook tests with typed ports * Integrate timeline hydration with submission authority * Harden pre-hydration timeline reconciliation * Make rewind E2E use real scroll intent * Preserve live timeline state through hydration * Keep mounted image attachments retained * Protect preview persistence and lifecycle hydration * Preserve idless live assistant continuations * Preserve timeline rows across delayed hydration * test(app): cover real assistant image files * Preserve assistant tool ordering during hydration
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { runAssistantImageOperationWithRetry } from "./retry";
|
||||
|
||||
describe("assistant image retry", () => {
|
||||
it("recovers from transient failures while the image remains mounted", async () => {
|
||||
const waits: number[] = [];
|
||||
let attempts = 0;
|
||||
|
||||
const result = await runAssistantImageOperationWithRetry({
|
||||
operation: async () => {
|
||||
attempts += 1;
|
||||
if (attempts < 3) {
|
||||
throw new Error("transient");
|
||||
}
|
||||
return "loaded";
|
||||
},
|
||||
delaysMs: [10, 20, 30],
|
||||
wait: async (delayMs) => {
|
||||
waits.push(delayMs);
|
||||
},
|
||||
});
|
||||
|
||||
expect({ attempts, waits, result }).toEqual({
|
||||
attempts: 3,
|
||||
waits: [10, 20],
|
||||
result: "loaded",
|
||||
});
|
||||
});
|
||||
|
||||
it("stops retrying after the image is released", async () => {
|
||||
let stopped = false;
|
||||
let attempts = 0;
|
||||
|
||||
await expect(
|
||||
runAssistantImageOperationWithRetry({
|
||||
operation: async () => {
|
||||
attempts += 1;
|
||||
throw new Error("transient");
|
||||
},
|
||||
delaysMs: [10, 20],
|
||||
shouldStop: () => stopped,
|
||||
wait: async () => {
|
||||
stopped = true;
|
||||
},
|
||||
}),
|
||||
).rejects.toThrow("transient");
|
||||
expect(attempts).toBe(1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user