diff --git a/index.html b/index.html
index 5226c95..f438230 100644
--- a/index.html
+++ b/index.html
@@ -722,6 +722,7 @@
agents[key].state = agentData.state;
agents[key].currentTask = agentData.currentTask;
agents[key].lastAction = agentData.lastAction;
+ agents[key].parallelCount = agentData.parallelCount || 0;
if (agentData.liveLog && agentData.liveLog.length > 0) {
agents[key].liveLog = agentData.liveLog;
}
@@ -1416,15 +1417,23 @@
ctx.fillText("💾 DELEGATE", x - 6, y + 3 + bounce);
} else if (agent.state === 'WORKING') {
+ const count = agent.parallelCount || 1;
+ const bubbleW = (count > 1) ? 54 : 40;
ctx.fillStyle = "#064e3b";
- ctx.fillRect(x - 10, y - 10, 40, 20);
- ctx.strokeStyle = "#34d399";
+ ctx.fillRect(x - 10, y - 10, bubbleW, 20);
+ ctx.strokeStyle = (count > 1) ? "#facc15" : "#34d399";
ctx.lineWidth = 1.5;
- ctx.strokeRect(x - 10, y - 10, 40, 20);
+ ctx.strokeRect(x - 10, y - 10, bubbleW, 20);
ctx.font = "10px 'Press Start 2P'";
ctx.fillStyle = "#34d399";
ctx.fillText(">", x - 4, y + 4);
+ if (count > 1) {
+ ctx.font = "10px 'Press Start 2P'";
+ ctx.fillStyle = "#facc15";
+ ctx.fillText(`x${count}`, x + 24, y + 4);
+ }
+
} else if (agent.state === 'THINKING') {
const bob = Math.sin(time * 0.01) * 2;
ctx.fillStyle = "#713f12";
diff --git a/server.py b/server.py
index 46ec1d1..768e59f 100644
--- a/server.py
+++ b/server.py
@@ -232,9 +232,8 @@ class RealtimeRPGHandler(http.server.SimpleHTTPRequestHandler):
lead_last_pulse_seconds = 999
lead_recent_logs = []
- coder_session_active = False
- coder_task_desc = ""
- coder_recent_logs = []
+ coder_sessions = []
+ active_coder_count = 0
try:
search_patterns = [
@@ -248,7 +247,7 @@ class RealtimeRPGHandler(http.server.SimpleHTTPRequestHandler):
rollout_files = sorted(all_rollout_files, key=os.path.getmtime, reverse=True)
- for rf in rollout_files[:10]:
+ for rf in rollout_files[:15]:
age_seconds = round(time.time() - os.path.getmtime(rf), 1)
with open(rf, 'r') as fp:
lines = fp.readlines()
@@ -300,11 +299,14 @@ class RealtimeRPGHandler(http.server.SimpleHTTPRequestHandler):
session_prompt = first_prompt or "Heartbeat / Backlog Monitoring"
# Map to specific agent
- if session_role == "coder" and not coder_session_active:
+ if session_role == "coder":
if not is_completed and age_seconds < 60:
- coder_session_active = True
- coder_task_desc = session_prompt[:120]
- coder_recent_logs = recent_logs
+ active_coder_count += 1
+ coder_sessions.append({
+ "task": session_prompt[:120],
+ "logs": recent_logs,
+ "age": age_seconds
+ })
elif session_role == "lead" and not lead_heartbeat_active:
lead_last_pulse_seconds = age_seconds
if not is_completed and age_seconds < 60:
@@ -321,23 +323,27 @@ class RealtimeRPGHandler(http.server.SimpleHTTPRequestHandler):
status["lead_heartbeat"] = {"error": str(e), "is_beating": False, "last_pulse_seconds_ago": 999}
# 4. Derive Agent RPG States STRICTLY for target_project
- # Coder 1
+ # Coder 1 (supports multi-instance count x2, x3)
if "coder-tasks" in latest_active_tasks:
task = latest_active_tasks["coder-tasks"]
status["agents"]["coder1"]["state"] = "WORKING"
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"]["parallelCount"] = max(1, active_coder_count)
+ elif active_coder_count > 0:
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..."]
+ primary_coder = coder_sessions[0]
+ status["agents"]["coder1"]["currentTask"] = f"Aktiv ({active_coder_count}x): {primary_coder['task'] or 'Coding Session'}"
+ status["agents"]["coder1"]["lastAction"] = f"OpenClaw Coder Turn ({active_coder_count} parallel)"
+ status["agents"]["coder1"]["liveLog"] = primary_coder["logs"] or ["Implementing feature / bugfix..."]
+ status["agents"]["coder1"]["parallelCount"] = active_coder_count
else:
status["agents"]["coder1"]["state"] = "IDLE"
status["agents"]["coder1"]["currentTask"] = f"Keine offenen Tasks in {target_project}"
status["agents"]["coder1"]["lastAction"] = "Idle / Bereit"
status["agents"]["coder1"]["liveLog"] = ["Wartet auf neue Queue-Aufgaben..."]
+ status["agents"]["coder1"]["parallelCount"] = 0
# Coder 2 (Tester / Staging)
if "tester-tasks" in latest_active_tasks or "deployments" in latest_active_tasks: