fix: harden long conversation continuity
Memind CI / Test, build, and release guards (push) Successful in 1m28s

This commit is contained in:
john
2026-07-27 21:39:34 +08:00
parent 5ae166fc44
commit 48c61a3279
16 changed files with 1635 additions and 94 deletions
+47
View File
@@ -35,6 +35,53 @@ test('consumeSessionEventsUntilFinish resolves on Finish', async () => {
assert.equal(result.tokenState.totalTokens, 12);
});
test('consumeSessionEventsUntilFinish ignores a stale unscoped Finish before request activity', async () => {
const frames = [
'data: {"type":"Finish","token_state":{"totalTokens":0}}\n\n',
'data: {"type":"ActiveRequests","request_id":"req-fresh","request_ids":["req-fresh"]}\n\n',
'data: {"type":"Finish","request_id":"req-fresh","token_state":{"totalTokens":0}}\n\n',
'data: {"type":"Message","request_id":"req-fresh","message":{"role":"assistant"}}\n\n',
'data: {"type":"Finish","token_state":{"totalTokens":12}}\n\n',
];
const stream = new ReadableStream({
start(controller) {
for (const frame of frames) controller.enqueue(new TextEncoder().encode(frame));
controller.close();
},
});
const result = await consumeSessionEventsUntilFinish(stream, {
requestId: 'req-fresh',
timeoutMs: 5000,
requireRequestActivityBeforeUnscopedFinish: true,
});
assert.equal(result.tokenState.totalTokens, 12);
});
test('consumeSessionEventsUntilFinish fails fast when an empty Finish has no later activity', async () => {
const stream = new ReadableStream({
start(controller) {
controller.enqueue(new TextEncoder().encode(
'data: {"type":"Finish","request_id":"req-empty","token_state":{"totalTokens":0}}\n\n',
));
},
});
await assert.rejects(
consumeSessionEventsUntilFinish(stream, {
requestId: 'req-empty',
timeoutMs: 5000,
emptyFinishGraceMs: 5,
requireRequestActivityBeforeUnscopedFinish: true,
}),
(error) => {
assert.equal(error.code, 'SESSION_EMPTY_FINISH');
return true;
},
);
});
test('consumeSessionEventsUntilFinish records a successful raster generate_image result', async () => {
const toolResult = JSON.stringify({
ok: true,