TodoListMiddleware vs Other Planning Tools in DeepAgents: Key Functional Differences
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.
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 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 (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
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
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_todostool, 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, while other middleware returns raw results. - The implementation validates single-write constraints in
libs/deepagents/tests/unit_tests/test_todo_middleware.pyto 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, 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, the actual middleware class implementation resides in the LangChain repository at langchain/agents/middleware.py. The DeepAgents-specific tests and CLI rendering logic are located at libs/deepagents/tests/unit_tests/test_todo_middleware.py and 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →