From 1b8677734b90f2c58ffa273384c0585077b23723 Mon Sep 17 00:00:00 2001 From: Christoph Pfoertner Date: Sat, 3 Oct 2026 21:33:05 +0200 Subject: [PATCH] fix(agents): recognize active OpenClaw coder sessions dynamically from rollouts --- server.py | 124 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 76 insertions(+), 48 deletions(-) diff --git a/server.py b/server.py index ec3ccf5..74731a1 100644 --- a/server.py +++ b/server.py @@ -213,65 +213,88 @@ class RealtimeRPGHandler(http.server.SimpleHTTPRequestHandler): status["gitea"]["connected"] = False status["gitea"]["error"] = str(e) - # 3. Check OpenClaw Lead Session Activity STRICTLY for target_project + # 3. Check OpenClaw Active Session Activity for target_project (Lead, Coder, Reviewer) lead_heartbeat_active = False lead_task_desc = "" lead_last_pulse_seconds = 999 lead_recent_logs = [] + coder_session_active = False + coder_task_desc = "" + coder_recent_logs = [] + try: - rollout_files = sorted( - glob.glob(f'/home/christoph/openclaw/data/openclaw/agents/{target_project}-lead/agent/codex-home/sessions/**/rollout-*.jsonl', recursive=True), - key=os.path.getmtime, - reverse=True - ) - if rollout_files: - newest_rollout = rollout_files[0] - lead_last_pulse_seconds = round(time.time() - os.path.getmtime(newest_rollout), 1) + search_patterns = [ + f'/home/christoph/openclaw/data/openclaw/agents/{target_project}-coder/agent/codex-home/sessions/**/rollout-*.jsonl', + f'/home/christoph/openclaw/data/openclaw/agents/{target_project}-lead/agent/codex-home/sessions/**/rollout-*.jsonl', + f'/home/christoph/openclaw/data/openclaw/agents/projekt-lead/agent/codex-home/sessions/**/rollout-*.jsonl' + ] + all_rollout_files = [] + for p in search_patterns: + all_rollout_files.extend(glob.glob(p, recursive=True)) - with open(newest_rollout, 'r') as fp: + rollout_files = sorted(all_rollout_files, key=os.path.getmtime, reverse=True) + + for rf in rollout_files[:10]: + age_seconds = round(time.time() - os.path.getmtime(rf), 1) + with open(rf, 'r') as fp: lines = fp.readlines() - if lines: - last_line_data = json.loads(lines[-1]) - last_payload_type = last_line_data.get('payload', {}).get('type') - is_completed = (last_payload_type in ['task_complete', 'turn_complete', 'stop']) - - # Extract recent live logs - for l in lines[-8:]: - try: - item = json.loads(l) - p_type = item.get('type') - p_payload = item.get('payload', {}) - if p_payload.get('type') == 'message' and p_payload.get('role') == 'assistant': - t = p_payload.get('content', [{}])[0].get('text', '') - if t: lead_recent_logs.append(f"🤖 {t[:90]}...") - elif p_payload.get('type') == 'item_completed': - i_name = p_payload.get('item', {}).get('type') - if i_name: lead_recent_logs.append(f"⚙️ Step: {i_name}") - except: - pass + if not lines: + continue + last_line_data = json.loads(lines[-1]) + last_payload_type = last_line_data.get('payload', {}).get('type') + is_completed = (last_payload_type in ['task_complete', 'turn_complete', 'stop']) + + # Extract logs + recent_logs = [] + session_prompt = "" + session_role = "lead" - if not is_completed and lead_last_pulse_seconds < 45: + for l in lines[-8:]: + try: + item = json.loads(l) + p_payload = item.get('payload', {}) + if p_payload.get('type') == 'message' and p_payload.get('role') == 'assistant': + t = p_payload.get('content', [{}])[0].get('text', '') + if t: recent_logs.append(f"🤖 {t[:90]}...") + elif p_payload.get('type') == 'item_completed': + i_name = p_payload.get('item', {}).get('type') + if i_name: recent_logs.append(f"⚙️ Step: {i_name}") + except: + pass + + for l in lines: + try: + p = json.loads(l) + if p.get('payload', {}).get('type') == 'message' and p.get('payload', {}).get('role') == 'user': + txt = p.get('payload', {}).get('content', [{}])[0].get('text', '') + if not txt.startswith(""): + session_prompt = txt + if "coder für issue" in txt.lower() or "du bist der coder" in txt.lower() or "openclaw-coder" in txt.lower(): + session_role = "coder" + elif "review" in txt.lower() and ("du bist der" in txt.lower() or "qa" in txt.lower()): + session_role = "reviewer" + except: + pass + + # Map to specific agent + if session_role == "coder" and not coder_session_active: + if not is_completed and age_seconds < 60: + coder_session_active = True + coder_task_desc = session_prompt[:120] + coder_recent_logs = recent_logs + elif session_role == "lead" and not lead_heartbeat_active: + lead_last_pulse_seconds = age_seconds + if not is_completed and age_seconds < 60: lead_heartbeat_active = True - for l in reversed(lines[-10:]): - p = json.loads(l) - if p.get('payload', {}).get('type') == 'message' and p.get('payload', {}).get('role') == 'user': - lead_task_desc = p.get('payload', {}).get('content', [{}])[0].get('text', '')[:120] - break - else: - lead_heartbeat_active = False + lead_task_desc = session_prompt[:120] + lead_recent_logs = recent_logs - status["lead_heartbeat"] = { - "last_pulse_seconds_ago": lead_last_pulse_seconds, - "project": target_project, - "is_beating": lead_heartbeat_active - } - else: - status["lead_heartbeat"] = { - "last_pulse_seconds_ago": 999, - "project": target_project, - "is_beating": False - } + status["lead_heartbeat"] = { + "last_pulse_seconds_ago": lead_last_pulse_seconds, + "project": target_project, + "is_beating": lead_heartbeat_active + } except Exception as e: status["lead_heartbeat"] = {"error": str(e), "is_beating": False, "last_pulse_seconds_ago": 999} @@ -283,6 +306,11 @@ class RealtimeRPGHandler(http.server.SimpleHTTPRequestHandler): status["agents"]["coder1"]["currentTask"] = f"Task #{task.get('issueNumber', '')}: {task.get('title', 'Coding')}" status["agents"]["coder1"]["lastAction"] = "BullMQ: Active Code Session" status["agents"]["coder1"]["liveLog"] = ["git checkout feature-branch", "npm run test", "git commit -m 'feat: implementation'"] + elif coder_session_active: + status["agents"]["coder1"]["state"] = "WORKING" + status["agents"]["coder1"]["currentTask"] = f"Aktiv: {coder_task_desc or 'Coding Session'}" + status["agents"]["coder1"]["lastAction"] = "OpenClaw Coder Turn Active" + status["agents"]["coder1"]["liveLog"] = coder_recent_logs or ["Implementing feature / bugfix..."] else: status["agents"]["coder1"]["state"] = "IDLE" status["agents"]["coder1"]["currentTask"] = f"Keine offenen Tasks in {target_project}"