feat: session pause/resume to reduce resource usage
Adds pause and resume commands to session/ctl:
- pause: stops the tool server process, closes connections
- resume: respawns the process and re-establishes connection
When paused, tool calls return 'connection closed' errors which
propagate to the chat log. Useful for reducing resource usage when
many sessions exist but aren't actively being used.
Also adds session/{id}/paused file to check pause status.
This commit is contained in:
parent
ff3f8c8819
commit
8cebf08cbd
|
|
@ -33,6 +33,14 @@ func listSessions(ctx HandlerCtx) ([]FsNodeDecl, error) {
|
|||
return []byte(sessionEnvContent(s)), nil
|
||||
})),
|
||||
|
||||
// paused — session pause status (read-only)
|
||||
Leaf("paused", 0444, Read(func(_ HandlerCtx) ([]byte, error) {
|
||||
if s.IsPaused() {
|
||||
return []byte("true\n"), nil
|
||||
}
|
||||
return []byte("false\n"), nil
|
||||
})),
|
||||
|
||||
// ctl — session control
|
||||
Leaf("ctl", 0666,
|
||||
Read(nopRead),
|
||||
|
|
@ -143,6 +151,10 @@ func handleSessionCtl(sess *Session, kill func(), data []byte) error {
|
|||
}
|
||||
case "invalidate":
|
||||
sess.InvalidateModelsCache()
|
||||
case "pause":
|
||||
return sess.Pause()
|
||||
case "resume":
|
||||
return sess.Resume()
|
||||
default:
|
||||
return fmt.Errorf("unknown session ctl command: %s", cmd[0])
|
||||
}
|
||||
|
|
|
|||
54
fs/types.go
54
fs/types.go
|
|
@ -25,6 +25,7 @@ type Session struct {
|
|||
cancel context.CancelFunc
|
||||
proc *toolsrv.Process // toolsrv subprocess (session owns lifecycle)
|
||||
keeper *toolsrv.ProcessKeeper // resilient process manager (respawns on failure)
|
||||
paused bool // true when session is paused (process stopped)
|
||||
|
||||
// toolsConn is the multiplexed connection to the remote tool server,
|
||||
// shared by agent execution and 9P tool-management handlers.
|
||||
|
|
@ -227,6 +228,59 @@ func (sess *Session) Interrupt() {
|
|||
}
|
||||
}
|
||||
|
||||
// IsPaused returns true if the session is paused.
|
||||
func (sess *Session) IsPaused() bool {
|
||||
sess.mu.RLock()
|
||||
defer sess.mu.RUnlock()
|
||||
return sess.paused
|
||||
}
|
||||
|
||||
// Pause stops the tool server process to save resources. Tool calls will
|
||||
// return errors until Resume is called. Returns an error if already paused.
|
||||
func (sess *Session) Pause() error {
|
||||
sess.mu.Lock()
|
||||
defer sess.mu.Unlock()
|
||||
if sess.paused {
|
||||
return fmt.Errorf("session already paused")
|
||||
}
|
||||
// Close the keeper which kills the underlying process
|
||||
if sess.keeper != nil {
|
||||
sess.keeper.Close()
|
||||
} else if sess.proc != nil {
|
||||
sess.proc.Close()
|
||||
}
|
||||
// Close the connection (it will fail on next use anyway)
|
||||
if sess.toolsConn != nil {
|
||||
if c, ok := sess.toolsConn.(interface{ Close() }); ok {
|
||||
c.Close()
|
||||
}
|
||||
sess.toolsConn = nil
|
||||
}
|
||||
sess.paused = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Resume restarts the tool server process after a Pause. Returns an error
|
||||
// if not paused or if respawn fails.
|
||||
func (sess *Session) Resume() error {
|
||||
sess.mu.Lock()
|
||||
defer sess.mu.Unlock()
|
||||
if !sess.paused {
|
||||
return fmt.Errorf("session not paused")
|
||||
}
|
||||
if sess.keeper == nil {
|
||||
return fmt.Errorf("no process keeper available")
|
||||
}
|
||||
// Dial triggers respawn if needed
|
||||
conn, err := sess.keeper.Dial()
|
||||
if err != nil {
|
||||
return fmt.Errorf("resume failed: %w", err)
|
||||
}
|
||||
sess.toolsConn = conn
|
||||
sess.paused = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- AgentLog methods ---
|
||||
|
||||
// AppendLog appends data to the agent's log and bumps the version.
|
||||
|
|
|
|||
Loading…
Reference in New Issue