> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Live Todo Streaming

> See the agent's todo checklist tick off in real time

Subscribe to `TODO_UPDATED` to render a live checklist that updates as the agent adds and ticks off items.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    U[👤 User] --> A[🤖 Agent]
    A --> T[🛠️ Todo Tool]
    T --> E[📡 TODO_UPDATED]
    E --> C[🖥️ Live Checklist]

    classDef user fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef agent fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef event fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff

    class U user
    class A,T agent
    class E event
    class C output
```

The agent adds and updates todos with the built-in todo tool; each change emits a `TODO_UPDATED` event carrying the **full ordered list**, so any subscriber renders the current state without tracking deltas.

## Quick Start

**1. Enable the todo tool**

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

agent = Agent(
    name="Planner",
    instructions="Break tasks into todos, then execute them one by one.",
    tools=["todo_add", "todo_list", "todo_update"],
)
agent.start("Plan a blog launch — research, draft, publish.")
```

**2. Subscribe to live updates**

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent
from praisonaiagents.streaming import StreamEvent, StreamEventType

MARK = {"pending": "[ ]", "in_progress": "[~]", "completed": "[x]", "cancelled": "[-]"}

def on_event(event: StreamEvent):
    if event.type == StreamEventType.TODO_UPDATED:
        for t in event.metadata["todos"]:
            print(f"{MARK[t['status']]} {t['task']}")

agent = Agent(
    name="Planner",
    instructions="Break the task into todos. Mark exactly one as in_progress at a time; complete it before moving on.",
    tools=["todo_add", "todo_list", "todo_update"],
)
agent.stream_emitter.add_callback(on_event)
agent.start("Plan a blog launch — research, draft, publish.")
```

> [!NOTE]
>
> Works for a **solo** `Agent` — no `AgentTeam(planning=True)` required. Any frontend (CLI, TUI, web, Python) can subscribe and render a live checklist.

***

## How It Works

Every `todo_add` and `todo_update` emits a `TODO_UPDATED` event with the complete, ordered list.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant U as 👤 User
    participant A as 🤖 Agent
    participant T as 🛠️ Todo Tool
    participant E as 📡 Emitter
    participant S as 🖥️ Subscriber

    U->>A: "Plan a blog launch"
    A->>T: todo_add("research topic")
    T->>E: TODO_UPDATED (full list)
    E->>S: render checklist
    A->>T: todo_update(1, status="completed")
    T->>E: TODO_UPDATED (full list)
    E->>S: re-render checklist
```

The event carries the **full ordered list**, so subscribers never track deltas — each event is a complete snapshot.

***

## What's in the event

The `TODO_UPDATED` event carries the current list in `metadata`.

| Field | Value |
| - | - |
| `event.type` | `StreamEventType.TODO_UPDATED` |
| `event.metadata["todos"]` | Full ordered list of todo dicts (`id`, `task`, `status`, `priority`, `category`, …) |

Each todo dict looks like:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
    "id": 1,
    "task": "research blog topic",
    "priority": "high",
    "category": "general",
    "status": "in_progress",
}
```

Status is one of `pending` | `in_progress` | `completed` | `cancelled`.

***

## The single-`in_progress` rule

Setting a todo to `in_progress` demotes any other `in_progress` item back to `pending`, so exactly one item is active at a time.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
todo_update(1, status="in_progress")   # todo 1 is now in_progress
todo_update(2, status="in_progress")   # todo 1 auto-demoted to pending; only todo 2 is in_progress
```

This keeps rendered checklists unambiguous — "what is the agent working on right now?" always has one answer. It matches the prompt policy recommended for the `coding` toolset.

***

## Choosing a Renderer

Pick a renderer based on where the checklist appears.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Q{"Where does it render?"} --> P[🖨️ Any TTY]
    Q --> R[🔄 Interactive CLI / TUI]
    Q --> N[🔌 Pipe / web]

    P --> PP["print()"]
    R --> RL["Rich Live"]
    N --> NJ["NDJSON stream"]

    classDef question fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef method fill:#F59E0B,stroke:#7C90A0,color:#fff

    class Q question
    class P,R,N,PP,RL,NJ method
```

| Renderer | Best for |
| - | - |
| Plain `print()` | Quickest — works on any TTY |
| Rich `Live` | In-place refreshing checklist for interactive CLI / TUI |
| NDJSON (`--output json`) | Pipe to a web client — the event serialises through the existing stream emitter |

Rich `Live` re-renders the same block on every event:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from rich.live import Live
from rich.text import Text
from praisonaiagents import Agent
from praisonaiagents.streaming import StreamEvent, StreamEventType

MARK = {"pending": "[ ]", "in_progress": "[~]", "completed": "[x]", "cancelled": "[-]"}
live = Live(Text(""), refresh_per_second=8)
live.start()

def on_event(event: StreamEvent):
    if event.type == StreamEventType.TODO_UPDATED:
        lines = [f"{MARK[t['status']]} {t['task']}" for t in event.metadata["todos"]]
        live.update(Text("\n".join(lines)))

agent = Agent(
    name="Planner",
    instructions="Break the task into todos. Keep exactly one in_progress.",
    tools=["todo_add", "todo_list", "todo_update"],
)
agent.stream_emitter.add_callback(on_event)
agent.start("Plan a blog launch — research, draft, publish.")
live.stop()
```

> [!NOTE]
>
> `TODO_UPDATED` events propagate through the async tool path too — async tools that mutate todos stream live in the same way. No extra wiring is required.

***

## Best Practices

> [accordion] Keep exactly one item in\_progress
>
> Instruct the agent to mark exactly one item `in_progress` and complete it before moving on. The runtime enforces the single-`in_progress` rule, but a clear instruction produces cleaner checklists.

> [accordion] Render the full list, not deltas
>
> Each `TODO_UPDATED` event is a complete snapshot in `metadata["todos"]`. Re-render the whole list every time — the event is idempotent, so you never need to reconcile partial updates.

> [accordion] Cheap when nothing is listening
>
> The emit is a no-op with no active subscriber, so leaving the todo tool enabled costs nothing until a callback is attached.

> [accordion] Combine with TOOL\_PROGRESS for a two-tier view
>
> Render the checklist from `TODO_UPDATED` and the current tool's live output from `TOOL_PROGRESS` for a list-plus-detail view. See [Tool Progress Streaming](https://praison.ai/docs/features/tool-progress-streaming.md).

***

## In AG-UI

Over the AG-UI wire, a `TODO_UPDATED` event reaches the client as a `custom` event named `praisonai.todo_updated` with `value` `{ content, tool_call, metadata }`. See the [AGUI API reference](https://praison.ai/docs/deploy/api/agui-api.md#praisonai-custom-events).

## Related

> [card] [Todo Planning](https://praison.ai/docs/features/todo-planning.md)
>
> Add, list, and update todos with the built-in todo tool

> [card] [Streaming](https://praison.ai/docs/features/streaming.md)
>
> Subscribe to streaming responses and events

> [card] [Tool Progress Streaming](https://praison.ai/docs/features/tool-progress-streaming.md)
>
> Stream incremental output from inside running tools

> [card] [Streaming Progress Compositor](https://praison.ai/docs/features/streaming-progress-compositor.md)
>
> Fold typed StreamEvents into a bounded status view


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.