fix: orphan session cleanup + instant reactivation via ghost watch
plugin/server/store.go:
- CleanStaleSessions now handles last_update_ms=0 (pre-cleanup era orphans)
- Zero-timestamp sessions: mark active ones interrupted, delete non-active ones
- Previously these were silently skipped with 'continue', accumulating forever
src/status-watcher.js:
- removeSession() keeps fileToSession mapping as ghost entry ('\x00ghost:key')
- When ghost file changes, emits 'session-reactivate' immediately instead of
waiting up to 2s for the session-monitor poll cycle
- Ghost removed after first trigger to avoid repeated events
src/session-monitor.js:
- Added pollNow() for immediate poll without waiting for interval tick
- Reactivation check now uses sessions.json updatedAt vs completedAt timestamp
(pure infrastructure: two on-disk timestamps, no AI involvement)
src/watcher-manager.js:
- Wires session-reactivate event: clearCompleted() + pollNow() for instant re-detection
- New sessions now show up within ~100ms of first file change instead of 2s
Net result: status box appears reliably on every turn, clears 3s after reply,
zero orphan sessions accumulating in the KV store.
This commit is contained in:
@@ -146,6 +146,18 @@ func (s *Store) CleanStaleSessions(staleThresholdMs, expireThresholdMs int64) (c
|
||||
lastUpdate = session.StartTimeMs
|
||||
}
|
||||
if lastUpdate == 0 {
|
||||
// No timestamps at all (pre-cleanup era orphan) — treat as expired immediately.
|
||||
// Delete non-active sessions; mark active ones as interrupted so they get
|
||||
// picked up on the next cleanup cycle with a real timestamp.
|
||||
if session.Status == "active" {
|
||||
session.Status = "interrupted"
|
||||
session.LastUpdateMs = now
|
||||
_ = s.SaveSession(session.SessionKey, session)
|
||||
cleaned++
|
||||
} else {
|
||||
_ = s.DeleteSession(session.SessionKey)
|
||||
expired++
|
||||
}
|
||||
continue
|
||||
}
|
||||
age := now - lastUpdate
|
||||
|
||||
Reference in New Issue
Block a user