diff --git a/src/praisonai-agents/praisonaiagents/agent/tool_execution.py b/src/praisonai-agents/praisonaiagents/agent/tool_execution.py index dcd98195f..4f45973bf 100644 --- a/src/praisonai-agents/praisonaiagents/agent/tool_execution.py +++ b/src/praisonai-agents/praisonaiagents/agent/tool_execution.py @@ -2088,12 +2088,17 @@ class ToolExecutionMixin: else: return response if final != response and isinstance(response, str): - # Match by content, not position: a concurrent turn may have - # appended a newer assistant message after this one. + # Prefer this turn's own messages (by identity) so a concurrent + # turn with identical text is never touched; fall back to a + # content match over shared history for untracked callers. + from .memory_mixin import _active_turn_owned + owned = _active_turn_owned.get() or [] with self._history_lock: - for msg in reversed(self.chat_history): - if (isinstance(msg, dict) and msg.get("role") == "assistant" - and msg.get("content") == response): + for candidates in (owned, self.chat_history): + msg = next((m for m in reversed(candidates) + if isinstance(m, dict) and m.get("role") == "assistant" + and m.get("content") == response), None) + if msg is not None: msg["content"] = final break return final diff --git a/src/praisonai-agents/tests/unit/plugins/test_after_agent_block.py b/src/praisonai-agents/tests/unit/plugins/test_after_agent_block.py index 74bb76db1..68a3c3ff5 100644 --- a/src/praisonai-agents/tests/unit/plugins/test_after_agent_block.py +++ b/src/praisonai-agents/tests/unit/plugins/test_after_agent_block.py @@ -132,6 +132,21 @@ class TestAgentAfterAgentEnforcement: assert all(SECRET not in str(m.get("content")) for m in agent.chat_history) assert agent.chat_history[-1]["content"] == "concurrent reply" + def test_block_scrubs_own_turn_not_identical_concurrent_turn(self): + agent = _agent(RaiseBlockPlugin()) + own = {"role": "assistant", "content": f"SSN {SECRET}"} + # A concurrent (untracked here) turn appends an identical answer later. + other = {"role": "assistant", "content": f"SSN {SECRET}"} + token = agent._begin_turn_tracking() + try: + agent._append_to_chat_history(own) + agent.chat_history.append(other) + out = agent._trigger_after_agent_hook("q", f"SSN {SECRET}", time.time()) + finally: + agent._end_turn_tracking(token) + assert own["content"] == out + assert other["content"] == f"SSN {SECRET}" + class EmailRedactPlugin(Plugin): @property