fix(core): derive activity from runtime probe, not terminal status (#1081)

Previously, sessions with terminal statuses (merged, done, killed, etc.)
had their activity unconditionally set to "exited" without checking if
the agent process was actually still running. This caused the dashboard
to show incorrect activity state for sessions where the PR was merged
but the agent was still alive in tmux.

Now the runtime liveness check runs first for all sessions, and activity
is derived from the actual process state via getActivityState(). Terminal
status no longer forces activity to "exited".

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
prod-blip 2026-04-10 16:20:26 +05:30
parent ff9bb76e63
commit b88b664dbb
1 changed files with 11 additions and 9 deletions

View File

@ -880,13 +880,9 @@ export function createSessionManager(deps: SessionManagerDeps): OpenCodeSessionM
plugins: ReturnType<typeof resolvePlugins>,
handleFromMetadata: boolean,
): Promise<void> {
// Skip all subprocess/IO work for sessions already known to be terminal.
if (TERMINAL_SESSION_STATUSES.has(session.status)) {
session.activity = "exited";
return;
}
// Check runtime liveness — but only if the handle came from metadata.
// Check runtime liveness first — regardless of session status.
// This fixes #1081: terminal statuses (merged, done, etc.) should not force
// activity to "exited" if the agent process is still alive.
// Fabricated handles (constructed as fallback for external sessions) should
// NOT override status to "killed" — we don't know if the session ever had
// a tmux session, and we'd clobber meaningful statuses like "pr_open".
@ -894,7 +890,11 @@ export function createSessionManager(deps: SessionManagerDeps): OpenCodeSessionM
try {
const alive = await plugins.runtime.isAlive(session.runtimeHandle);
if (!alive) {
session.status = "killed";
// Process is confirmed dead — set activity to exited.
// Only update status to "killed" if not already in a terminal state.
if (!TERMINAL_SESSION_STATUSES.has(session.status)) {
session.status = "killed";
}
session.activity = "exited";
return;
}
@ -903,10 +903,12 @@ export function createSessionManager(deps: SessionManagerDeps): OpenCodeSessionM
}
}
// Detect activity independently of runtime handle.
// Detect activity independently of runtime handle and session status.
// Activity detection reads JSONL files on disk — it only needs workspacePath,
// not a runtime handle. Gating on runtimeHandle caused sessions created by
// external scripts (which don't store runtimeHandle) to always show "unknown".
// This now runs for ALL sessions, including terminal statuses, so a merged
// session with a live agent shows accurate activity (ready/idle/waiting_input).
if (plugins.agent) {
try {
const detected = await plugins.agent.getActivityState(session, config.readyThresholdMs);