81 lines
4.7 KiB
Python
81 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
import glob, sqlite3, json, os, sys, time
|
|
|
|
t0 = time.time()
|
|
recovered = 0
|
|
interrupted_turns = []
|
|
|
|
def extract_prompt(msg):
|
|
if not msg: return ""
|
|
content = msg.get("content")
|
|
if isinstance(content, str): return content
|
|
if isinstance(content, list):
|
|
parts = []
|
|
for c in content:
|
|
if isinstance(c, dict): parts.append(c.get("text") or c.get("content") or "")
|
|
elif isinstance(c, str): parts.append(c)
|
|
return "\n".join(filter(None, parts))
|
|
return str(content or "")
|
|
|
|
for db in sorted(glob.glob("/home/node/.openclaw/agents/*/agent/openclaw-agent.sqlite")):
|
|
try:
|
|
conn = sqlite3.connect(db, timeout=10.0)
|
|
cursor = conn.cursor()
|
|
while True:
|
|
cursor.execute("PRAGMA foreign_key_check;")
|
|
violations = cursor.fetchall()
|
|
if not violations: break
|
|
for table, rowid, parent, fkid in violations:
|
|
cursor.execute(f"DELETE FROM {table} WHERE rowid = ?", (rowid,))
|
|
cursor.execute("SELECT session_key, entry_json, current_session_id FROM session_nodes WHERE status = \"running\"")
|
|
rows = cursor.fetchall()
|
|
agent_id = os.path.basename(os.path.dirname(os.path.dirname(db)))
|
|
for sk, ej, csid in rows:
|
|
try:
|
|
p = json.loads(ej) if ej else {}
|
|
sid = csid or p.get("sessionId")
|
|
if sid:
|
|
ev = cursor.execute("SELECT seq, event_json FROM transcript_events WHERE session_id = ? ORDER BY seq DESC LIMIT 1", (sid,)).fetchone()
|
|
if ev:
|
|
ev_seq, ev_raw = ev
|
|
try:
|
|
ev_data = json.loads(ev_raw)
|
|
msg = ev_data.get("message") or ev_data
|
|
if msg.get("role") == "user":
|
|
prompt_text = extract_prompt(msg)
|
|
ctx = p.get("restartRecoveryDeliveryContext") or p.get("delivery", {}).get("context") or p.get("delivery", {}).get("route", {}).get("context") or {}
|
|
interrupted_turns.append({
|
|
"agent": agent_id,
|
|
"session_key": sk,
|
|
"session_id": sid,
|
|
"context": ctx,
|
|
"prompt": prompt_text
|
|
})
|
|
cursor.execute("PRAGMA foreign_keys = ON")
|
|
cursor.execute("DELETE FROM transcript_events WHERE session_id = ? AND seq = ?", (sid, ev_seq))
|
|
except Exception as ex:
|
|
print(f"[recover_sessions] Error parsing event: {ex}", file=sys.stderr)
|
|
p["status"] = "done"
|
|
p["activeWriterRunId"] = None
|
|
p["activeClaimRunId"] = None
|
|
p["restartRecoveryDeliveryRunId"] = None
|
|
cursor.execute("UPDATE session_nodes SET status = \"done\", entry_json = ? WHERE session_key = ?", (json.dumps(p), sk))
|
|
cursor.execute("UPDATE session_nodes SET entry_valid = 1 WHERE session_key = ?", (sk,))
|
|
recovered += 1
|
|
except Exception: pass
|
|
cursor.execute("UPDATE session_windows SET status = \"done\" WHERE status = \"running\"")
|
|
conn.commit()
|
|
conn.close()
|
|
except Exception as e:
|
|
print(f"[recover_sessions] Warning for {db}: {e}", file=sys.stderr)
|
|
|
|
if interrupted_turns:
|
|
try:
|
|
with open("/tmp/openclaw-interrupted-turns.json", "w") as f:
|
|
json.dump(interrupted_turns, f, indent=2)
|
|
print(f"[recover_sessions] Detected {len(interrupted_turns)} interrupted user turn(s) saved to /tmp/openclaw-interrupted-turns.json")
|
|
except Exception as e:
|
|
print(f"[recover_sessions] Error writing interrupted turns: {e}", file=sys.stderr)
|
|
|
|
print(f"[recover_sessions] Completed in {time.time() - t0:.2f}s ({recovered} wedged sessions recovered)")
|