christian-bromann

deepagents-todolist

Using TodoListMiddleware for task planning and tracking progress with the write_todos tool in Deep Agents for complex multi-step workflows.

christian-bromann 3 1 Updated 3mo ago
GitHub

Install

npx skillscat add christian-bromann/langchain-skills/deepagents-todolist

Install via the SkillsCat registry.

SKILL.md

deepagents-todolist (JavaScript/TypeScript)

Overview

TodoListMiddleware provides agents with task planning and progress tracking capabilities through the write_todos tool. It's automatically included in every deep agent and helps agents break down complex, multi-step tasks into manageable pieces.

When to Use TodoList Middleware

Use TodoList When Skip TodoList When
Complex multi-step tasks requiring coordination Simple, single-action tasks
Long-running operations where progress visibility matters Quick operations (< 3 steps)
Tasks that may need plan adaptation Fixed, predetermined workflows

Basic Usage

Default Configuration (Included Automatically)

import { createDeepAgent } from "deepagents";

// TodoListMiddleware is included by default
const agent = await createDeepAgent({});

// Agent will automatically use write_todos for complex tasks
const result = await agent.invoke({
  messages: [{
    role: "user",
    content: "Create a TypeScript web scraper that extracts product data, stores it in a database, and generates a report."
  }]
});

Customizing TodoList Middleware

import { createAgent, todoListMiddleware } from "langchain";

const agent = createAgent({
  model: "claude-sonnet-4-5-20250929",
  middleware: [
    todoListMiddleware({
      systemPrompt: `Use the write_todos tool to plan your work:
      1. Break down the task into 3-5 major steps
      2. Mark tasks as 'in_progress' when you start
      3. Mark tasks as 'completed' when done
      4. Update the list if plans change
      `,
    }),
  ],
});

Decision Table: Todo List Patterns

Task Type Todo List Strategy Example
Sequential steps Create all todos upfront, complete in order Build app: setup → code → test → deploy
Discovery-based Add todos as you learn what's needed Research: initial search → follow-up → synthesis
Parallel work Multiple "in_progress" items allowed Data processing: extract + transform + load

Code Examples

Example 1: Sequential Task Breakdown

import { createDeepAgent } from "deepagents";

const agent = await createDeepAgent({});

const result = await agent.invoke({
  messages: [{
    role: "user",
    content: `Create a REST API for a todo application:
    1. Design the data models
    2. Implement CRUD endpoints
    3. Add authentication
    4. Write tests
    5. Create API documentation
    `
  }]
});

// Agent's internal planning (via write_todos):
// [
//   { content: "Design data models for Todo items", status: "pending" },
//   { content: "Implement CRUD endpoints (GET, POST, PUT, DELETE)", status: "pending" },
//   { content: "Add JWT authentication middleware", status: "pending" },
//   { content: "Write unit and integration tests", status: "pending" },
//   { content: "Generate OpenAPI documentation", status: "pending" }
// ]

Example 2: Custom TodoList Instructions

import { createAgent, todoListMiddleware } from "langchain";
import { tool } from "langchain";
import { z } from "zod";

const runTests = tool(
  async ({ testSuite }: { testSuite: string }) => {
    return `Tests in ${testSuite} passed`;
  },
  {
    name: "run_tests",
    description: "Run a test suite",
    schema: z.object({ testSuite: z.string() }),
  }
);

const deployCode = tool(
  async ({ environment }: { environment: string }) => {
    return `Deployed to ${environment}`;
  },
  {
    name: "deploy_code",
    description: "Deploy code to an environment",
    schema: z.object({ environment: z.string() }),
  }
);

const agent = createAgent({
  model: "gpt-4",
  tools: [runTests, deployCode],
  middleware: [
    todoListMiddleware({
      systemPrompt: `For deployment tasks, always:
      1. Create a todo list with safety checks
      2. Run tests before deployment
      3. Mark each step as completed before proceeding
      `,
    }),
  ],
});

const result = await agent.invoke({
  messages: [{
    role: "user",
    content: "Deploy the application to production"
  }]
});

Example 3: Accessing Todo State

import { createDeepAgent } from "deepagents";

const agent = await createDeepAgent({});

const result = await agent.invoke(
  {
    messages: [{
      role: "user",
      content: "Create a data processing pipeline"
    }]
  },
  { configurable: { thread_id: "session-1" } }
);

// Access the todo list from the final state
const todos = result.todos || [];
for (const todo of todos) {
  console.log(`[${todo.status}] ${todo.content}`);
}

Boundaries

What Agents CAN Do with TodoLists

✅ Create todo lists with custom content and structure
✅ Update todo status (pending → in_progress → completed)
✅ Add new todos as work progresses
✅ Remove todos that become irrelevant
✅ Reorganize or reprioritize todos
✅ Use todos for any task complexity level

What Agents CANNOT Do

❌ Change the tool name from write_todos
❌ Use custom status values (must be pending/in_progress/completed)
❌ Access todos from other threads without the thread_id
❌ Disable TodoListMiddleware in createDeepAgent (it's always included)
❌ Share todos across multiple agents (each agent has its own state)

Gotchas

1. TodoList is Stateful - Requires Thread ID

// ❌ Todo list won't persist without thread_id
await agent.invoke({ messages: [{ role: "user", content: "Task 1" }] });
await agent.invoke({ messages: [{ role: "user", content: "Task 2" }] });

// ✅ Use thread_id for persistence
const config = { configurable: { thread_id: "user-session" } };
await agent.invoke({ messages: [{ role: "user", content: "Task 1" }] }, config);
await agent.invoke({ messages: [{ role: "user", content: "Task 2" }] }, config);

2. TodoList Middleware is Always Present

// You cannot remove TodoListMiddleware from createDeepAgent
// ❌ This won't remove TodoList
const agent = await createDeepAgent({ middleware: [] });  // TodoList still included

// ✅ If you need full control, use createAgent from LangChain
import { createAgent } from "langchain";

const agent2 = createAgent({
  model: "gpt-4",
  middleware: []  // No middleware at all
});

3. TodoList is Optional for Simple Tasks

// The agent won't always use write_todos for simple tasks

// Simple task - agent likely won't create todos
const result1 = await agent.invoke({
  messages: [{ role: "user", content: "What is 2+2?" }]
});
// No todos in state

// Complex task - agent will likely create todos
const result2 = await agent.invoke({
  messages: [{ role: "user", content: "Build a web scraper and analyze the data" }]
});
// Todos present in state

Full Documentation