Files
waggle-os/packages/agent/tests/w46-rawdetail-recall.test.ts
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

124 lines
4.9 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { MindDB, FrameStore, SessionStore, rawTurnHeader, type Reranker } from '@waggle/core';
import { Orchestrator } from '../src/orchestrator.js';
import { MockEmbedder } from '../../hive-mind-core/tests/mind/helpers/mock-embedder.js';
/**
* W4.6 — RAWDETAIL lane rendering in recallMemory (plan component #10,
* query side). Raw-turn frames are written by harvest (W4.6 write side);
* here we seed them directly and assert: section renders LAST, CE-top
* turn + dialogue neighbors included, kill switch + no-reranker skip.
*/
/** Deterministic fake CE: score = marker-word hit count. */
function markerReranker(marker: string): Reranker {
return {
scoreBatch: async (_q: string, docs: string[]) =>
docs.map(d => (d.includes(marker) ? 1 : 0)),
} as Reranker;
}
describe('W4.6 — recallMemory raw dialogue excerpts', () => {
let db: MindDB;
let frames: FrameStore;
let gopId: string;
let savedKill: string | undefined;
beforeEach(() => {
db = new MindDB(':memory:');
frames = new FrameStore(db);
gopId = new SessionStore(db).create().gop_id;
savedKill = process.env['WAGGLE_RAWDETAIL'];
});
afterEach(() => {
if (savedKill === undefined) delete process.env['WAGGLE_RAWDETAIL'];
else process.env['WAGGLE_RAWDETAIL'] = savedKill;
db.close();
});
function seedTurns(): void {
const texts = [
'we should plan the gallery visit for the weekend',
'I loved the painting of a sunset with a pink sky',
'it was hanging in the east wing near the entrance',
];
texts.forEach((text, i) => {
frames.createIFrame(
gopId,
`${rawTurnHeader('conv-x', i, i % 2 === 0 ? 'user' : 'assistant')}\n${text}`,
'normal',
'import',
`2026-05-${String(10 + i).padStart(2, '0')}T12:00:00Z`,
);
});
}
it('renders the verbatim excerpts section with CE-top turn + neighbors, dated and speaker-tagged', async () => {
seedTurns();
const orchestrator = new Orchestrator({
db, embedder: new MockEmbedder(), reranker: markerReranker('painting'),
});
const result = await orchestrator.recallMemory('which painting did I love?');
const text = result.text;
expect(text).toContain('## Raw dialogue excerpts (verbatim)');
expect(text).toContain('painting of a sunset with a pink sky');
// ±1 neighbors ride along
expect(text).toContain('gallery visit');
expect(text).toContain('east wing');
// speaker + date prefixes — parenthesized speaker (colon-suffixed role
// labels would collide with the injection scanner's smuggling patterns)
expect(text).toMatch(/- \[2026-05-11\] \(assistant\) I loved the painting/);
// rendered LAST — after every other section
const rawIdx = text.indexOf('## Raw dialogue excerpts');
for (const section of ['## Workspace Memory', '## Personal Memory', '## Memory Facts']) {
const idx = text.indexOf(section);
if (idx >= 0) expect(idx).toBeLessThan(rawIdx);
}
});
it('counts raw excerpts in totalCount and surfaces them in recalled', async () => {
seedTurns();
const orchestrator = new Orchestrator({
db, embedder: new MockEmbedder(), reranker: markerReranker('painting'),
});
const result = await orchestrator.recallMemory('which painting did I love?');
expect(result.count).toBeGreaterThanOrEqual(3);
expect(result.recalled?.some(s => s.includes('pink sky'))).toBe(true);
});
it('WAGGLE_RAWDETAIL=0 kill switch suppresses the lane', async () => {
seedTurns();
process.env['WAGGLE_RAWDETAIL'] = '0';
const orchestrator = new Orchestrator({
db, embedder: new MockEmbedder(), reranker: markerReranker('painting'),
});
const result = await orchestrator.recallMemory('which painting did I love?');
expect(result.text).not.toContain('## Raw dialogue excerpts');
});
it('skips the lane when no reranker is available (no CE floor → no lane)', async () => {
seedTurns();
process.env['WAGGLE_RERANKER'] = '0';
try {
const orchestrator = new Orchestrator({ db, embedder: new MockEmbedder() });
const result = await orchestrator.recallMemory('which painting did I love?');
expect(result.text).not.toContain('## Raw dialogue excerpts');
} finally {
delete process.env['WAGGLE_RERANKER'];
}
});
it('raw turns surfaced as search snippets never double-render in the excerpts section', async () => {
seedTurns();
const orchestrator = new Orchestrator({
db, embedder: new MockEmbedder(), reranker: markerReranker('painting'),
});
const result = await orchestrator.recallMemory('which painting did I love?');
// the CE-top turn body must appear exactly once in the rendered block
const occurrences = result.text.split('painting of a sunset with a pink sky').length - 1;
expect(occurrences).toBe(1);
});
});