-
-
Notifications
You must be signed in to change notification settings - Fork 550
Description
Description
When a parent (supervisor) agent delegates a task to a sub-agent via delegate_task, and the sub-agent has a memory instance configured, the sub-agent persists its own user message (the delegated task text) and assistant response into the same conversationId as the parent agent. On subsequent turns, the parent's prepareMessages() loads these extra messages, causing the LLM to see "phantom" user messages it did not originate.
This results in the LLM re-answering prior prompts or producing confused responses in multi-turn conversations.
Steps to Reproduce
- Create a supervisor agent with
memoryconfigured - Create a sub-agent also with
memoryconfigured (sharing the same Memory instance or separate — both reproduce) - Register the sub-agent on the supervisor via
subAgents: [mySubAgent] - Call
supervisorAgent.streamText(prompt, { conversationId: "conv-1", userId: "user-1" }) - The supervisor delegates to the sub-agent via
delegate_task - Inspect the memory store — two user messages now exist for the same turn:
- The original user message (from the supervisor's
streamTextcall) - The delegated task text (stored by the sub-agent when it processes the delegation)
- The original user message (from the supervisor's
- Send a second message to the same
conversationId - The supervisor now sees both user messages in history and the LLM attempts to answer both
Expected Behavior
Sub-agent delegation should not pollute the parent conversation's memory. Only the supervisor's user/assistant messages should be persisted under the parent conversationId. Sub-agent internal messages should either:
- Not be persisted at all, or
- Be persisted under a separate
conversationId, or - Be automatically filtered out when loading parent conversation history
Actual Behavior
SubAgentManager.handoffTask() passes the parent's conversationId directly to the sub-agent:
const handoffConversationId = conversationId || crypto.randomUUID();
// ...
const baseOptions = {
conversationId: handoffConversationId,
userId,
// ...
};The sub-agent's streamText() then stores messages under this same conversationId, creating duplicates.
Root Cause
In packages/core/src/agent/subagent/index.ts, handoffTask() reuses the parent's conversationId for the sub-agent call. When the sub-agent has memory, its prepareMessages() → memory load → generate → memory persist pipeline treats the delegated task as a new user message in the parent's conversation.
The includeAgentsMemory supervisor config is read-side only (controls what appears in the supervisor's system prompt) and does not prevent sub-agents from writing to memory.
The metadata.subAgentId / metadata.subAgentName fields added in PR #786 enable post-hoc filtering but are not automatically used during message loading.
Workaround
Remove memory from sub-agents. Since sub-agents are typically stateless task executors, they don't need conversation persistence:
// Before (broken)
const subAgent = new Agent({ name: "subAgent", memory, tools: [...] });
// After (workaround)
const subAgent = new Agent({ name: "subAgent", tools: [...] });Suggested Fix
One or more of:
- Skip memory persistence for delegated calls: When
parentAgentIdis set on theOperationContext(which already happens during delegation), skip memory persistence in the sub-agent - Use a separate conversationId for sub-agents: Generate a child
conversationId(e.g.,sub-${parentConversationId}-${subAgentName}-${uuid}) instead of reusing the parent's - Auto-filter on read: When loading messages for the parent agent, automatically filter out messages where
metadata.subAgentNameis set
Environment
@voltagent/core: latest (installed from npm)- Memory adapter:
@voltagent/libsql(LibSQLMemoryAdapter) - Node.js 22
Related
- [FEAT] Saves subAgentId and subAgentName to the database #775 / PR fix: ensure sub-agent metadata is persisted alongside supervisor history #786 — Added
subAgentId/subAgentNamemetadata to persisted sub-agent messages - [BUG] Sub-agent context sharing not working correctly in sequential workflows #30 — Sub-agent context sharing