Fix paused session resume lifecycle

This commit is contained in:
Ollie Agent 2026-08-17 18:23:00 +02:00
parent e1136fdb82
commit 49e43248b0
1 changed files with 39 additions and 5 deletions

View File

@ -119,15 +119,23 @@ func (s *Session) ToolsConn() *toolclient.ToolsrvConn {
// Caller is responsible for closing the returned conn.
func (s *Session) DialToolServer() *toolclient.ToolsrvConn {
s.mu.RLock()
if s.paused {
s.mu.RUnlock()
return nil
}
k := s.Keeper
s.mu.RUnlock()
if k == nil {
s.log.Debug("DialToolServer: no keeper")
return nil
}
s.log.Debug("DialToolServer: dialing keeper paused=%v", s.IsPaused())
conn, err := k.Dial()
if err != nil {
s.log.Debug("DialToolServer: dial failed: %v", err)
return nil
}
s.log.Debug("DialToolServer: dial succeeded")
return conn
}
@ -351,24 +359,31 @@ func (s *Session) SetSessionID(newID string) error {
// Pause stops the tool server to save resources.
func (s *Session) Pause() error {
s.log.Debug("Pause: begin")
s.mu.Lock()
defer s.mu.Unlock()
if s.paused {
return fmt.Errorf("session already paused")
}
s.paused = true
s.log.Debug("Pause: marked paused")
if s.Cancel != nil {
s.Cancel()
}
s.log.Debug("Pause: context canceled")
if s.Keeper != nil {
s.log.Debug("Pause: closing keeper")
s.Keeper.Close()
} else if s.Proc != nil {
s.Proc.Close()
}
if s.toolsConn != nil {
s.log.Debug("Pause: closing tools connection")
s.toolsConn.Close()
s.toolsConn = nil
}
s.paused = true
s.log.Debug("Pause: complete")
go s.saveSession()
PublishEvent("session."+s.ID+".pause", "")
return nil
@ -376,26 +391,31 @@ func (s *Session) Pause() error {
// Resume restarts the tool server after a Pause.
func (s *Session) Resume() error {
s.log.Debug("Resume: begin")
s.mu.Lock()
defer s.mu.Unlock()
if !s.paused {
s.mu.Unlock()
return fmt.Errorf("session not paused")
}
ctx, cancel := context.WithCancel(serverCtx)
s.Ctx = ctx
s.Cancel = cancel
s.log.Debug("Resume: context created")
// Ensure we always have a Keeper. If the session was restored from disk
// without infra, spawn a new tool server now.
if s.Keeper == nil {
cwd := ""
if len(s.agents) > 0 {
cwd = s.agents[0].Cwd()
}
s.log.Info("Resume: no keeper, starting toolsrv")
cwd := s.cwd
if cwd == "" {
cwd, _ = os.Getwd()
}
if s.Remote != "" {
s.log.Info("Resume: spawning remote toolsrv target=%s cwd=%s", s.Remote, cwd)
} else {
s.log.Info("Resume: spawning local toolsrv cwd=%s", cwd)
}
infra, err := SetupToolServer(ToolServerConfig{
Ctx: ctx,
CWD: cwd,
@ -404,8 +424,12 @@ func (s *Session) Resume() error {
Yolo: pkgYolo,
})
if err != nil {
s.log.Error("Resume: toolsrv startup failed: %v", err)
cancel()
s.mu.Unlock()
return fmt.Errorf("resume: %w", err)
}
s.log.Info("Resume: toolsrv started")
s.Proc = infra.Proc
s.Keeper = infra.Keeper
s.toolsConn = infra.ToolsConn
@ -415,12 +439,14 @@ func (s *Session) Resume() error {
}
} else {
// Keeper exists — just re-dial.
s.log.Debug("Resume: dialing existing keeper")
s.Keeper.SetContext(ctx)
conn, err := s.Keeper.Dial()
if err != nil {
return fmt.Errorf("resume failed: %w", err)
}
s.toolsConn = conn
s.log.Debug("Resume: existing keeper dial succeeded")
// Update agent connections with fresh dials
for _, ag := range s.agents {
agConn, err := s.Keeper.Dial()
@ -436,6 +462,13 @@ func (s *Session) Resume() error {
}
s.paused = false
s.mu.Unlock()
s.log.Debug("Resume: marked active")
// Start bypass approval loop for the new session context.
if pkgBypassNotify != nil {
s.StartBypassLoop(pkgBypassNotify)
}
// Rebuild full runtimes for restored agents (they have stub runtimes from persist).
for _, ag := range s.agents {
@ -450,6 +483,7 @@ func (s *Session) Resume() error {
}
go s.saveSession()
PublishEvent("session."+s.ID+".resume", "")
s.log.Debug("Resume: complete")
return nil
}