Skip to content

Commit a9e1bdd

Browse files
committed
fix(bot): add TTL to ForegroundSessionState to prevent stuck busy state (#115)
1 parent caf3240 commit a9e1bdd

1 file changed

Lines changed: 33 additions & 11 deletions

File tree

src/scheduled-task/foreground-state.ts

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
import { logger } from "../utils/logger.js";
22

3+
const DEFAULT_BUSY_TTL_MS = 30 * 60 * 1000; // 30 minutes
4+
35
class ForegroundSessionState {
4-
private activeSessionIds = new Set<string>();
6+
// sessionId → expiration timestamp (Date.now() + ttlMs)
7+
private activeSessions = new Map<string, number>();
58

6-
markBusy(sessionId: string): void {
9+
markBusy(sessionId: string, ttlMs: number = DEFAULT_BUSY_TTL_MS): void {
710
if (!sessionId) {
811
return;
912
}
1013

11-
this.activeSessionIds.add(sessionId);
14+
this.activeSessions.set(sessionId, Date.now() + ttlMs);
1215
logger.debug(
13-
`[ScheduledTaskForeground] Marked session busy: session=${sessionId}, count=${this.activeSessionIds.size}`,
16+
`[ScheduledTaskForeground] Marked session busy: session=${sessionId}, count=${this.activeSessions.size}`,
1417
);
1518
}
1619

@@ -19,29 +22,48 @@ class ForegroundSessionState {
1922
return;
2023
}
2124

22-
this.activeSessionIds.delete(sessionId);
25+
this.activeSessions.delete(sessionId);
2326
logger.debug(
24-
`[ScheduledTaskForeground] Marked session idle: session=${sessionId}, count=${this.activeSessionIds.size}`,
27+
`[ScheduledTaskForeground] Marked session idle: session=${sessionId}, count=${this.activeSessions.size}`,
2528
);
2629
}
2730

31+
// Remove entries whose TTL has expired. Called automatically by isBusy().
32+
sweepExpired(): number {
33+
const now = Date.now();
34+
let removed = 0;
35+
for (const [sessionId, expiresAt] of this.activeSessions) {
36+
if (expiresAt <= now) {
37+
this.activeSessions.delete(sessionId);
38+
removed++;
39+
}
40+
}
41+
if (removed > 0) {
42+
logger.info(
43+
`[ScheduledTaskForeground] Swept ${removed} expired busy session(s), remaining=${this.activeSessions.size}`,
44+
);
45+
}
46+
return removed;
47+
}
48+
2849
isBusy(): boolean {
29-
return this.activeSessionIds.size > 0;
50+
this.sweepExpired();
51+
return this.activeSessions.size > 0;
3052
}
3153

3254
clearAll(reason: string): void {
33-
if (this.activeSessionIds.size === 0) {
55+
if (this.activeSessions.size === 0) {
3456
return;
3557
}
3658

3759
logger.info(
38-
`[ScheduledTaskForeground] Cleared foreground busy state: reason=${reason}, count=${this.activeSessionIds.size}`,
60+
`[ScheduledTaskForeground] Cleared foreground busy state: reason=${reason}, count=${this.activeSessions.size}`,
3961
);
40-
this.activeSessionIds.clear();
62+
this.activeSessions.clear();
4163
}
4264

4365
__resetForTests(): void {
44-
this.activeSessionIds.clear();
66+
this.activeSessions.clear();
4567
}
4668
}
4769

0 commit comments

Comments
 (0)