Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion packages/dmoss-agent/src/core/session/session-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,14 @@ function buildSessionContext(state: SessionState): Message[] {
const compactionIdx = path.findIndex(
(entry) => entry.type === "compaction" && entry.id === compaction.id,
);
let foundFirstKept = false;
// If firstKeptEntryId is missing from the path (corruption/race — the codec
// only warns on load), the kept tail would otherwise be silently dropped on
// replay, losing recent history the compaction summary does NOT cover. Fall
// back to keeping every pre-compaction entry instead of dropping them.
const firstKeptExists = path
.slice(0, compactionIdx < 0 ? 0 : compactionIdx)
.some((entry) => entry.id === compaction.firstKeptEntryId);
let foundFirstKept = !firstKeptExists;
for (let i = 0; i < compactionIdx; i++) {
const entry = path[i];
if (entry.id === compaction.firstKeptEntryId) {
Expand Down
44 changes: 44 additions & 0 deletions packages/dmoss-agent/test/session-compaction-replay.spec.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env node
/**
* Compaction replay must not silently drop kept history when
* compaction.firstKeptEntryId is missing from the path (corruption/race — the
* JSONL codec only warns on load). Before the fix, buildSessionContext never
* flipped foundFirstKept, so every pre-compaction entry was dropped, losing
* recent history the summary does NOT cover. Red before / green after.
*
* Run:
* npm run build -w @rdk-moss/agent
* node packages/dmoss-agent/test/session-compaction-replay.spec.mjs
*/
import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { SessionManager } from '../dist/core/session/session-manager.js';

const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'dmoss-compaction-replay-'));
try {
const sm = new SessionManager(tmpDir);
const key = 's1';

await sm.append(key, { role: 'user', content: 'PRECOMP-1' });
await sm.append(key, { role: 'assistant', content: 'PRECOMP-2' });
await sm.append(key, { role: 'user', content: 'PRECOMP-3' });
// Compaction whose firstKeptEntryId does NOT exist in the path.
await sm.appendCompaction(key, 'THE-COMPACTION-SUMMARY', 'nonexistent-kept-id', 100);
await sm.append(key, { role: 'assistant', content: 'POSTCOMP-1' });

const ctx = await sm.load(key);
const text = JSON.stringify(ctx);

assert.match(text, /THE-COMPACTION-SUMMARY/, 'compaction summary must be replayed');
assert.match(text, /POSTCOMP-1/, 'post-compaction message must be present');
// The core bug: pre-compaction kept messages must NOT vanish when
// firstKeptEntryId is missing.
assert.match(text, /PRECOMP-1/, 'pre-compaction history must not be lost when firstKeptEntryId is missing');
assert.match(text, /PRECOMP-3/, 'all pre-compaction messages preserved');

console.log(' [PASS] compaction replay preserves history when firstKeptEntryId is missing');
} finally {
fs.rmSync(tmpDir, { recursive: true, force: true });
}
Loading