1010
1111from ..types .exceptions import SessionException
1212from ..types .session import Session , SessionAgent , SessionMessage
13- from .agent_session_manager import AgentSessionManager
13+ from .repository_session_manager import RepositorySessionManager
1414from .session_repository import SessionRepository
1515
1616logger = logging .getLogger (__name__ )
2020MESSAGE_PREFIX = "message_"
2121
2222
23- class FileSessionManager (AgentSessionManager , SessionRepository ):
24- """File-based session manager for local filesystem storage."""
23+ class FileSessionManager (RepositorySessionManager , SessionRepository ):
24+ """File-based session manager for local filesystem storage.
25+
26+ Creates the following filesystem structure for the session storage:
27+ /<sessions_dir>/
28+ └── session_<session_id>/
29+ ├── session.json # Session metadata
30+ └── agents/
31+ └── agent_<agent_id>/
32+ ├── agent.json # Agent metadata
33+ └── messages/
34+ ├── message_<created_timestamp>_<id1>.json
35+ └── message_<created_timestamp>_<id2>.json
36+
37+ """
2538
2639 def __init__ (self , session_id : str , storage_dir : Optional [str ] = None ):
2740 """Initialize FileSession with filesystem storage.
@@ -44,10 +57,22 @@ def _get_agent_path(self, session_id: str, agent_id: str) -> str:
4457 session_path = self ._get_session_path (session_id )
4558 return os .path .join (session_path , "agents" , f"{ AGENT_PREFIX } { agent_id } " )
4659
47- def _get_message_path (self , session_id : str , agent_id : str , message_id : str ) -> str :
48- """Get message file path."""
60+ def _get_message_path (self , session_id : str , agent_id : str , message_id : str , timestamp : str ) -> str :
61+ """Get message file path.
62+
63+ Args:
64+ session_id: ID of the session
65+ agent_id: ID of the agent
66+ message_id: ID of the message
67+ timestamp: ISO format timestamp to include in filename for sorting
68+ Returns:
69+ The filename for the message
70+ """
4971 agent_path = self ._get_agent_path (session_id , agent_id )
50- return os .path .join (agent_path , "messages" , f"{ MESSAGE_PREFIX } { message_id } .json" )
72+ # Use timestamp for sortable filenames
73+ # Replace colons and periods in ISO format with underscores for filesystem compatibility
74+ filename_timestamp = timestamp .replace (":" , "_" ).replace ("." , "_" )
75+ return os .path .join (agent_path , "messages" , f"{ MESSAGE_PREFIX } { filename_timestamp } _{ message_id } .json" )
5176
5277 def _read_file (self , path : str ) -> dict [str , Any ]:
5378 """Read JSON file."""
@@ -135,17 +160,26 @@ def create_message(self, session_id: str, agent_id: str, session_message: Sessio
135160 session_id ,
136161 agent_id ,
137162 session_message .message_id ,
163+ session_message .created_at ,
138164 )
139165 session_dict = asdict (session_message )
140166 self ._write_file (message_file , session_dict )
141167
142168 def read_message (self , session_id : str , agent_id : str , message_id : str ) -> Optional [SessionMessage ]:
143169 """Read message data."""
144- message_file = self ._get_message_path (session_id , agent_id , message_id )
145- if not os .path .exists (message_file ):
170+ # Get the messages directory
171+ messages_dir = os .path .join (self ._get_agent_path (session_id , agent_id ), "messages" )
172+ if not os .path .exists (messages_dir ):
146173 return None
147- message_data = self ._read_file (message_file )
148- return SessionMessage .from_dict (message_data )
174+
175+ # List files in messages directory, and check if the filename ends with the message id
176+ for filename in os .listdir (messages_dir ):
177+ if filename .endswith (f"{ message_id } .json" ):
178+ file_path = os .path .join (messages_dir , filename )
179+ message_data = self ._read_file (file_path )
180+ return SessionMessage .from_dict (message_data )
181+
182+ return None
149183
150184 def update_message (self , session_id : str , agent_id : str , session_message : SessionMessage ) -> None :
151185 """Update message data."""
@@ -156,7 +190,7 @@ def update_message(self, session_id: str, agent_id: str, session_message: Sessio
156190
157191 # Preserve the original created_at timestamp
158192 session_message .created_at = previous_message .created_at
159- message_file = self ._get_message_path (session_id , agent_id , message_id )
193+ message_file = self ._get_message_path (session_id , agent_id , message_id , session_message . created_at )
160194 self ._write_file (message_file , asdict (session_message ))
161195
162196 def list_messages (
@@ -168,20 +202,25 @@ def list_messages(
168202 raise SessionException (f"Messages directory missing from agent: { agent_id } in session { session_id } " )
169203
170204 # Read all message files
171- messages : list [SessionMessage ] = []
205+ message_files : list [str ] = []
172206 for filename in os .listdir (messages_dir ):
173207 if filename .startswith (MESSAGE_PREFIX ) and filename .endswith (".json" ):
174- file_path = os .path .join (messages_dir , filename )
175- message_data = self ._read_file (file_path )
176- messages .append (SessionMessage .from_dict (message_data ))
208+ message_files .append (filename )
177209
178- # Sort by created_at timestamp (oldest first)
179- messages .sort (key = lambda x : x . created_at )
210+ # Sort filenames - the timestamp in the file's name will sort chronologically
211+ message_files .sort ()
180212
181- # Apply pagination
213+ # Apply pagination to filenames
182214 if limit is not None :
183- messages = messages [offset : offset + limit ]
215+ message_files = message_files [offset : offset + limit ]
184216 else :
185- messages = messages [offset :]
217+ message_files = message_files [offset :]
218+
219+ # Load only the message files
220+ messages : list [SessionMessage ] = []
221+ for filename in message_files :
222+ file_path = os .path .join (messages_dir , filename )
223+ message_data = self ._read_file (file_path )
224+ messages .append (SessionMessage .from_dict (message_data ))
186225
187226 return messages
0 commit comments