Agent setup_agent permanently writes state_prompt into ChatMemoryBuffer history
Description
When a workflow agent runs with initial_state / state_prompt and the default ChatMemoryBuffer, setup_agent mutates the stored user ChatMessage in place. The formatted "Current state: … Current message: …" string becomes the permanent user turn in memory, so later LLM context, analytics, and resumed chats permanently contain workflow state (and lose a clean user utterance).
This is distinct from the within-run nesting fix (formatted_input_with_state / related issues): the corruption survives across the run into memory.get().
Environment
llama-index-core==0.14.25- Python 3.13
Minimal repro
import asyncio
from llama_index.core.agent.workflow import ReActAgent
from llama_index.core.llms.mock import MockLLM
from llama_index.core.memory import ChatMemoryBuffer
def ping(x: str) -> str:
"""Echo the input string."""
return x
async def main() -> None:
llm = MockLLM(max_tokens=32)
memory = ChatMemoryBuffer.from_defaults(token_limit=5000)
agent = ReActAgent(
name="agent",
llm=llm,
tools=[ping],
initial_state={"user_id": "u-42", "plan": "secret-plan"},
streaming=False,
timeout=60,
)
try:
await agent.run(user_msg="What is 2+2?", memory=memory, max_iterations=1)
except Exception:
pass # mutation happens in setup_agent before max-iterations
content = memory.get()[0].content or ""
print(repr(content))
assert "secret-plan" in content # permanently written into history
asyncio.run(main())Expected
Memory keeps the user text "What is 2+2?". State injection should only affect the ephemeral LLM prompt.
Actual
Stored user message becomes:
Current state:
{'user_id': 'u-42', 'plan': 'secret-plan'}
Current message:
What is 2+2?Root cause (pointer)
In BaseWorkflowAgent.setup_agent, block.text = self.state_prompt.format(...) mutates llm_input[-1] in place. ChatMemoryBuffer.get() returns the same ChatMessage objects held in the store, so the mutation persists. Using SQL Memory does not write the mutated objects back, but agents default to ChatMemoryBuffer.
Suggested fix direction
Copy-on-write before applying state_prompt (mutate a shallow copy of the message / text block for the LLM call only), or otherwise ensure memory never receives the state-prefixed text.
Source: run-llama/llama_index