Menu
popagent
publicLatest change 6edc7e928c57dab23c0973ed3f7ee70b3ebb72d5 - Initial popagent baseline by AkurAI Build
import { beforeAll, describe, expect, test } from "bun:test";
import { LongTermMemoryStore } from "./long-term-memory";
const store = new LongTermMemoryStore();
const resourceId = `memory-test-${crypto.randomUUID()}`;
beforeAll(() => store.init());
describe("LongTermMemoryStore", () => {
test("retains an episode and recalls it across sessions", async () => {
await store.retainEpisode({
resourceId,
sessionId: "first-session",
userText: "I always use dark mode and prefer concise answers",
assistantText: "I will remember those preferences.",
});
const recalled = await store.recall({
resourceId,
query: "What display mode and answer style do I prefer?",
});
expect(recalled.some((memory) => memory.content.includes("dark mode"))).toBe(true);
expect(recalled.some((memory) => memory.sessionId === "first-session")).toBe(true);
});
test("upserts explicit facts and ranks them above episodes", async () => {
await store.rememberFact({
resourceId,
sessionId: "second-session",
content: "The user's preferred runtime is Bun.",
importance: 0.9,
});
await store.rememberFact({
resourceId,
sessionId: "third-session",
content: "The user's preferred runtime is Bun.",
importance: 0.9,
});
const recalled = await store.recall({ resourceId, query: "preferred runtime" });
expect(recalled[0]).toEqual(expect.objectContaining({
kind: "fact",
content: "The user's preferred runtime is Bun.",
}));
expect(recalled.filter((memory) => memory.content.includes("preferred runtime is Bun"))).toHaveLength(1);
});
test("does not leak memories between resources", async () => {
const recalled = await store.recall({
resourceId: `${resourceId}-other`,
query: "dark mode Bun",
});
expect(recalled).toEqual([]);
});
test("enforces the durable-fact size limit for every write path", async () => {
const content = "x".repeat(2_001);
await expect(store.rememberFact({
resourceId,
sessionId: "oversized-fact",
content,
})).rejects.toThrow("Fact content exceeds 2000 characters");
const existing = (await store.list({ resourceId, kind: "fact", limit: 1 }))[0];
expect(existing).toBeDefined();
await expect(store.updateFact({
id: existing!.id,
resourceId,
content,
importance: 1,
})).rejects.toThrow("Fact content exceeds 2000 characters");
});
test("truncates retained episodes and total rendered recall context", async () => {
const marker = `budget${crypto.randomUUID().replaceAll("-", "")}`;
await store.retainEpisode({
resourceId,
sessionId: "large-episode",
userText: `${marker} ${"u".repeat(8_000)}`,
assistantText: `${marker} ${"a".repeat(16_000)}`,
});
const episode = (await store.list({ resourceId, query: marker, kind: "episode", limit: 1 }))[0];
expect(episode).toBeDefined();
expect(episode!.content.length).toBeLessThanOrEqual(12_050);
expect(episode!.content.match(/… \[truncated\]/g)).toHaveLength(2);
for (let index = 0; index < 15; index++) {
await store.rememberFact({
resourceId,
sessionId: "recall-budget",
key: `${marker}.${index}`,
content: `${marker} ${index} `.padEnd(2_000, String(index % 10)),
importance: 1,
});
}
const rendered = await store.formatRecall({ resourceId, query: marker, limit: 20 });
expect(rendered).toBeDefined();
expect(rendered!.length).toBeLessThanOrEqual(24_000);
expect(rendered).toContain("… [truncated]");
});
});