on main help sign in
fix(agent): release activeRequests before publishing TypeAgentFinished

I keep hitting a bug where a turn finishes -- desktop notification fires, agent is clearly done -- but the terminal stays stuck on the spinner with input blocked until something unrelated wakes the TUI up. The TUI polls IsSessionBusy() inside its TypeAgentFinished handler and only re-evaluates on incoming tea.Msgs. Run() right now publishes the notification while activeRequests still holds the session's cancel func, so the poll on receipt sees true and the busy gates stay stuck. Reorder so cleanup runs first. ๐Ÿ’˜ Generated with Crush Assisted-by: Claude Opus 4.7 via Crush <crush@charm.land>

commit9d3466885813791119e6d3bf72460eb0b623f894 authorSven Olsen <sven2718@gmail.com> dateFri 8 May 2026, 14:34 (5 months ago) parent78088cd
1 file changed+14โˆ’11
internal/agent/agent.go+14โˆ’11
internal/agent/agent.go+14โˆ’11
@@ -581,16 +581,6 @@ func (a *sessionAgent) Run(ctx context.Context, call SessionAgentCall) (*fantasy
581581 return nil, err
582582 }
583583
584โˆ’ // Send notification that agent has finished its turn (skip for
585โˆ’ // nested/non-interactive sessions).
586โˆ’ if !call.NonInteractive && a.notify != nil {
587โˆ’ a.notify.Publish(pubsub.CreatedEvent, notify.Notification{
588โˆ’ SessionID: call.SessionID,
589โˆ’ SessionTitle: currentSession.Title,
590โˆ’ Type: notify.TypeAgentFinished,
591โˆ’ })
592โˆ’ }
593โˆ’
594584 if shouldSummarize {
595585 a.activeRequests.Del(call.SessionID)
596586 if summarizeErr := a.Summarize(genCtx, call.SessionID, call.ProviderOptions); summarizeErr != nil {
@@ -608,10 +598,23 @@ func (a *sessionAgent) Run(ctx context.Context, call SessionAgentCall) (*fantasy
608598 }
609599 }
610600
611โˆ’ // Release active request before processing queued messages.
601+ // Release active request before publishing the notification.
602+ // TUI handlers poll IsSessionBusy() and only re-evaluate when a
603+ // tea.Msg arrives, so the cleanup must precede the notify or
604+ // subscribers see stale busy state at the moment of receipt.
612605 a.activeRequests.Del(call.SessionID)
613606 cancel()
614607
608+ // Send notification that agent has finished its turn (skip for
609+ // nested/non-interactive sessions).
610+ if !call.NonInteractive && a.notify != nil {
611+ a.notify.Publish(pubsub.CreatedEvent, notify.Notification{
612+ SessionID: call.SessionID,
613+ SessionTitle: currentSession.Title,
614+ Type: notify.TypeAgentFinished,
615+ })
616+ }
617+
615618 queuedMessages, ok := a.messageQueue.Get(call.SessionID)
616619 if !ok || len(queuedMessages) == 0 {
617620 return result, err