mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-22 17:44:53 +02:00
[eric] sessions: get_all_sessions promotes only disk sessions the layout still cards, so deleted chats stop resurrecting on reopen
This commit is contained in:
@@ -4639,17 +4639,20 @@ class AgentManager:
|
||||
def get_all_sessions(self, dashboard_id: str | None = None) -> list[AgentSession]:
|
||||
if not dashboard_id:
|
||||
return list(self.sessions.values())
|
||||
# Memory first, then promote any on-disk sessions for this dashboard
|
||||
# that aren't loaded yet. Imported sessions (and ones not resumed since
|
||||
# a restart) live on disk but not in memory, so without the disk pass
|
||||
# their cards render blank, the frontend's AgentCard returns null when
|
||||
# a card's session is missing from the agents slice. Promoting into
|
||||
# self.sessions bounds the disk read to once per session per run, like
|
||||
# resume_session. Mirrors get_browser_agent_children's memory+disk walk.
|
||||
# Memory first, then promote on-disk sessions for this dashboard, but
|
||||
# ONLY ones the dashboard's layout still has a card for. A session keeps
|
||||
# its dashboard_id when its card is deleted, so promoting by tag alone
|
||||
# resurrected deleted chats on every reopen; the layout's cards are the
|
||||
# real source of truth for what's on the board. Imported sessions ARE in
|
||||
# the layout, so they still surface, and this bounds the disk read to
|
||||
# once per session per run, like resume_session.
|
||||
result = [s for s in self.sessions.values() if s.dashboard_id == dashboard_id]
|
||||
seen = {s.id for s in result}
|
||||
card_ids = self._dashboard_card_ids(dashboard_id)
|
||||
for sid, data in _load_all_session_data():
|
||||
if sid in seen or data.get("dashboard_id") != dashboard_id:
|
||||
if sid in seen or sid not in card_ids:
|
||||
continue
|
||||
if data.get("dashboard_id") != dashboard_id:
|
||||
continue
|
||||
try:
|
||||
sess = AgentSession(**data)
|
||||
@@ -4661,6 +4664,18 @@ class AgentManager:
|
||||
result.append(sess)
|
||||
return result
|
||||
|
||||
def _dashboard_card_ids(self, dashboard_id: str) -> set[str]:
|
||||
"""Session ids the dashboard's layout currently has agent cards for.
|
||||
Read straight off disk (no dashboards-module import, avoids a cycle)."""
|
||||
try:
|
||||
import os
|
||||
import backend.config.paths as _paths
|
||||
from backend.config.json_store import read_json_or_none
|
||||
d = read_json_or_none(os.path.join(_paths.DASHBOARDS_DIR, f"{dashboard_id}.json")) or {}
|
||||
return set((d.get("layout", {}).get("cards") or {}).keys())
|
||||
except Exception:
|
||||
return set()
|
||||
|
||||
def get_session(self, session_id: str) -> Optional[AgentSession]:
|
||||
return self.sessions.get(session_id)
|
||||
|
||||
|
||||
@@ -323,6 +323,39 @@ def test_dashboard_export_import_carries_agent_cards_and_transcript(tmp_path, mo
|
||||
assert sum(len(s.messages) for s in found) == 2, "and with their transcripts"
|
||||
|
||||
|
||||
def test_get_all_sessions_does_not_resurrect_deleted_cards(tmp_path, monkeypatch):
|
||||
# Deleting a card removes it from the layout but the session keeps its
|
||||
# dashboard_id on disk. get_all_sessions must surface only sessions the
|
||||
# layout still has a card for, or deleted chats come back on every reopen.
|
||||
from backend.apps.agents import agent_manager as am
|
||||
import backend.config.paths as paths
|
||||
sdir = tmp_path / "sessions"
|
||||
ddir = tmp_path / "dashboards"
|
||||
sdir.mkdir()
|
||||
ddir.mkdir()
|
||||
monkeypatch.setattr(am, "SESSIONS_DIR", str(sdir))
|
||||
monkeypatch.setattr(paths, "DASHBOARDS_DIR", str(ddir))
|
||||
monkeypatch.setattr(am.agent_manager, "sessions", {})
|
||||
|
||||
did = "d1"
|
||||
|
||||
def sess(sid):
|
||||
return {
|
||||
"id": sid, "name": sid, "status": "completed", "model": "sonnet",
|
||||
"mode": "agent", "messages": [], "branches": {}, "active_branch_id": "main",
|
||||
"dashboard_id": did,
|
||||
}
|
||||
|
||||
(sdir / "kept.json").write_text(json.dumps(sess("kept")))
|
||||
(sdir / "deleted.json").write_text(json.dumps(sess("deleted"))) # still tagged, card gone
|
||||
# The layout has a card only for "kept" (the user deleted "deleted"'s card).
|
||||
(ddir / f"{did}.json").write_text(json.dumps({"id": did, "layout": {"cards": {"kept": {"session_id": "kept"}}}}))
|
||||
|
||||
ids = {s.id for s in am.agent_manager.get_all_sessions(dashboard_id=did)}
|
||||
assert "kept" in ids, "a session the layout still has a card for must surface"
|
||||
assert "deleted" not in ids, "a session whose card was deleted must NOT resurrect"
|
||||
|
||||
|
||||
def test_dashboard_serialize_rewrites_refs_to_bundle_ids():
|
||||
from backend.apps.swarm.entities.dashboards import DashboardExportable
|
||||
from backend.apps.swarm.models import EntityType
|
||||
|
||||
Reference in New Issue
Block a user