Resume sessions whose background subagents a restart killed

This commit is contained in:
Ian Gulliver
2026-10-23 15:16:03 -07:00
parent b5a72d713b
commit 2cfd408bc5
+25 -1
View File
@@ -35,6 +35,8 @@ const agentActor = "agent"
const restartNotice = "byre restarted while you were mid-turn, so your last turn was cut off. Re-check the working tree (git status and the files you were editing) before continuing where you left off." const restartNotice = "byre restarted while you were mid-turn, so your last turn was cut off. Re-check the working tree (git status and the files you were editing) before continuing where you left off."
const subagentsLostNotice = "Your agent process ended while background subagents you started were still running, so they were stopped and will never report back. Anything they found is lost; redo that work (run them again or do it yourself) and carry on where you left off."
var progressSubtypes = map[string]bool{"status": true, "thinking_tokens": true} var progressSubtypes = map[string]bool{"status": true, "thinking_tokens": true}
type Manager struct { type Manager struct {
@@ -456,11 +458,15 @@ func (m *Manager) Recover(ctx context.Context) error {
if err != nil { if err != nil {
return err return err
} }
orphaned := []string{}
for _, sess := range stale { for _, sess := range stale {
err = m.store.SetSubagents(ctx, sess.Id, 0, "byre") err = m.store.SetSubagents(ctx, sess.Id, 0, "byre")
if err != nil { if err != nil {
return err return err
} }
if sess.State == api.AgentSessionStateIdle {
orphaned = append(orphaned, sess.Id)
}
} }
sessions, err := m.store.SessionsInState(ctx, api.AgentSessionStateRunning) sessions, err := m.store.SessionsInState(ctx, api.AgentSessionStateRunning)
@@ -488,6 +494,10 @@ func (m *Manager) Recover(ctx context.Context) error {
} }
} }
for _, id := range orphaned {
m.wakeOrphaned(ctx, id)
}
idle, err := m.store.SessionsInState(ctx, api.AgentSessionStateIdle) idle, err := m.store.SessionsInState(ctx, api.AgentSessionStateIdle)
if err != nil { if err != nil {
return err return err
@@ -508,6 +518,15 @@ func (m *Manager) Recover(ctx context.Context) error {
return nil return nil
} }
func (m *Manager) wakeOrphaned(ctx context.Context, sessionID string) {
_, _, err := m.Message(ctx, sessionID, "byre", Prompt{Text: subagentsLostNotice})
if err != nil {
slog.Error("wake session whose subagents were stopped", "session", sessionID, "err", err)
return
}
slog.Info("woke session whose subagents were stopped", "session", sessionID)
}
func (m *Manager) failResume(ctx context.Context, sess api.AgentSession, err error) error { func (m *Manager) failResume(ctx context.Context, sess api.AgentSession, err error) error {
slog.Error("resume session", "session", sess.Id, "err", err) slog.Error("resume session", "session", sess.Id, "err", err)
if sess.Role == api.Reviewer { if sess.Role == api.Reviewer {
@@ -982,7 +1001,8 @@ func (m *Manager) pump(l *live) {
quiet := closing || l.stopping quiet := closing || l.stopping
m.mu.Unlock() m.mu.Unlock()
if len(l.subagents) > 0 && !closing { orphaned := len(l.subagents) > 0 && !closing
if orphaned {
err := m.store.SetSubagents(ctx, l.id, 0, "byre") err := m.store.SetSubagents(ctx, l.id, 0, "byre")
if err != nil { if err != nil {
slog.Error("clear exited session's subagents", "session", l.id, "err", err) slog.Error("clear exited session's subagents", "session", l.id, "err", err)
@@ -1000,6 +1020,10 @@ func (m *Manager) pump(l *live) {
return return
} }
if sess.State == api.AgentSessionStateIdle && orphaned {
m.wakeOrphaned(ctx, l.id)
return
}
if sess.State != api.AgentSessionStateRunning { if sess.State != api.AgentSessionStateRunning {
return return
} }