mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-28 06:41:14 +00:00
fix: harden chat history lifecycle edges
This commit is contained in:
@@ -732,6 +732,7 @@ describe("chat pane catalog session lifecycle", () => {
|
||||
pane.hasOlderMessages = vi.fn(() => true);
|
||||
pane.loadOlderMessages = vi.fn(async () => undefined);
|
||||
vi.stubGlobal("IntersectionObserver", undefined);
|
||||
vi.stubGlobal("TouchEvent", undefined);
|
||||
const thread = document.createElement("div");
|
||||
const event = new WheelEvent("wheel", { deltaY: -1 });
|
||||
Object.defineProperty(event, "currentTarget", { value: thread });
|
||||
@@ -825,6 +826,27 @@ describe("chat pane native history pagination", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("does not consume bootstrap history while disconnected", () => {
|
||||
const client = { request: vi.fn() } as unknown as GatewayBrowserClient;
|
||||
const { pane, state } = createTestChatPane({ client, sessions: {} as SessionCapability });
|
||||
state.connected = false;
|
||||
state.chatHistoryPagination = { hasMore: true, nextOffset: 2, totalMessages: 4 };
|
||||
const construct = vi.fn();
|
||||
class FakeIntersectionObserver {
|
||||
constructor() {
|
||||
construct();
|
||||
}
|
||||
disconnect() {}
|
||||
observe() {}
|
||||
}
|
||||
vi.stubGlobal("IntersectionObserver", FakeIntersectionObserver);
|
||||
|
||||
pane.syncHistoryObserver();
|
||||
|
||||
expect(construct).not.toHaveBeenCalled();
|
||||
expect(pane.historyAutoLoadBlocked).toBe(false);
|
||||
});
|
||||
|
||||
it("stops non-scrollable bootstrap after one older page", async () => {
|
||||
const request = vi.fn(async () => ({
|
||||
messages: [nativeHistoryMessage(3), nativeHistoryMessage(4)],
|
||||
|
||||
@@ -979,6 +979,7 @@ class ChatPane extends OpenClawLightDomElement {
|
||||
}
|
||||
if (
|
||||
typeof IntersectionObserver !== "function" ||
|
||||
!this.state?.connected ||
|
||||
this.loadingOlder ||
|
||||
!this.hasOlderMessages()
|
||||
) {
|
||||
@@ -1089,7 +1090,7 @@ class ChatPane extends OpenClawLightDomElement {
|
||||
let upward =
|
||||
(event instanceof WheelEvent && event.deltaY < 0) ||
|
||||
(event instanceof KeyboardEvent && CHAT_HISTORY_UPWARD_KEYS.has(event.key));
|
||||
if (event instanceof TouchEvent) {
|
||||
if (typeof TouchEvent !== "undefined" && event instanceof TouchEvent) {
|
||||
const touchY = event.touches[0]?.clientY ?? null;
|
||||
if (event.type === "touchstart") {
|
||||
this.historyTouchY = touchY;
|
||||
|
||||
@@ -148,6 +148,42 @@ describe("session message cache", () => {
|
||||
expect(snapshot?.pagination).toEqual({ hasMore: false, totalMessages: 140 });
|
||||
});
|
||||
|
||||
it("keeps the newer same-depth snapshot when a stale pane saves later", () => {
|
||||
const host = createHost();
|
||||
const cache: ChatMessageCache = new Map();
|
||||
const current = [1, 2, 3].map((seq) => ({
|
||||
content: `current-${seq}`,
|
||||
__openclaw: { seq },
|
||||
}));
|
||||
cacheChatSessionSnapshot(
|
||||
cache,
|
||||
host,
|
||||
{ sessionKey: "home" },
|
||||
{
|
||||
messages: current,
|
||||
pagination: { hasMore: false, totalMessages: 3 },
|
||||
sessionId: "session-1",
|
||||
},
|
||||
);
|
||||
|
||||
cacheChatSessionSnapshot(
|
||||
cache,
|
||||
host,
|
||||
{ sessionKey: "home" },
|
||||
{
|
||||
messages: current.slice(0, 2),
|
||||
pagination: { hasMore: true, nextOffset: 2, totalMessages: 3 },
|
||||
sessionId: "session-1",
|
||||
},
|
||||
);
|
||||
|
||||
expect(readChatSessionSnapshot(cache, host, { sessionKey: "home" })).toEqual({
|
||||
messages: current,
|
||||
pagination: { hasMore: false, totalMessages: 3 },
|
||||
sessionId: "session-1",
|
||||
});
|
||||
});
|
||||
|
||||
it("does not retain history across backing session changes", () => {
|
||||
const host = createHost();
|
||||
const cache: ChatMessageCache = new Map();
|
||||
|
||||
@@ -205,6 +205,15 @@ function mergeRetainedSessionDepth(
|
||||
const incomingBounds = transcriptSequenceBounds(incoming.messages);
|
||||
const existingTotal = existing.pagination.totalMessages;
|
||||
const incomingTotal = incoming.pagination.totalMessages;
|
||||
if (
|
||||
existingBounds &&
|
||||
incomingBounds &&
|
||||
typeof existingTotal === "number" &&
|
||||
incomingTotal === existingTotal &&
|
||||
incomingBounds.newest < existingBounds.newest
|
||||
) {
|
||||
return existing;
|
||||
}
|
||||
if (
|
||||
!existingBounds ||
|
||||
!incomingBounds ||
|
||||
@@ -212,7 +221,6 @@ function mergeRetainedSessionDepth(
|
||||
typeof incomingTotal !== "number" ||
|
||||
incomingTotal < existingTotal ||
|
||||
incomingBounds.oldest <= existingBounds.oldest ||
|
||||
incomingBounds.newest < existingBounds.newest ||
|
||||
incomingBounds.oldest > existingBounds.newest + 1
|
||||
) {
|
||||
return incoming;
|
||||
|
||||
Reference in New Issue
Block a user