@@ -305,7 +305,13 @@ func (a *sessionAgent) Run(ctx context.Context, call SessionAgentCall) (result *
305 // (the pubsub broker fan-in does not serialize publishes from
305 // (the pubsub broker fan-in does not serialize publishes from
306 // different upstream brokers).
306 // different upstream brokers).
307 defer func() {
307 defer func() {
308+ // Use a context detached from the run context: workspace
309+ // shutdown cancels ctx before this goroutine returns, but the
310+ // buffered streaming deltas must still land before the DB is
311+ // closed. A short timeout bounds the flush.
312+ flushCtx, flushCancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second)
308− if flushErr := a.messages.FlushAll(ctx); flushErr != nil {
314+ if flushErr := a.messages.FlushAll(flushCtx); flushErr != nil {
309 slog.Error("Failed to flush pending message updates after run", "error", flushErr)
315 slog.Error("Failed to flush pending message updates after run", "error", flushErr)
311 if skipRunComplete {
317 if skipRunComplete {
@@ -577,11 +583,20 @@ func (a *sessionAgent) Run(ctx context.Context, call SessionAgentCall) (result *
577 if currentAssistant == nil {
583 if currentAssistant == nil {
578 return result, err
584 return result, err
586+ // Persist final state with a context detached from the run
587+ // context. The run context (ctx) is derived from the
588+ // workspace context, which workspace shutdown cancels before
589+ // agent goroutines finish; using ctx here would drop the
590+ // final assistant state. WithoutCancel keeps the values
591+ // (e.g. session ID) while ignoring cancellation, and a short
592+ // timeout bounds the cleanup writes.
593+ cleanupCtx, cleanupCancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second)
594+ defer cleanupCancel()
580 // Ensure we finish thinking on error to close the reasoning state.
595 // Ensure we finish thinking on error to close the reasoning state.
581 currentAssistant.FinishThinking()
596 currentAssistant.FinishThinking()
582 toolCalls := currentAssistant.ToolCalls()
597 toolCalls := currentAssistant.ToolCalls()
583− // INFO: we use the parent context here because the genCtx has been cancelled.
598+ // INFO: we use the cleanup context here because the genCtx has been cancelled.
584− msgs, createErr := a.messages.List(ctx, currentAssistant.SessionID)
599+ msgs, createErr := a.messages.List(cleanupCtx, currentAssistant.SessionID)
585 if createErr != nil {
600 if createErr != nil {
586 return nil, createErr
601 return nil, createErr
@@ -590,7 +605,7 @@ func (a *sessionAgent) Run(ctx context.Context, call SessionAgentCall) (result *
590 tc.Finished = true
605 tc.Finished = true
591 tc.Input = "{}"
606 tc.Input = "{}"
592 currentAssistant.AddToolCall(tc)
607 currentAssistant.AddToolCall(tc)
593− updateErr := a.messages.Update(ctx, *currentAssistant)
608+ updateErr := a.messages.Update(cleanupCtx, *currentAssistant)
594 if updateErr != nil {
609 if updateErr != nil {
595 return nil, updateErr
610 return nil, updateErr
@@ -623,7 +638,7 @@ func (a *sessionAgent) Run(ctx context.Context, call SessionAgentCall) (result *
623 Content: content,
638 Content: content,
624 IsError: true,
639 IsError: true,
626− _, createErr = a.messages.Create(ctx, currentAssistant.SessionID, message.CreateMessageParams{
641+ _, createErr = a.messages.Create(cleanupCtx, currentAssistant.SessionID, message.CreateMessageParams{
627 Role: message.Tool,
642 Role: message.Tool,
628 Parts: []message.ContentPart{
643 Parts: []message.ContentPart{
629 toolResult,
644 toolResult,
@@ -670,9 +685,9 @@ func (a *sessionAgent) Run(ctx context.Context, call SessionAgentCall) (result *
671 currentAssistant.AddFinish(message.FinishReasonError, defaultTitle, err.Error())
686 currentAssistant.AddFinish(message.FinishReasonError, defaultTitle, err.Error())
673− // Note: we use the parent context here because the genCtx has been
688+ // Note: we use the cleanup context here because the genCtx has been
674 // cancelled.
689 // cancelled.
675− updateErr := a.messages.Update(ctx, *currentAssistant)
690+ updateErr := a.messages.Update(cleanupCtx, *currentAssistant)
676 if updateErr != nil {
691 if updateErr != nil {
677 return nil, updateErr
692 return nil, updateErr