backendHistoryWindow.test.ts3.0 KBView on GitHub import { useCedarStore } from '@/modules/store';
import type { Message } from '@/store/messages/MessageTypes';
/**
* What the agent is told about the conversation so far.
*
* The browser holds only the newest page of a long thread and the send trims that to the last
* 50 — so on a long chat the agent used to be answering with no idea what came before, and
* nothing on the wire said so. The send now marks where its window starts, which is the server's
* cue to read the rest of the thread out of the DB.
*/
const THREAD = 'chat_long';
function msg(i: number, minute: number): Message {
return {
id: `m${i}`,
role: i % 2 === 0 ? 'user' : 'assistant',
type: 'text',
content: `turn ${i}`,
createdAt: `2026-09-01T10:${String(minute).padStart(2, '0')}:00.000Z`,
} as Message;
}
let sentBody: Record<string, unknown>;
function seed(messages: Message[], hasMoreMessages: boolean) {
useCedarStore.setState((state) => ({
...state,
threadMap: {
[THREAD]: {
id: THREAD,
lastLoaded: new Date().toISOString(),
messages,
hasMoreMessages,
},
},
mainThreadId: THREAD,
activeThreadId: THREAD,
providerConfig: { provider: 'mastra', baseURL: 'https://example.test' },
}));
}
beforeEach(() => {
sentBody = {};
global.fetch = jest.fn(async (_url: unknown, init: { body?: string } = {}) => {
sentBody = JSON.parse(init.body ?? '{}').json ?? {};
return {
ok: true,
status: 200,
body: { getReader: () => ({ read: async () => ({ done: true, value: undefined }) }) },
};
}) as unknown as typeof fetch;
});
describe('the history window sent to the agent', () => {
it('marks where the window starts when the send trimmed older turns off it', async () => {
seed(
Array.from({ length: 60 }, (_, i) => msg(i, i)),
false,
);
await useCedarStore.getState().sendMessage({ overridePrompt: 'and then?' });
expect((sentBody.messages as unknown[]).length).toBe(50);
// 60 candidates trimmed to 50 — the 10 oldest are missing, so the server is told to fetch
// everything before the 11th (index 10).
expect(sentBody.historyOldestCreatedAt).toBe(msg(10, 10).createdAt);
});
it('marks it when the thread has pages the browser never loaded', async () => {
seed([msg(0, 0), msg(1, 1)], true);
await useCedarStore.getState().sendMessage({ overridePrompt: 'and then?' });
// Only two messages in memory, but the server said there is more behind them.
expect(sentBody.historyOldestCreatedAt).toBe(msg(0, 0).createdAt);
});
it('leaves it off a short thread that is entirely in memory', async () => {
seed([msg(0, 0), msg(1, 1)], false);
await useCedarStore.getState().sendMessage({ overridePrompt: 'and then?' });
// Nothing was left out, so a backfill query would be guaranteed to return nothing.
expect(sentBody.historyOldestCreatedAt).toBeUndefined();
expect((sentBody.messages as unknown[]).length).toBe(2);
});
});