feat(sessions): add --source flag for third-party session isolation (#3255)

When third-party tools (Paperclip orchestrator, etc.) spawn hermes chat
as a subprocess, their sessions pollute user session history and search.

- hermes chat --source <tag> (also HERMES_SESSION_SOURCE env var)
- exclude_sources parameter on list_sessions_rich() and search_messages()
- Sessions with source=tool hidden from sessions list/browse/search
- Third-party adapters pass --source tool to isolate agent sessions

Cherry-picked from PR #3208 by HenkDz.

Co-authored-by: Henkey <noonou7@gmail.com>
This commit is contained in:
Teknium
2026-03-26 14:35:31 -07:00
committed by GitHub
parent 41ee207a5e
commit db241ae6ce
7 changed files with 143 additions and 9 deletions

View File

@@ -572,6 +572,7 @@ class SessionDB:
def list_sessions_rich(
self,
source: str = None,
exclude_sources: List[str] = None,
limit: int = 20,
offset: int = 0,
) -> List[Dict[str, Any]]:
@@ -583,7 +584,18 @@ class SessionDB:
Uses a single query with correlated subqueries instead of N+2 queries.
"""
source_clause = "WHERE s.source = ?" if source else ""
where_clauses = []
params = []
if source:
where_clauses.append("s.source = ?")
params.append(source)
if exclude_sources:
placeholders = ",".join("?" for _ in exclude_sources)
where_clauses.append(f"s.source NOT IN ({placeholders})")
params.extend(exclude_sources)
where_sql = f"WHERE {' AND '.join(where_clauses)}" if where_clauses else ""
query = f"""
SELECT s.*,
COALESCE(
@@ -598,11 +610,11 @@ class SessionDB:
s.started_at
) AS last_active
FROM sessions s
{source_clause}
{where_sql}
ORDER BY s.started_at DESC
LIMIT ? OFFSET ?
"""
params = (source, limit, offset) if source else (limit, offset)
params.extend([limit, offset])
with self._lock:
cursor = self._conn.execute(query, params)
rows = cursor.fetchall()
@@ -818,6 +830,7 @@ class SessionDB:
self,
query: str,
source_filter: List[str] = None,
exclude_sources: List[str] = None,
role_filter: List[str] = None,
limit: int = 20,
offset: int = 0,
@@ -850,6 +863,11 @@ class SessionDB:
where_clauses.append(f"s.source IN ({source_placeholders})")
params.extend(source_filter)
if exclude_sources is not None:
exclude_placeholders = ",".join("?" for _ in exclude_sources)
where_clauses.append(f"s.source NOT IN ({exclude_placeholders})")
params.extend(exclude_sources)
if role_filter:
role_placeholders = ",".join("?" for _ in role_filter)
where_clauses.append(f"m.role IN ({role_placeholders})")