feat: add local SQLite memory store

This commit is contained in:
Eyejoker
2026-03-31 23:22:46 +09:00
parent 1c287f4ad0
commit 42ed5ef113
4 changed files with 530 additions and 0 deletions

View File

@@ -30,6 +30,7 @@ import {
getNewMessagesBySeq,
getOpenWorkItem,
getPendingServiceHandoffs,
recallMemories,
getRegisteredAgentTypesForJid,
getMessagesSince,
getNewMessages,
@@ -46,6 +47,7 @@ import {
setSession,
setRouterStateForService,
setExplicitRoomMode,
rememberMemory,
storeChatMetadata,
storeMessage,
updateRegisteredGroupName,
@@ -1280,6 +1282,68 @@ describe('message seq cursors', () => {
});
});
describe('memories', () => {
it('recalls scoped memories through FTS and exact keyword matching', () => {
rememberMemory({
scopeKind: 'room',
scopeKey: 'room:test-group',
content: '세션 재시작 후에도 방 메모리를 주입한다.',
keywords: ['room:test-group', 'session-reset'],
sourceKind: 'compact',
sourceRef: 'compact:1',
});
rememberMemory({
scopeKind: 'room',
scopeKey: 'room:test-group',
content: '이 메모리는 다른 검색어다.',
keywords: ['room:test-group'],
sourceKind: 'compact',
sourceRef: 'compact:2',
});
const byText = recallMemories({
scopeKind: 'room',
scopeKey: 'room:test-group',
text: 'session reset',
limit: 5,
});
expect(byText).toHaveLength(1);
expect(byText[0].content).toContain('방 메모리를 주입한다');
const byKeyword = recallMemories({
scopeKind: 'room',
scopeKey: 'room:test-group',
keywords: ['session-reset'],
limit: 5,
});
expect(byKeyword).toHaveLength(1);
expect(byKeyword[0].content).toContain('방 메모리를 주입한다');
});
it('archives old memories when a scope exceeds its bounded limit', () => {
for (let index = 0; index < 305; index += 1) {
rememberMemory({
scopeKind: 'room',
scopeKey: 'room:bounded',
content: `memory-${index}`,
keywords: ['room:bounded'],
sourceKind: 'compact',
sourceRef: `compact:${index}`,
});
}
const recalled = recallMemories({
scopeKind: 'room',
scopeKey: 'room:bounded',
limit: 500,
});
expect(recalled).toHaveLength(300);
expect(recalled.some((memory) => memory.content === 'memory-0')).toBe(false);
expect(recalled.some((memory) => memory.content === 'memory-304')).toBe(true);
});
});
describe('work items', () => {
it('tracks produced, retry, and delivered states', () => {
const item = createProducedWorkItem({