@@ -167,6 +167,10 @@ func (s *Service) Run(ctx context.Context) error {
167167 return fmt .Errorf ("prepare daemon data directory: %w" , err )
168168 }
169169
170+ // Restore provider session refs from disk so daemon restarts can
171+ // resume CLI sessions instead of starting fresh conversations.
172+ s .loadSessionState ()
173+
170174 // Log CLI tool versions at startup so operators can see at a glance
171175 // whether any runner is out of the tested range.
172176 CheckRunnerVersions (map [string ]string {
@@ -367,6 +371,7 @@ func (s *Service) setProviderSession(sessionID, providerSessionRef string) {
367371 s .mu .Lock ()
368372 defer s .mu .Unlock ()
369373 s .providerSessions [sessionID ] = providerSessionRef
374+ s .persistSessionState ()
370375}
371376
372377// pinSessionWorkspace records the first workspaceRoot used for a session and
@@ -378,9 +383,67 @@ func (s *Service) pinSessionWorkspace(sessionID, workspaceRoot string) string {
378383 return existing
379384 }
380385 s .sessionWorkspaces [sessionID ] = workspaceRoot
386+ s .persistSessionState ()
381387 return workspaceRoot
382388}
383389
390+ // ── Session state persistence ──────────────────────────────────────
391+ //
392+ // Persists providerSessions and sessionWorkspaces to disk so that
393+ // daemon restarts can resume CLI sessions (--resume / thread/resume)
394+ // instead of starting fresh conversations.
395+
396+ type sessionStateFile struct {
397+ ProviderSessions map [string ]string `json:"providerSessions"`
398+ SessionWorkspaces map [string ]string `json:"sessionWorkspaces"`
399+ }
400+
401+ func sessionStatePath () string {
402+ return filepath .Join (config .HomeDir (), "sessions.json" )
403+ }
404+
405+ // persistSessionState writes the current session mappings to disk.
406+ // Caller must hold s.mu.
407+ func (s * Service ) persistSessionState () {
408+ data := sessionStateFile {
409+ ProviderSessions : s .providerSessions ,
410+ SessionWorkspaces : s .sessionWorkspaces ,
411+ }
412+ body , err := json .Marshal (data )
413+ if err != nil {
414+ log .Printf ("failed to marshal session state: %v" , err )
415+ return
416+ }
417+ if err := os .WriteFile (sessionStatePath (), body , 0o600 ); err != nil {
418+ log .Printf ("failed to persist session state: %v" , err )
419+ }
420+ }
421+
422+ // loadSessionState restores provider session refs and workspace mappings
423+ // from disk. Called once at startup.
424+ func (s * Service ) loadSessionState () {
425+ body , err := os .ReadFile (sessionStatePath ())
426+ if err != nil {
427+ return // file doesn't exist yet — first run
428+ }
429+ var data sessionStateFile
430+ if err := json .Unmarshal (body , & data ); err != nil {
431+ log .Printf ("failed to parse session state: %v" , err )
432+ return
433+ }
434+ if data .ProviderSessions != nil {
435+ for k , v := range data .ProviderSessions {
436+ s .providerSessions [k ] = v
437+ }
438+ }
439+ if data .SessionWorkspaces != nil {
440+ for k , v := range data .SessionWorkspaces {
441+ s .sessionWorkspaces [k ] = v
442+ }
443+ }
444+ log .Printf ("restored %d provider sessions from disk" , len (data .ProviderSessions ))
445+ }
446+
384447func (s * Service ) setTaskWorkspace (taskRunID , workspaceRoot string ) {
385448 taskRunID = strings .TrimSpace (taskRunID )
386449 workspaceRoot = strings .TrimSpace (workspaceRoot )
0 commit comments