Fix: Proper session rotation detection (v1.2)
Fixes the bug where watcher stayed stuck on old sessions after /new or /reset. Changes: - Added file_score() function combining mtime + size for better detection - Added INACTIVITY_THRESHOLD (30s) - if no new data, check for active session - Tracks last_data_time and file size to detect stale sessions - Switches to newer session when current is inactive The previous v1.1 fix (mtime polling) was incomplete because new sessions can have older mtime than recently-written old sessions. Tested: Watcher now properly follows session rotation on /new and /reset
This commit is contained in:
@@ -1,9 +1,15 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
TrueRecall v1.1 - Real-time Qdrant Watcher
|
||||
TrueRecall v1.2 - Real-time Qdrant Watcher
|
||||
Monitors OpenClaw sessions and stores to memories_tr instantly.
|
||||
|
||||
This is the CAPTURE component. For curation and injection, install v2.
|
||||
|
||||
Changelog:
|
||||
- v1.2: Fixed session rotation bug - added inactivity detection (30s threshold)
|
||||
and improved file scoring to properly detect new sessions on /new or /reset
|
||||
- v1.1: Added 1-second mtime polling for session rotation
|
||||
- v1.0: Initial release
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -134,6 +140,11 @@ def store_to_qdrant(turn: Dict[str, Any], dry_run: bool = False) -> bool:
|
||||
|
||||
|
||||
def get_current_session_file():
|
||||
"""Find the most recently active session file.
|
||||
|
||||
Uses a combination of creation time and modification time to handle
|
||||
session rotation when /new or /reset is used.
|
||||
"""
|
||||
if not SESSIONS_DIR.exists():
|
||||
return None
|
||||
|
||||
@@ -141,7 +152,20 @@ def get_current_session_file():
|
||||
if not files:
|
||||
return None
|
||||
|
||||
return max(files, key=lambda p: p.stat().st_mtime)
|
||||
# Score files by: recency (mtime) + size activity
|
||||
# Files with very recent mtime AND non-zero size are likely active
|
||||
def file_score(p: Path) -> float:
|
||||
try:
|
||||
stat = p.stat()
|
||||
mtime = stat.st_mtime
|
||||
size = stat.st_size
|
||||
# Prefer files with recent mtime and non-zero size
|
||||
# Add small bonus for larger files (active sessions grow)
|
||||
return mtime + (size / 1e9) # size bonus is tiny vs mtime
|
||||
except Exception:
|
||||
return 0
|
||||
|
||||
return max(files, key=file_score)
|
||||
|
||||
|
||||
def parse_turn(line: str, session_name: str) -> Optional[Dict[str, Any]]:
|
||||
@@ -225,6 +249,10 @@ def watch_session(session_file: Path, dry_run: bool = False):
|
||||
last_position = 0
|
||||
|
||||
last_session_check = time.time()
|
||||
last_data_time = time.time() # Track when we last saw new data
|
||||
last_file_size = session_file.stat().st_size if session_file.exists() else 0
|
||||
|
||||
INACTIVITY_THRESHOLD = 30 # seconds - if no data for 30s, check for new session
|
||||
|
||||
with open(session_file, 'r') as f:
|
||||
while running:
|
||||
@@ -232,15 +260,45 @@ def watch_session(session_file: Path, dry_run: bool = False):
|
||||
print("Session file removed, looking for new session...")
|
||||
return None
|
||||
|
||||
current_time = time.time()
|
||||
|
||||
# Check for newer session every 1 second
|
||||
if time.time() - last_session_check > 1.0:
|
||||
last_session_check = time.time()
|
||||
if current_time - last_session_check > 1.0:
|
||||
last_session_check = current_time
|
||||
newest_session = get_current_session_file()
|
||||
if newest_session and newest_session != session_file:
|
||||
print(f"Newer session detected: {newest_session.name}")
|
||||
return newest_session
|
||||
|
||||
# Check if current file is stale (no new data for threshold)
|
||||
if current_time - last_data_time > INACTIVITY_THRESHOLD:
|
||||
try:
|
||||
current_size = session_file.stat().st_size
|
||||
# If file hasn't grown, check if another session is active
|
||||
if current_size == last_file_size:
|
||||
newest_session = get_current_session_file()
|
||||
if newest_session and newest_session != session_file:
|
||||
print(f"Current session inactive, switching to: {newest_session.name}")
|
||||
return newest_session
|
||||
else:
|
||||
# File grew, update tracking
|
||||
last_file_size = current_size
|
||||
last_data_time = current_time
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Process new lines and update activity tracking
|
||||
old_position = last_position
|
||||
process_new_lines(f, session_name, dry_run)
|
||||
|
||||
# If we processed new data, update activity timestamp
|
||||
if last_position > old_position:
|
||||
last_data_time = current_time
|
||||
try:
|
||||
last_file_size = session_file.stat().st_size
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
return session_file
|
||||
|
||||
Reference in New Issue
Block a user