mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 02:29:33 +01:00
fix(sessions): retry a failed keep-alive write before the expiry and close its window
Merge-time fix for #666. A nudge only goes out inside the last five minutes before `expiresAt`, so the check armed a whole `KEEP_WARM_LEAD_MS` after a failed write landed at or past the expiry and stopped with "too close to expiry": the retry the status promised never happened. The window `beginKeepAliveTurn()` opened also stayed open for its ten-minute ceiling with no turn running. A failed write now ends the window (`endKeepAliveTurn()` joins `KeepWarmSessionView`; `Session` already has it) and re-arms at `min(now + KEEP_WARM_RETRY_MS, expiresAt - KEEP_WARM_LAST_CHANCE_MS)`. The test advances the clock and sees the second write, and a second one pins that the retry never lands past the last-chance line. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
83d80578e4
commit
e408fa21bc
5 files changed
+52
-6
No files matched your search
@@ -337,6 +337,10 @@ class FakeSession implements KeepWarmSessionView {
|
||||
beginKeepAliveTurn(windowMs: number): void {
|
||||
this.keepAliveWindows.push(windowMs);
|
||||
}
|
||||
keepAliveWindowsEnded = 0;
|
||||
endKeepAliveTurn(): void {
|
||||
this.keepAliveWindowsEnded += 1;
|
||||
}
|
||||
async writeViaMux(data: string): Promise<boolean> {
|
||||
this.writes.push(data);
|
||||
return this.writeOk;
|
||||
@@ -474,7 +478,12 @@ describe('PromptCacheKeepWarmService', () => {
|
||||
service.stop();
|
||||
});
|
||||
|
||||
it('keeps a failed write out of the tally and retries after the lead time', async () => {
|
||||
/**
|
||||
* A nudge only goes out inside the last five minutes before the expiry, so a retry armed a
|
||||
* whole lead time later would land past it and stop: the failed write ends the keep-alive
|
||||
* window (no turn is running) and retries a minute later, never past the last-chance line.
|
||||
*/
|
||||
it('keeps a failed write out of the tally, ends the keep-alive window and retries a minute later', async () => {
|
||||
const session = new FakeSession();
|
||||
session.keepWarmEnabled = true;
|
||||
session.writeOk = false;
|
||||
@@ -484,11 +493,38 @@ describe('PromptCacheKeepWarmService', () => {
|
||||
service.schedule(session.id);
|
||||
await settle();
|
||||
expect(session.writes).toHaveLength(1);
|
||||
expect(session.keepAliveWindowsEnded).toBe(1);
|
||||
expect(last()).toMatchObject({
|
||||
state: 'waiting',
|
||||
nudges: 0,
|
||||
nextCheckAt: NOW + KEEP_WARM_RETRY_MS,
|
||||
reason: expect.stringContaining('could not be written'),
|
||||
});
|
||||
|
||||
session.writeOk = true;
|
||||
await vi.advanceTimersByTimeAsync(KEEP_WARM_RETRY_MS);
|
||||
await settle();
|
||||
expect(session.writes).toEqual([`${KEEP_WARM_NUDGE}\r`, `${KEEP_WARM_NUDGE}\r`]);
|
||||
expect(session.keepAliveWindowsEnded).toBe(1);
|
||||
expect(last()).toMatchObject({ state: 'nudged', nudges: 1 });
|
||||
service.stop();
|
||||
});
|
||||
|
||||
it('never arms the retry after a failed write past the last-chance line', async () => {
|
||||
const session = new FakeSession();
|
||||
session.keepWarmEnabled = true;
|
||||
session.writeOk = false;
|
||||
// Due, with only 30 s left before the last-chance line.
|
||||
session.promptCache = warmCache(NOW + KEEP_WARM_LAST_CHANCE_MS + 30_000);
|
||||
const { ports, last } = makePorts(session);
|
||||
const service = new PromptCacheKeepWarmService(ports);
|
||||
await service.schedule(session.id);
|
||||
expect(last()).toMatchObject({ state: 'waiting', nextCheckAt: NOW + 30_000 });
|
||||
await vi.advanceTimersByTimeAsync(30_000);
|
||||
await settle();
|
||||
expect(session.writes).toHaveLength(1);
|
||||
expect(last()).toMatchObject({ state: 'stopped', reason: 'too close to expiry to land a turn' });
|
||||
expect(vi.getTimerCount()).toBe(0);
|
||||
service.stop();
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user