fix(agent): reopenSession replaces plan steps for executing sessions too
A follow-up on an executing session (first turn didn't complete_task) is still a new direction — the old plan's steps must not block the new one. Previously reopenSession was a no-op for executing sessions, leaving done steps that caused errPlanInFlight on the next propose_plan call.
This commit is contained in:
@@ -478,11 +478,16 @@ func (s *store) setGoal(ctx context.Context, sessionID, goal string) error {
|
||||
// audit trail survives. proposePlan's anyStarted check excludes
|
||||
// `replaced` (see proposePlan), so the next propose_plan takes the
|
||||
// fresh-generation path rather than being refused with errPlanInFlight.
|
||||
// 2. Clears outcome/summary so the panel doesn't show the old result.
|
||||
// This runs for BOTH terminal and executing sessions — a follow-up on
|
||||
// an executing session (first turn didn't complete_task) is still a
|
||||
// new direction, and the old plan's steps must not block the new one.
|
||||
// 2. If the session was terminal, clears outcome/summary and flips status
|
||||
// to `executing` so the panel doesn't show the old result.
|
||||
// 3. Stamps last_active_at.
|
||||
//
|
||||
// Returns true if the session was actually reopened (was terminal), false if
|
||||
// it was already active (no-op — the follow-up is just a continuation).
|
||||
// it was already active (the plan steps are still replaced — the new
|
||||
// direction needs a fresh plan — but the status doesn't change).
|
||||
func (s *store) reopenSession(ctx context.Context, sessionID string) bool {
|
||||
if s == nil || sessionID == "" || sessionID == "ephemeral" {
|
||||
return false
|
||||
@@ -492,12 +497,14 @@ func (s *store) reopenSession(ctx context.Context, sessionID string) bool {
|
||||
`SELECT status FROM agent_sessions WHERE id = $1`, sessionID).Scan(¤tStatus); err != nil {
|
||||
return false
|
||||
}
|
||||
if currentStatus != "done" && currentStatus != "failed" {
|
||||
return false
|
||||
}
|
||||
// Always replace prior plan steps — the follow-up is a new direction
|
||||
// regardless of whether the prior turn completed. Without this, an
|
||||
// executing session with done steps would block proposePlan with
|
||||
// errPlanInFlight.
|
||||
s.pool.Exec(ctx,
|
||||
`UPDATE session_plan_steps SET status = 'replaced', finished_at = COALESCE(finished_at, now()) WHERE session_id = $1 AND status <> 'replaced'`,
|
||||
sessionID)
|
||||
if currentStatus == "done" || currentStatus == "failed" {
|
||||
s.pool.Exec(ctx,
|
||||
`UPDATE agent_sessions SET status = 'executing', outcome = NULL, summary = NULL, last_active_at = now() WHERE id = $1`,
|
||||
sessionID)
|
||||
@@ -505,6 +512,9 @@ func (s *store) reopenSession(ctx context.Context, sessionID string) bool {
|
||||
"info", "nomos", sessionID, map[string]any{"prior_status": currentStatus})
|
||||
return true
|
||||
}
|
||||
s.pool.Exec(ctx, `UPDATE agent_sessions SET last_active_at = now() WHERE id = $1`, sessionID)
|
||||
return false
|
||||
}
|
||||
|
||||
// planStepInput is one step as the agent proposes it.
|
||||
type planStepInput struct {
|
||||
|
||||
Reference in New Issue
Block a user