Files
pi_mcps/bigmind/bigmind/auto_close.py
T
2026-04-03 13:37:45 +02:00

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)