55"""
66
77import json
8- import os
8+ import re
99import time
1010from pathlib import Path
1111
1212SESSIONS_DIR = Path .home () / ".corecoder" / "sessions"
13+ _SAFE_SESSION_RE = re .compile (r"[^A-Za-z0-9._-]+" )
14+
15+
16+ def _normalize_session_id (session_id : str | None ) -> str :
17+ if not session_id :
18+ return f"session_{ int (time .time ())} "
19+
20+ name = session_id .strip ().replace ("\\ " , "/" ).split ("/" )[- 1 ]
21+ name = _SAFE_SESSION_RE .sub ("-" , name ).strip (".-_" )
22+ return name or f"session_{ int (time .time ())} "
23+
24+
25+ def _session_path (session_id : str ) -> Path :
26+ path = (SESSIONS_DIR / f"{ _normalize_session_id (session_id )} .json" ).resolve ()
27+ root = SESSIONS_DIR .resolve ()
28+ if root != path .parent :
29+ raise ValueError ("Invalid session id" )
30+ return path
1331
1432
1533def save_session (messages : list [dict ], model : str , session_id : str | None = None ) -> str :
1634 """Save conversation to disk. Returns the session ID."""
1735 SESSIONS_DIR .mkdir (parents = True , exist_ok = True )
1836
19- if not session_id :
20- session_id = f"session_{ int (time .time ())} "
37+ session_id = _normalize_session_id (session_id )
2138
2239 data = {
2340 "id" : session_id ,
@@ -26,14 +43,14 @@ def save_session(messages: list[dict], model: str, session_id: str | None = None
2643 "messages" : messages ,
2744 }
2845
29- path = SESSIONS_DIR / f" { session_id } .json"
46+ path = _session_path ( session_id )
3047 path .write_text (json .dumps (data , ensure_ascii = False , indent = 2 ))
3148 return session_id
3249
3350
3451def load_session (session_id : str ) -> tuple [list [dict ], str ] | None :
3552 """Load a saved session. Returns (messages, model) or None."""
36- path = SESSIONS_DIR / f" { session_id } .json"
53+ path = _session_path ( session_id )
3754 if not path .exists ():
3855 return None
3956
0 commit comments