diff --git a/agent/dashboard/routes.py b/agent/dashboard/routes.py index 112cbdac..bef2c269 100644 --- a/agent/dashboard/routes.py +++ b/agent/dashboard/routes.py @@ -783,9 +783,15 @@ async def api_create_thread( @router.get("/threads/{thread_id}") async def api_get_thread( thread_id: str, + mark_viewed: bool = True, session: dict[str, Any] = _SESSION_DEP, ) -> dict[str, Any]: - return await get_dashboard_thread(thread_id, session["sub"], email=session.get("email")) + return await get_dashboard_thread( + thread_id, + session["sub"], + email=session.get("email"), + mark_viewed=mark_viewed, + ) @router.post("/threads/{thread_id}/messages") diff --git a/agent/dashboard/thread_api.py b/agent/dashboard/thread_api.py index ad3e4a73..43998264 100644 --- a/agent/dashboard/thread_api.py +++ b/agent/dashboard/thread_api.py @@ -420,7 +420,7 @@ async def _mark_thread_viewed( async def get_dashboard_thread( - thread_id: str, login: str, *, email: str | None = None + thread_id: str, login: str, *, email: str | None = None, mark_viewed: bool = True ) -> dict[str, Any]: client = langgraph_client() try: @@ -456,7 +456,7 @@ async def get_dashboard_thread( else None ), ) - if status != "running": + if mark_viewed and status != "running": metadata = await _mark_thread_viewed( client, thread_id, diff --git a/tests/test_dashboard_thread_api_activity.py b/tests/test_dashboard_thread_api_activity.py index 017ec0b7..7393d397 100644 --- a/tests/test_dashboard_thread_api_activity.py +++ b/tests/test_dashboard_thread_api_activity.py @@ -83,6 +83,25 @@ async def test_get_dashboard_thread_marks_finished_thread_viewed(monkeypatch) -> assert client.threads.thread["metadata"]["last_viewed_run_id"] == "run-1" +async def test_get_dashboard_thread_skips_mark_viewed_when_disabled(monkeypatch) -> None: + client = FakeClient( + { + "source": "dashboard", + "github_login": "octocat", + "latest_run_id": "run-1", + "latest_run_status": "success", + }, + "success", + ) + monkeypatch.setattr(thread_api, "langgraph_client", lambda: client) + + result = await thread_api.get_dashboard_thread("tid", "octocat", mark_viewed=False) + + assert result["status"] == "finished" + assert result["viewed"] is False + assert "last_viewed_run_id" not in client.threads.thread["metadata"] + + async def test_get_dashboard_thread_does_not_mark_running_thread_viewed(monkeypatch) -> None: client = FakeClient( { diff --git a/ui/src/lib/agents/api.ts b/ui/src/lib/agents/api.ts index 7e87df39..7d695edd 100644 --- a/ui/src/lib/agents/api.ts +++ b/ui/src/lib/agents/api.ts @@ -93,8 +93,12 @@ export const agentsApi = { agentsRequest(`/schedules/${encodeURIComponent(scheduleId)}`, { method: "DELETE", }), - getThread: (threadId: string) => - agentsRequest(`/threads/${encodeURIComponent(threadId)}`), + getThread: (threadId: string, options?: { markViewed?: boolean }) => + agentsRequest( + `/threads/${encodeURIComponent(threadId)}${ + options?.markViewed === false ? "?mark_viewed=false" : "" + }` + ), createThread: (body: ThreadCreateRequest) => agentsRequest("/threads", { method: "POST", diff --git a/ui/src/lib/agents/queries.ts b/ui/src/lib/agents/queries.ts index ec9307c3..032bdf6f 100644 --- a/ui/src/lib/agents/queries.ts +++ b/ui/src/lib/agents/queries.ts @@ -33,7 +33,7 @@ export function usePrefetchAgentThreadDetails( threadIds.forEach((threadId) => { void queryClient.prefetchQuery({ queryKey: agentThreadKeys.detail(threadId), - queryFn: () => agentsApi.getThread(threadId), + queryFn: () => agentsApi.getThread(threadId, { markViewed: false }), }) }) }, [activeThreadId, queryClient, threads]) @@ -54,6 +54,7 @@ export function useAgentThread(threadId: string) { return useQuery({ queryKey: agentThreadKeys.detail(threadId), queryFn: () => agentsApi.getThread(threadId), + refetchOnMount: "always", refetchInterval: (query) => { const status = query.state.data?.status return status === "running" ? 2000 : false