diff --git a/lib/crewai/src/crewai/experimental/conversational_mixin.py b/lib/crewai/src/crewai/experimental/conversational_mixin.py index e3da7b8c9..119ab2ac3 100644 --- a/lib/crewai/src/crewai/experimental/conversational_mixin.py +++ b/lib/crewai/src/crewai/experimental/conversational_mixin.py @@ -153,6 +153,9 @@ class _ConversationalMixin: def kickoff(self, *args: Any, **kwargs: Any) -> Any: pass + def _persist_method_completion(self, method_name: Any) -> None: + pass + @property def method_outputs(self) -> list[Any]: pass @@ -330,6 +333,7 @@ class _ConversationalMixin: and self._is_public_turn_result(result) ): self.append_assistant_message(self._stringify_result(result)) + self._persist_state_after_fallback_append() except Exception as exc: failed_event = ConversationTurnFailedEvent( type="conversation_turn_failed", @@ -419,6 +423,7 @@ class _ConversationalMixin: and self._is_public_turn_result(result) ): self.append_assistant_message(self._stringify_result(result)) + self._persist_state_after_fallback_append() except HumanFeedbackPending as exc: return exc except Exception as exc: @@ -879,6 +884,23 @@ class _ConversationalMixin: self._is_execution_resuming = False object.__setattr__(self, "_turn_classified_intent", None) + def _persist_state_after_fallback_append(self) -> None: + """Re-persist after fallback append so restore sees the assistant reply. + + Per-method snapshots inside ``kickoff`` run before this fallback, so + return-only ``@listen`` handlers need one more save for restore to see + the assistant message on the next turn. + """ + if not self._method_outputs: + return + last_entry = self._method_outputs[-1] + if not isinstance(last_entry, dict) or not last_entry.get("method"): + return + + from crewai.flow.types import FlowMethodName + + self._persist_method_completion(FlowMethodName(str(last_entry["method"]))) + def _apply_pending_conversational_turn(self) -> None: """Drain the stashed user message + classify if intents configured.