From 68beb599c9e0ddacebc7428924f2d0752822e335 Mon Sep 17 00:00:00 2001 From: ciregenz Date: Tue, 23 Jun 2026 11:51:29 -0700 Subject: [PATCH] [eric] agents: cloud_sync convention-clean (sync_session_close off leading-_, @typechecked, typed) --- .../manager/session/SessionLifecycleMixin.py | 2 +- .../apps/agents/manager/session/cloud_sync.py | 36 +++++++------------ 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/backend/apps/agents/manager/session/SessionLifecycleMixin.py b/backend/apps/agents/manager/session/SessionLifecycleMixin.py index 5e3fa279..c0ef85b3 100644 --- a/backend/apps/agents/manager/session/SessionLifecycleMixin.py +++ b/backend/apps/agents/manager/session/SessionLifecycleMixin.py @@ -18,7 +18,7 @@ from backend.apps.agents.manager.session.session_store import ( _save_session as save_session, build_search_text, ) -from backend.apps.agents.manager.session.cloud_sync import _sync_session_close as sync_session_close +from backend.apps.agents.manager.session.cloud_sync import sync_session_close from backend.apps.agents.manager.session.apply_context_window import apply_context_window from backend.apps.agents.manager.session import lifecycle from backend.apps.agents.manager.view_builder_state import ( diff --git a/backend/apps/agents/manager/session/cloud_sync.py b/backend/apps/agents/manager/session/cloud_sync.py index 09c4e5af..fa02e716 100644 --- a/backend/apps/agents/manager/session/cloud_sync.py +++ b/backend/apps/agents/manager/session/cloud_sync.py @@ -1,36 +1,26 @@ +"""Submit a session snapshot to the cloud on close. The cloud consumes the dump however it sees +fit; the desktop just hands off a snapshot. Skipped for mock sessions so dev runs don't post to +the real backend. Synthesizes a closed_at timestamp on the cloud-bound dump if the session lacks +one (two paths, browser_agent close and shutdown_all_sessions, previously sent it null, which +left the cloud unable to compute duration_ms). Fixed here at the bottleneck so no call site can +miss it; the on-disk session JSON keeps its original (possibly None) closed_at.""" + from datetime import datetime +from typeguard import typechecked + from backend.apps.agents.core.models import AgentSession -from backend.apps.service.client import sync as _sync +from backend.apps.service.client import sync as submit_to_cloud -def _sync_session_close(session: AgentSession, close_reason: str = "user"): - """Submit the session state to the cloud on close. The cloud - consumes the dump however it sees fit; the desktop just hands off - a snapshot. Skipped for mock sessions so dev runs don't post to - the real backend. - - Synthesizes a `closed_at` timestamp on the dump if the session - doesn't have one. Two paths previously sent close-events without - a timestamp and made the cloud unable to compute duration_ms - (which surfaced as duration_ms=null on 90% of session.ended events, - browser-agent and shutdown paths in particular): - - 1. browser_agent.py calls this without setting closed_at. - 2. shutdown_all_sessions() clears closed_at to None for the - on-disk restore mechanism, then syncs. - - Fix is here at the bottleneck rather than at every caller so we - can't miss a future call site. The on-disk session JSON keeps its - original (possibly None) closed_at, only the cloud-bound dump - gets the synthesized timestamp. - """ +@typechecked +def sync_session_close(session: AgentSession, close_reason: str = "user") -> None: if close_reason == "mock" or getattr(session, "_mock_run", False): return try: dump = session.model_dump(mode="json") if not dump.get("closed_at"): dump["closed_at"] = datetime.now().isoformat() - _sync(dump) + submit_to_cloud(dump) except Exception: pass