Skip to content

Commit eef1b93

Browse files
committed
feat: Update SSE session management and agent status handling for improved connection accuracy
1 parent e07b276 commit eef1b93

3 files changed

Lines changed: 30 additions & 7 deletions

File tree

src/mcp_server.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,12 @@ def init_session_id() -> str:
5858

5959
# Live SSE session registry (in-memory only, resets on server restart).
6060
# Contains session_ids that currently have an active SSE TCP connection.
61-
# SSE agents in this set are provably online regardless of heartbeat timeout.
61+
# Sessions are refreshed on each tool call POST and during msg_wait heartbeats.
62+
# After the last refresh, sessions are considered stale after _SSE_STALE_SECONDS.
63+
# Kept deliberately short (60s) so that agents go offline quickly after msg_wait
64+
# ends or they stop making tool calls, rather than staying "live" for 5+ minutes.
6265
_active_sse_sessions: dict[str, float] = {}
63-
_SSE_STALE_SECONDS = max(120, MSG_WAIT_TIMEOUT + 15)
66+
_SSE_STALE_SECONDS = 60
6467
def get_session_id() -> str | None:
6568
"""Get session ID for this SSE connection."""
6669
return _session_id.get()

src/static/js/shared-agent-status.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,29 @@
33
* Determine agent state using SSE connection presence as the primary signal.
44
*
55
* States (priority order):
6-
* Listening — SSE connected + last_activity is msg_wait (green ⏳)
7-
* Working — SSE connected + NOT in msg_wait (amber ⚡)
8-
* Offline — no SSE + heartbeat expired (dark ⚫)
6+
* Listening — SSE connected + last_activity is msg_wait (⏳)
7+
* Working — SSE connected + NOT msg_wait + activity within 60s (⚡)
8+
* Idle — SSE connected + NOT msg_wait + activity older than 60s (🔌)
9+
* (SSE open but agent stopped responding — may be processing
10+
* a long task or is a stale connection)
11+
* Offline — no SSE + heartbeat expired (⚫)
912
*
1013
* For stdio agents (no SSE): fall back to heartbeat-based detection.
1114
* stdio agents will show Listening when is_online=true, Offline otherwise.
1215
*/
16+
const WORKING_TIMEOUT_S = 60;
17+
1318
function getAgentState(agent) {
1419
if (!agent) return "Offline";
1520

1621
if (agent.is_sse_connected) {
1722
if (agent.last_activity === "msg_wait") return "Listening";
23+
// Check how long ago the last activity was
24+
const lastActivityTime = agent.last_activity_time ? new Date(agent.last_activity_time) : null;
25+
if (lastActivityTime) {
26+
const elapsedS = (Date.now() - lastActivityTime.getTime()) / 1000;
27+
if (elapsedS > WORKING_TIMEOUT_S) return "Idle";
28+
}
1829
return "Working";
1930
}
2031

@@ -25,10 +36,10 @@
2536

2637
/**
2738
* Return the state emoji for display in the card.
28-
* ⏳ Listening, ⚡ Working, ⚫ Offline
39+
* ⏳ Listening, ⚡ Working, 🔌 Idle, ⚫ Offline
2940
*/
3041
function getStateEmoji(state) {
31-
const map = { Listening: "⏳", Working: "⚡", Offline: "⚫" };
42+
const map = { Listening: "⏳", Working: "⚡", Idle: "🔌", Offline: "⚫" };
3243
return map[state] || "⚫";
3344
}
3445

src/tools/dispatch.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,15 @@ async def _refresh_heartbeat() -> None:
816816
logger.debug(f"[msg_wait] heartbeat refreshed for agent_id={agent_id}")
817817
except Exception as e:
818818
logger.warning(f"[msg_wait] Failed to refresh heartbeat for {agent_id}: {e}")
819+
# Also refresh the in-process SSE session timestamp so is_agent_sse_connected()
820+
# stays true during active msg_wait polling. Without this, the short
821+
# _SSE_STALE_SECONDS window would expire mid-wait and flip the agent offline.
822+
try:
823+
session_id = src.mcp_server.get_session_id()
824+
if session_id:
825+
src.mcp_server.mark_sse_connected(session_id)
826+
except Exception:
827+
pass
819828

820829
if agent_id and token:
821830
try:

0 commit comments

Comments
 (0)