fix(frontend): the running-elsewhere strip stops accusing your own tab - #5857
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe change records local session settlement timestamps, derives remote-run state from local status and refreshed backend liveness, refreshes liveness after turn completion, and uses the derived state during session hydration. ChangesSession liveness lifecycle
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant useAgentChatSession
participant QueryClient
participant sessionRunningElsewhereAtomFamily
participant useSessionHydration
useAgentChatSession->>QueryClient: Invalidate session-liveness after turn completion
QueryClient->>sessionRunningElsewhereAtomFamily: Refresh liveness timestamp
sessionRunningElsewhereAtomFamily->>useSessionHydration: Provide remote-run state
useSessionHydration->>useSessionHydration: Apply remote-run state with local busy guard
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Railway Preview Environment
Updated at 2026-08-10T14:57:03.503Z |
765a717 to
625f356
Compare
Issue #5844. runningElsewhere mixed an instant local signal with the cached 15s liveness poll, so the strip appeared after a local answer finished. - Invalidate session liveness when a turn finishes. - Treat running and awaiting as active local ownership. An error is settled: stale liveness stays hidden, while a newer true result can reveal a run from another tab. - Stamp active-to-idle and active-to-error transitions, and only trust liveness data newer than that settlement. This also avoids local transcript catch-up polling after a turn. 163 AgentChatSlice tests pass; TypeScript, ESLint, and Prettier are clean.
625f356 to
4b0eb5c
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro Plus
Run ID: 8125c6f7-72b5-459e-9e3d-2eeb88a7d84c
📒 Files selected for processing (4)
web/oss/src/components/AgentChatSlice/state/liveness.test.tsweb/oss/src/components/AgentChatSlice/state/liveness.tsweb/oss/src/components/AgentChatSlice/state/sessions.runStatus.test.tsweb/oss/src/components/AgentChatSlice/state/sessions.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- web/oss/src/components/AgentChatSlice/state/liveness.test.ts
- web/oss/src/components/AgentChatSlice/state/liveness.ts
| it("stamps an active run when it errors without restamping on cleanup", () => { | ||
| const store = createStore() | ||
| const id = `run-status-parked-${Date.now()}` | ||
|
|
||
| store.set(setSessionStatusAtom, {id, status: "awaiting"}) | ||
| store.set(setSessionStatusAtom, {id, status: "error"}) | ||
| const settledAt = store.get(sessionLocalSettledAtAtomFamily(id)) | ||
| expect(settledAt).toBeGreaterThan(0) | ||
|
|
||
| store.set(setSessionStatusAtom, {id, status: "idle"}) | ||
| expect(store.get(sessionLocalSettledAtAtomFamily(id))).toBe(settledAt) | ||
| }) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate and inspect the target test and related run-status implementation without executing repository code.
target="web/oss/src/components/AgentChatSlice/state/sessions.runStatus.test.ts"
if [ -f "$target" ]; then
echo "== target target =="
wc -l "$target"
echo "== target contents =="
cat -n "$target"
else
echo "Target not found; locating similar files..."
fd -i 'sessions.*runStatus.*test.*|runStatus.*test' .
fi
echo "== related implementation files =="
fd -i 'sessions.*ts|runStatus.*ts' web/oss/src/components/AgentChatSlice/state 2>/dev/null || true
echo "== Date.now usages in related state files =="
rg -n "Date\.now|setSessionStatusAtom|sessionLocalSettledAtAtomFamily|settlement|settled" web/oss/src/components/AgentChatSlice/state || trueRepository: Agenta-AI/agenta
Length of output: 9065
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== sessions.ts relevant atom definitions =="
sed -n '860,930p' web/oss/src/components/AgentChatSlice/state/sessions.ts | cat -n
echo "== package scripts/vitest availability =="
fd -a package.json . | sed 's#^\./##' | head -20
if [ -f web/package.json ]; then
node - <<'JS'
const fs = require('fs')
const pkg = JSON.parse(fs.readFileSync('web/package.json', 'utf8'))
console.log(JSON.stringify(pkg.scripts || {}, null, 2))
console.log('vitest' in (pkg.dependencies || {}))
console.log('`@vitest`' in (Object.assign({}, pkg.devDependencies, pkg.dependencies || {})))
JS
fi
rg -n '"vitest"|"test:vite"|describe|testEnvironment' web/package.json web/vitest.config* web/package-lock.json 2>/dev/null || trueRepository: Agenta-AI/agenta
Length of output: 7523
Make the cleanup assertion distinguish the timestamp values.
setSessionStatusAtom can stamp the same Date.now() value for both the error settlement and the later idle cleanup path. Assert settledAt against the mocked error time, then keep the cleanup value equal to that same value rather than only comparing the two timestamps.
Fixes #5844.
Root cause
runningElsewhere = nest.isRunning && !busymixed an instant local signal with a stale one:busydrops when the stream closes, whilenest.isRunningcomes from the project-wide liveness poll (staleTime 10s / refetch 15s). Nothing refreshed it at turn end, so for up to about 15 seconds after every answer, the tab that ran the turn accused itself of running elsewhere.The fix
onFinish. The runner clears its heartbeat before ending the response stream, so the immediate refetch sees the settled server state.runningandawaitingas active ownership by this tab.erroris correctly treated as settled, not active, so a newer liveness result can still reveal a subsequent run from another tab.This also avoids unnecessary transcript catch-up polling after local turns and subscribes the dock to the derived boolean instead of every liveness poll result.
Verification
tsc --noEmitpasses.Live check: create an agent and send a message. The strip must not appear in the same tab after success, while awaiting approval, or from stale liveness after an error. A fresh run from a second tab must still show the strip.