# Bug Report: Subagents lack checkpoint persistence and state query truncates to…ol execution history
## Summary
Subagents in deepagents do not have checkpoint persistence enabled, and when querying state, the tool execution records from subagents are truncated. This is inconsistent with LangGraph's documented behavior for subgraph checkpointing.
## Problem Description
### Issue 1: Subagents compiled without checkpointer
According to [LangGraph documentation](https://langchain-ai.github.io/langgraph/how-tos/subgraph/), subgraphs can have their own memory:
```python
subgraph_builder = StateGraph(...)
subgraph = subgraph_builder.compile(checkpointer=True)
```
And you can check subgraph state using:
```python
graph.get_state(config, subgraphs=True)
```
However, in the current deepagents implementation, **subagents are compiled without any checkpointer**, making it impossible to:
- Persist subagent execution history
- Query subagent state using `get_state(subgraphs=True)`
- Track subagent's internal message history in multi-agent systems
### Issue 2: Tool execution history truncated
When a subagent completes execution, only the final text message is returned to the parent agent. All intermediate tool calls, tool results, and reasoning steps are discarded.
## Root Cause Analysis
### 1. Subagents compiled without checkpointer parameter
**File:** `libs/deepagents/deepagents/middleware/subagents.py`
**Lines 244-249** (general-purpose subagent):
```python
general_purpose_subagent = create_agent(
default_model,
system_prompt=DEFAULT_SUBAGENT_PROMPT,
tools=default_tools,
middleware=general_purpose_middleware,
) # ❌ No checkpointer parameter
```
**Lines 270-275** (custom subagents):
```python
agents[agent_["name"]] = create_agent(
subagent_model,
system_prompt=agent_["system_prompt"],
tools=_tools,
middleware=_middleware,
) # ❌ No checkpointer parameter
```
**Compare to main agent** (`libs/deepagents/deepagents/graph.py:149-161`):
```python
return create_agent(
model,
system_prompt=...,
tools=tools,
middleware=deepagent_middleware,
checkpointer=checkpointer, # ✅ Main agent has checkpointer
store=store,
...
)
```
### 2. Timing issue prevents checkpointer propagation
**File:** `libs/deepagents-cli/deepagents_cli/agent.py:416`
```python
agent = create_deep_agent(
...
middleware=agent_middleware,
interrupt_on=interrupt_on,
).with_config(config)
agent.checkpointer = InMemorySaver() # ⚠️ Assigned AFTER subagents are compiled
```
**Problem sequence:**
1. `create_deep_agent()` is called (checkpointer=None at this point)
2. Inside `create_deep_agent()`, `SubAgentMiddleware` is initialized
3. `SubAgentMiddleware.__init__()` immediately calls `_get_subagents()` and compiles all subagents
4. Subagents are compiled with checkpointer=None
5. THEN the main agent gets assigned `InMemorySaver()`
6. By this time, subagents are already compiled and cannot retroactively get the checkpointer
### 3. Message history truncation
**File:** `libs/deepagents/deepagents/middleware/subagents.py:315-322`
```python
def _return_command_with_state_update(result: dict, tool_call_id: str) -> Command:
state_update = {k: v for k, v in result.items() if k not in _EXCLUDED_STATE_KEYS}
return Command(
update={
**state_update,
"messages": [ToolMessage(result["messages"][-1].text, tool_call_id=tool_call_id)],
# ^^^^^^^^^^^^^^^^^^^^^^^^^^
# Only returns the LAST message's text!
}
)
```
When subagents execute (lines 348/363):
```python
result = subagent.invoke(subagent_state) # or await subagent.ainvoke(...)
```
The `result["messages"]` contains the full conversation history:
- User input (HumanMessage)
- AI tool call requests (AIMessage with tool_calls)
- Tool execution results (ToolMessage)
- AI final response (AIMessage)
**But only the last message's text is returned**, losing:
- ❌ All tool names called by subagent
- ❌ Tool call parameters
- ❌ Tool execution results
- ❌ Intermediate reasoning steps
- ✅ Only final text output is preserved
### 4. State isolation
**File:** `libs/deepagents/deepagents/middleware/subagents.py:64, 328-329`
```python
_EXCLUDED_STATE_KEYS = ("messages", "todos")
subagent_state = {k: v for k, v in runtime.state.items() if k not in _EXCLUDED_STATE_KEYS}
subagent_state["messages"] = [HumanMessage(content=description)]
```
This prevents subagents from accessing the parent's message history and todos.
## Impact
| Feature | Expected (LangGraph docs) | Current Implementation | Gap |
|---------|--------------------------|------------------------|-----|
| Subagent checkpointer | `compile(checkpointer=True)` | `compile()` no checkpointer | ❌ Completely missing |
| Subagent state persistence | Each tool call recorded | Not persisted, memory only | ❌ Cannot retrieve history |
| `get_state(subgraphs=True)` | Returns subgraph state | Subgraph has no checkpoint | ❌ Feature unavailable |
| Tool execution history | Preserved in messages | Only final text retained | ❌ Loses intermediate steps |
## Affected Code Locations
| Issue | File | Lines | Severity |
|-------|------|-------|----------|
| General-purpose subagent no checkpointer | `libs/deepagents/deepagents/middleware/subagents.py` | 244-249 | 🔴 Critical |
| Custom subagents no checkpointer | `libs/deepagents/deepagents/middleware/subagents.py` | 270-275 | 🔴 Critical |
| Main agent checkpointer late assignment | `libs/deepagents-cli/deepagents_cli/agent.py` | 416 | 🔴 Critical |
| Message history truncation | `libs/deepagents/deepagents/middleware/subagents.py` | 320 | 🔴 Critical |
| State keys hard-coded exclusion | `libs/deepagents/deepagents/middleware/subagents.py` | 64, 328 | ⚠️ Medium |
## Proposed Solutions
### Solution 1: Pass checkpointer to subagents
Modify `_get_subagents()` to accept and pass checkpointer:
```python
def _get_subagents(
...,
checkpointer: Checkpointer | None = None, # Add parameter
) -> ...:
...
general_purpose_subagent = create_agent(
default_model,
system_prompt=DEFAULT_SUBAGENT_PROMPT,
tools=default_tools,
middleware=general_purpose_middleware,
checkpointer=checkpointer, # Pass to subagent
)
# Similarly for custom subagents
agents[agent_["name"]] = create_agent(
...,
checkpointer=checkpointer,
)
```
### Solution 2: Initialize checkpointer before creating agent
In `libs/deepagents-cli/deepagents_cli/agent.py`:
```python
checkpointer = InMemorySaver() # Create first
agent = create_deep_agent(
...
checkpointer=checkpointer, # Pass during creation
middleware=agent_middleware,
interrupt_on=interrupt_on,
).with_config(config)
```
### Solution 3: Preserve subagent execution history
Option A: Return full message history
```python
def _return_command_with_state_update(result: dict, tool_call_id: str) -> Command:
return Command(
update={
**state_update,
"messages": result["messages"], # Return all messages
}
)
```
Option B: Store subagent execution in metadata
```python
"messages": [ToolMessage(
content=result["messages"][-1].text,
tool_call_id=tool_call_id,
additional_kwargs={"subagent_history": result["messages"]},
)]
```
## Environment
- deepagents version: [latest from master branch as of 2025-12-15]
- Commit: `fce8aa6` (chore(harbor): add some tracing, remove max_iterations)
- Python version: 3.11
- Platform: macOS
## Steps to Reproduce
1. Create a deepagent with subagents
2. Execute a task that invokes a subagent with multiple tool calls
3. Query the state: `agent.get_state(config, subgraphs=True)`
4. Observe:
- Subgraph state is empty/unavailable (no checkpoint)
- Parent agent's messages only show final subagent output text
- All intermediate tool calls are lost
## Expected Behavior
- Subagents should be compiled with `checkpointer=True` (or shared checkpointer)
- `agent.get_state(config, subgraphs=True)` should return subagent's internal state
- Subagent's tool execution history should be preserved and queryable
- Multi-agent systems should be able to track each agent's message history independently
## Actual Behavior
- Subagents have no checkpointer
- `get_state(subgraphs=True)` cannot retrieve subagent history
- Only final text output from subagents is preserved
- Tool calls made by subagents are lost after execution completes
---
**Related Documentation:**
- [LangGraph Subgraph Documentation](https://langchain-ai.github.io/langgraph/how-tos/subgraph/)
- LangGraph checkpoint documentation on subgraph persistence