// PicoClaw - Ultra-lightweight personal AI agent package agent import ( "context" "github.com/sipeed/picoclaw/pkg/bus" "github.com/sipeed/picoclaw/pkg/providers" ) // Finalize handles turn finalization, either: // - Early return when allResponsesHandled=true (ExecuteTools already finalized) // - Normal finalization for allResponsesHandled=false (sets finalContent, saves session, compact) func (p *Pipeline) Finalize( ctx context.Context, turnCtx context.Context, ts *turnState, exec *turnExecution, turnStatus TurnEndStatus, finalContent string, ) (turnResult, error) { al := p.al // When allResponsesHandled=true, ExecuteTools already finalized // (added handledToolResponseSummary, saved session, set phase to Completed). // But still check for hard abort - if requested, abort the turn. if exec.allResponsesHandled { if ts.hardAbortRequested() { return al.abortTurn(ts) } ts.setPhase(TurnPhaseCompleted) return turnResult{ finalContent: finalContent, status: turnStatus, followUps: append([]bus.InboundMessage(nil), ts.followUps...), }, nil } ts.setPhase(TurnPhaseFinalizing) ts.setFinalContent(finalContent) if !ts.opts.NoHistory { finalMsg := providers.Message{ Role: "assistant", Content: finalContent, ReasoningContent: responseReasoningContent(exec.response), } ts.agent.Sessions.AddFullMessage(ts.sessionKey, finalMsg) ts.recordPersistedMessage(finalMsg) ts.ingestMessage(turnCtx, al, finalMsg) if err := ts.agent.Sessions.Save(ts.sessionKey); err != nil { al.emitEvent( EventKindError, ts.eventMeta("runTurn", "turn.error"), ErrorPayload{ Stage: "session_save", Message: err.Error(), }, ) return turnResult{status: TurnEndStatusError}, err } } if ts.opts.EnableSummary { al.contextManager.Compact( turnCtx, &CompactRequest{ SessionKey: ts.sessionKey, Reason: ContextCompressReasonSummarize, Budget: ts.agent.ContextWindow, }, ) } ts.setPhase(TurnPhaseCompleted) return turnResult{ finalContent: finalContent, status: turnStatus, followUps: append([]bus.InboundMessage(nil), ts.followUps...), }, nil }