# TodoListMiddleware vs Other Planning Tools in DeepAgents: Key Functional Differences

> Discover the functional differences between TodoListMiddleware and other planning tools in DeepAgents. Learn how TodoListMiddleware's dedicated checklist API contrasts with auxiliary workflows in SubAgentMiddleware and HumanInT...

- Repository: [LangChain/deepagents](https://github.com/langchain-ai/deepagents)
- Tags: comparison
- Published: 2026-03-17

---

**TodoListMiddleware provides a dedicated, stateful checklist API through the `write_todos` tool with strict single-call-per-turn enforcement, while other planning middleware like SubAgentMiddleware and HumanInTheLoopMiddleware support auxiliary workflows such as delegation and human approval without maintaining a persistent todo list structure.**

The `langchain-ai/deepagents` repository offers multiple middleware components for agent orchestration, but understanding the functional difference between TodoListMiddleware and other planning tools is critical for architecting effective agent workflows. While all middleware layers contribute to task execution, TodoListMiddleware stands apart as the only component designed specifically for structured self-planning via a persistent checklist mechanism.

## Primary Purpose and API Design

### Dedicated Planning Interface vs. Auxiliary Support

**TodoListMiddleware** supplies a *single* planning tool—`write_todos`—that lets the LLM break a higher-level goal into a concrete checklist and stores that checklist in the agent’s state. This creates a formal planning API that the LLM can invoke to structure its approach to complex tasks.

Other planning-related middleware provides *support* for planning without offering a dedicated planning API:

- **SubAgentMiddleware**: Enables sub-agents to perform their own independent planning
- **HumanInTheLoopMiddleware**: Allows human approval of plans before execution
- **InterruptOnConfig**: Stops execution based on configuration flags rather than explicit planning

## State Management and Persistence

TodoListMiddleware maintains a `todos` list inside the agent’s **AgentState**. This list persists across conversation turns and remains queryable via the `todos` field of the agent’s output. The state structure specifically tracks task content, status, and active form data.

Other middleware components use state for different concerns:

- **FilesystemMiddleware** tracks a virtual file system
- **SummarizationMiddleware** maintains a running conversation summary
- **SubAgentMiddleware** manages each sub-agent’s independent runtime

None of these alternatives expose a dedicated "planning list" or structured task tracking mechanism.

## Tool-Call Contracts and Constraints

### Single-Write Enforcement

TodoListMiddleware strictly validates that `write_todos` is called **once per model turn** maximum. If the LLM attempts multiple parallel calls to `write_todos` within a single `AIMessage`, the middleware returns error-type tool messages. This constraint is enforced in the test suite at [`libs/deepagents/tests/unit_tests/test_todo_middleware.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/tests/unit_tests/test_todo_middleware.py).

Other middleware imposes no such restrictions. Tools like `write_file` or `summarize` can be invoked any number of times per turn unless the underlying tool implementation specifically limits usage.

## Output Formatting and UI Integration

After a successful `write_todos` call, TodoListMiddleware formats the todo list as a **markdown-style checklist** that the CLI UI renders distinctly. The mapping in [`libs/cli/deepagents_cli/widgets/messages.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/cli/deepagents_cli/widgets/messages.py) specifically handles `"write_todos": self._format_todos_output` to produce the visual checklist representation.

Other middleware returns raw tool results (such as file contents) or internal data structures that the UI formats differently, without the specialized checklist rendering logic.

## Integration in the Agent Graph

Both TodoListMiddleware and other planning tools are inserted into the default middleware stack by [`deepagents/deepagents/graph.py`](https://github.com/langchain-ai/deepagents/blob/main/deepagents/deepagents/graph.py) (approximately lines 190-260). TodoListMiddleware is instantiated alongside FilesystemMiddleware, SubAgentMiddleware, and others, but each contributes a different set of tools and lifecycle hooks to the agent graph.

## Usage Patterns and Code Examples

### Using TodoListMiddleware for Structured Planning

```python
from langchain.agents import create_agent
from langchain.agents.middleware import TodoListMiddleware
from langchain_core.messages import HumanMessage

# Create an agent with the todo-list middleware

agent = create_agent(
    model=my_llm,                     # any LangChain-compatible model

    middleware=[TodoListMiddleware()],  # adds the `write_todos` tool

)

# Ask the agent to plan a task

result = agent.invoke(
    {"messages": [HumanMessage(content="Plan a weekend hackathon")]},
)

# The result contains a formatted todo list and a `todos` field

print(result["todos"])       # → list[dict] with `content`, `status`, `activeForm`

print(result["messages"][-1].content)   # markdown checklist shown in the UI

```

### Using Other Middleware for Planning-Related Behavior

```python
from langchain.agents import create_agent
from deepagents.middleware.subagents import SubAgentMiddleware
from deepagents.middleware.human_in_the_loop import HumanInTheLoopMiddleware
from deepagents.middleware.interrupt_on_config import InterruptOnConfig
from langchain_core.messages import HumanMessage

# Build an agent that can delegate sub-goals and pause for human approval

agent = create_agent(
    model=my_llm,
    middleware=[
        SubAgentMiddleware(),                     # lets the LLM spin up sub-agents

        HumanInTheLoopMiddleware(interrupt_on={"write_file": True}),
        InterruptOnConfig({"max_steps": 10}),
    ],
)

# The LLM may now call `write_file` as a tool; before it runs the call,

# the HumanInTheLoopMiddleware will surface a confirmation prompt.

result = agent.invoke({"messages": [HumanMessage(content="Create a README file")]})

```

## Summary

- **TodoListMiddleware** is a dedicated planning component that gives the LLM a structured way to create, store, and retrieve a checklist via the `write_todos` tool, enforcing strict one-call-per-turn safety guarantees.
- The other middleware pieces in DeepAgents support auxiliary planning workflows—such as sub-agent delegation, human approvals, and execution interruption—but do not provide a built-in checklist or persistent task state.
- TodoListMiddleware specifically handles markdown formatting for UI rendering through [`libs/cli/deepagents_cli/widgets/messages.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/cli/deepagents_cli/widgets/messages.py), while other middleware returns raw results.
- The implementation validates single-write constraints in [`libs/deepagents/tests/unit_tests/test_todo_middleware.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/tests/unit_tests/test_todo_middleware.py) to prevent parallel planning calls.

## Frequently Asked Questions

### Can TodoListMiddleware be used alongside SubAgentMiddleware?

Yes. Both middleware components are designed to work together in the same agent configuration. TodoListMiddleware provides the parent agent with structured planning capabilities via `write_todos`, while SubAgentMiddleware allows the agent to delegate specific sub-goals to specialized child agents that may employ their own planning strategies.

### Why does TodoListMiddleware restrict write_todos to one call per turn?

The single-write enforcement prevents the LLM from generating conflicting or redundant task lists within a single inference step. According to the source code in [`langchain/agents/middleware.py`](https://github.com/langchain-ai/deepagents/blob/main/langchain/agents/middleware.py), this constraint ensures that the state maintains a coherent, sequential checklist progression rather than competing parallel plans.

### Where is the TodoListMiddleware implementation located?

While DeepAgents integrates TodoListMiddleware into its graph at [`deepagents/deepagents/graph.py`](https://github.com/langchain-ai/deepagents/blob/main/deepagents/deepagents/graph.py), the actual middleware class implementation resides in the LangChain repository at [`langchain/agents/middleware.py`](https://github.com/langchain-ai/deepagents/blob/main/langchain/agents/middleware.py). The DeepAgents-specific tests and CLI rendering logic are located at [`libs/deepagents/tests/unit_tests/test_todo_middleware.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/tests/unit_tests/test_todo_middleware.py) and [`libs/cli/deepagents_cli/widgets/messages.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/cli/deepagents_cli/widgets/messages.py) respectively.

### How does the UI render todo lists differently from other tool outputs?

The DeepAgents CLI specifically maps the `write_todos` tool output to a dedicated formatting method `_format_todos_output` in the messages widget, producing a markdown checklist visualization. Other tool results—such as file contents from `write_file` or summaries from SummarizationMiddleware—render as raw text or specialized data structures without the checkbox-style formatting.