wip: [01-stabilize] paused at task 1/1 - OCR Hallucination Immune logic via Semantic delta window and fret-isolation
This commit is contained in:
161
.agent/services/claude-mem/tests/sdk-agent-resume.test.ts
Normal file
161
.agent/services/claude-mem/tests/sdk-agent-resume.test.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
import { describe, it, expect } from 'bun:test';
|
||||
|
||||
/**
|
||||
* Tests for SDKAgent resume parameter logic
|
||||
*
|
||||
* The resume parameter should ONLY be passed when:
|
||||
* 1. memorySessionId exists (was captured from a previous SDK response)
|
||||
* 2. lastPromptNumber > 1 (this is a continuation within the same SDK session)
|
||||
*
|
||||
* On worker restart or crash recovery, memorySessionId may exist from a previous
|
||||
* SDK session but we must NOT resume because the SDK context was lost.
|
||||
*/
|
||||
describe('SDKAgent Resume Parameter Logic', () => {
|
||||
/**
|
||||
* Helper function that mirrors the logic in SDKAgent.startSession()
|
||||
* This is the exact condition used at SDKAgent.ts line 99
|
||||
*/
|
||||
function shouldPassResumeParameter(session: {
|
||||
memorySessionId: string | null;
|
||||
lastPromptNumber: number;
|
||||
}): boolean {
|
||||
const hasRealMemorySessionId = !!session.memorySessionId;
|
||||
return hasRealMemorySessionId && session.lastPromptNumber > 1;
|
||||
}
|
||||
|
||||
describe('INIT prompt scenarios (lastPromptNumber === 1)', () => {
|
||||
it('should NOT pass resume parameter when lastPromptNumber === 1 even if memorySessionId exists', () => {
|
||||
// Scenario: Worker restart with stale memorySessionId from previous session
|
||||
const session = {
|
||||
memorySessionId: 'stale-session-id-from-previous-run',
|
||||
lastPromptNumber: 1, // INIT prompt
|
||||
};
|
||||
|
||||
const hasRealMemorySessionId = !!session.memorySessionId;
|
||||
const shouldResume = shouldPassResumeParameter(session);
|
||||
|
||||
expect(hasRealMemorySessionId).toBe(true); // memorySessionId exists
|
||||
expect(shouldResume).toBe(false); // but should NOT resume because it's INIT
|
||||
});
|
||||
|
||||
it('should NOT pass resume parameter when memorySessionId is null and lastPromptNumber === 1', () => {
|
||||
// Scenario: Fresh session, first prompt ever
|
||||
const session = {
|
||||
memorySessionId: null,
|
||||
lastPromptNumber: 1,
|
||||
};
|
||||
|
||||
const hasRealMemorySessionId = !!session.memorySessionId;
|
||||
const shouldResume = shouldPassResumeParameter(session);
|
||||
|
||||
expect(hasRealMemorySessionId).toBe(false);
|
||||
expect(shouldResume).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CONTINUATION prompt scenarios (lastPromptNumber > 1)', () => {
|
||||
it('should pass resume parameter when lastPromptNumber > 1 AND memorySessionId exists', () => {
|
||||
// Scenario: Normal continuation within same SDK session
|
||||
const session = {
|
||||
memorySessionId: 'valid-session-id',
|
||||
lastPromptNumber: 2, // CONTINUATION prompt
|
||||
};
|
||||
|
||||
const hasRealMemorySessionId = !!session.memorySessionId;
|
||||
const shouldResume = shouldPassResumeParameter(session);
|
||||
|
||||
expect(hasRealMemorySessionId).toBe(true);
|
||||
expect(shouldResume).toBe(true);
|
||||
});
|
||||
|
||||
it('should pass resume parameter for higher prompt numbers', () => {
|
||||
// Scenario: Later in a multi-turn conversation
|
||||
const session = {
|
||||
memorySessionId: 'valid-session-id',
|
||||
lastPromptNumber: 5, // 5th prompt in session
|
||||
};
|
||||
|
||||
const shouldResume = shouldPassResumeParameter(session);
|
||||
expect(shouldResume).toBe(true);
|
||||
});
|
||||
|
||||
it('should NOT pass resume parameter when memorySessionId is null even for lastPromptNumber > 1', () => {
|
||||
// Scenario: Bug case - somehow got to prompt 2 without capturing memorySessionId
|
||||
// This shouldn't happen in practice but we should handle it safely
|
||||
const session = {
|
||||
memorySessionId: null,
|
||||
lastPromptNumber: 2,
|
||||
};
|
||||
|
||||
const hasRealMemorySessionId = !!session.memorySessionId;
|
||||
const shouldResume = shouldPassResumeParameter(session);
|
||||
|
||||
expect(hasRealMemorySessionId).toBe(false);
|
||||
expect(shouldResume).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edge cases', () => {
|
||||
it('should handle empty string memorySessionId as falsy', () => {
|
||||
// Empty string should be treated as "no session ID"
|
||||
const session = {
|
||||
memorySessionId: '' as unknown as null,
|
||||
lastPromptNumber: 2,
|
||||
};
|
||||
|
||||
const hasRealMemorySessionId = !!session.memorySessionId;
|
||||
const shouldResume = shouldPassResumeParameter(session);
|
||||
|
||||
expect(hasRealMemorySessionId).toBe(false);
|
||||
expect(shouldResume).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle undefined memorySessionId as falsy', () => {
|
||||
const session = {
|
||||
memorySessionId: undefined as unknown as null,
|
||||
lastPromptNumber: 2,
|
||||
};
|
||||
|
||||
const hasRealMemorySessionId = !!session.memorySessionId;
|
||||
const shouldResume = shouldPassResumeParameter(session);
|
||||
|
||||
expect(hasRealMemorySessionId).toBe(false);
|
||||
expect(shouldResume).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Bug reproduction: stale session resume crash', () => {
|
||||
it('should NOT resume when worker restarts with stale memorySessionId', () => {
|
||||
// This is the exact bug scenario from the logs:
|
||||
// [17:30:21.773] Starting SDK query {
|
||||
// hasRealMemorySessionId=true,
|
||||
// resume_parameter=5439891b-...,
|
||||
// lastPromptNumber=1 ← NEW SDK session!
|
||||
// }
|
||||
// [17:30:24.450] Generator failed {error=Claude Code process exited with code 1}
|
||||
|
||||
const session = {
|
||||
memorySessionId: '5439891b-7d4b-4ee3-8662-c000f66bc199', // Stale from previous session
|
||||
lastPromptNumber: 1, // But this is a NEW session after restart
|
||||
};
|
||||
|
||||
const shouldResume = shouldPassResumeParameter(session);
|
||||
|
||||
// The fix: should NOT try to resume, should start fresh
|
||||
expect(shouldResume).toBe(false);
|
||||
});
|
||||
|
||||
it('should resume correctly for normal continuation (not after restart)', () => {
|
||||
// Normal case: same SDK session, continuing conversation
|
||||
const session = {
|
||||
memorySessionId: '5439891b-7d4b-4ee3-8662-c000f66bc199',
|
||||
lastPromptNumber: 2, // Second prompt in SAME session
|
||||
};
|
||||
|
||||
const shouldResume = shouldPassResumeParameter(session);
|
||||
|
||||
// Should resume - same session, valid memorySessionId
|
||||
expect(shouldResume).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user