94 lines
3.1 KiB
Python
94 lines
3.1 KiB
Python
"""Auto-close stale sessions older than 24 hours."""
|
|
import logging
|
|
from datetime import datetime, timezone, timedelta
|
|
from bigmind.db import db
|
|
|
|
logger = logging.getLogger("BigMindAutoClose")
|
|
|
|
STALE_THRESHOLD_HOURS = 24
|
|
|
|
|
|
def auto_close_stale_sessions(user_id: str) -> int:
|
|
"""
|
|
Close any open sessions for this user that are older than 24 hours.
|
|
Returns the number of sessions auto-closed.
|
|
"""
|
|
cutoff = (
|
|
datetime.now(timezone.utc) - timedelta(hours=STALE_THRESHOLD_HOURS)
|
|
).isoformat()
|
|
|
|
with db() as conn:
|
|
stale = conn.execute(
|
|
"""SELECT id, started_at FROM sessions
|
|
WHERE user_id=? AND ended_at IS NULL AND started_at < ?""",
|
|
(user_id, cutoff),
|
|
).fetchall()
|
|
|
|
for session in stale:
|
|
conn.execute(
|
|
"""UPDATE sessions
|
|
SET ended_at=CURRENT_TIMESTAMP,
|
|
one_liner='[auto-closed — session exceeded 24h]',
|
|
outcome='Session automatically closed after exceeding 24h without a proper close call.'
|
|
WHERE id=?""",
|
|
(session["id"],),
|
|
)
|
|
logger.info(
|
|
"Auto-closed stale session %s (started %s)",
|
|
session["id"],
|
|
session["started_at"],
|
|
)
|
|
|
|
return len(stale)
|
|
|
|
|
|
def close_orphaned_sessions(user_id: str, keep_session_id: str) -> list[str]:
|
|
"""
|
|
Close all open sessions for this user EXCEPT the specified keep_session_id.
|
|
Returns the list of session IDs that were closed.
|
|
|
|
Use this to clean up orphaned sessions from crashed IDEs, dead VS Code
|
|
windows, or any parallel session that was never properly closed.
|
|
"""
|
|
with db() as conn:
|
|
orphans = conn.execute(
|
|
"""SELECT id, started_at FROM sessions
|
|
WHERE user_id=? AND ended_at IS NULL AND id != ?""",
|
|
(user_id, keep_session_id),
|
|
).fetchall()
|
|
|
|
closed_ids = []
|
|
for session in orphans:
|
|
conn.execute(
|
|
"""UPDATE sessions
|
|
SET ended_at=CURRENT_TIMESTAMP,
|
|
one_liner='[orphaned — closed by memory_close_stale_sessions]',
|
|
outcome='Session was open but never properly closed (IDE crash or forgotten). Cleaned up manually.'
|
|
WHERE id=?""",
|
|
(session["id"],),
|
|
)
|
|
closed_ids.append(session["id"])
|
|
logger.info(
|
|
"Closed orphaned session %s (started %s)",
|
|
session["id"],
|
|
session["started_at"],
|
|
)
|
|
|
|
return closed_ids
|
|
|
|
|
|
def restart_server_in_place() -> None:
|
|
"""
|
|
Replace the current process image with a fresh copy via os.execv.
|
|
|
|
Called from a background thread so the MCP response is delivered first.
|
|
Inherits stdin/stdout file descriptors so the IDE stdio connection survives.
|
|
"""
|
|
import os
|
|
import sys
|
|
import time
|
|
time.sleep(0.5)
|
|
logger.info("🔄 os.execv — replacing process image with fresh copy")
|
|
os.execv(sys.executable, [sys.executable] + sys.argv)
|
|
|