Sagents.Todo (Sagents v0.8.0-rc.10)

Copy Markdown

TODO item structure for task tracking.

TODOs help agents break down complex tasks into manageable steps and track progress through multi-step workflows.

Identity

Each TODO's id is a positive integer that is unique within a single list. There is no global ID space — IDs only need to distinguish items within the same conversation's todo list. When building a list, prefer list_from_maps/1, which assigns positional IDs (1..N) for any item that doesn't supply one.

Status Values

  • :pending - Task not yet started
  • :in_progress - Currently being worked on
  • :completed - Task finished successfully
  • :cancelled - Task no longer needed

Usage

# Create a single TODO (id is required)
{:ok, todo} = Todo.new(%{id: 1, content: "Implement user authentication"})

# Build a list, letting positional defaults assign IDs
{:ok, todos} = Todo.list_from_maps([
  %{"content" => "Task A", "status" => "pending"},
  %{"content" => "Task B", "status" => "pending"}
])
# todos[0].id == 1, todos[1].id == 2

Summary

Functions

Create a single TODO from a map (for deserialization).

Build a list of TODOs from an ordered list of maps.

Create a new TODO item with validation.

Create a new TODO item, raising on error.

Convert a TODO struct to a map for serialization.

Types

t()

@type t() :: %Sagents.Todo{
  content: String.t(),
  id: integer(),
  status: :pending | :in_progress | :completed | :cancelled
}

Functions

from_map(map)

Create a single TODO from a map (for deserialization).

Coerces a stringified integer id to an integer. A missing or non-numeric id is an error from this entry point; callers handling a whole list should use list_from_maps/1 to get positional ID defaults.

Examples

{:ok, todo} = Todo.from_map(%{"id" => 1, "content" => "Task", "status" => "pending"})
{:ok, todo} = Todo.from_map(%{"id" => "1", "content" => "Task"})

list_from_maps(todos)

Build a list of TODOs from an ordered list of maps.

This is the canonical ingest point for any list of incoming todo data: tool calls, persisted state, conversation rehydrate. It guarantees every TODO has a positive integer id by assigning positional defaults:

  • Missing, nil, or non-numeric id → id = index + 1 (1-based).
  • Positive integer id → kept as is.
  • Numeric-string id (e.g. "5", "01") → coerced to integer.

The non-numeric fallback exists for backward compatibility with snapshots persisted before IDs were typed as integers (which used random base64 strings). Those legacy IDs lose their original identity on reload, but in-list order is preserved.

Returns {:ok, [Todo.t()]} if every item parses, or {:error, reason} on the first failure.

new(attrs \\ %{})

Create a new TODO item with validation.

Requires an explicit positive-integer id. There is no auto-generation — callers building a list should use list_from_maps/1 instead, which assigns positional IDs.

Stringified integer ids (e.g. "5") are coerced to integers so payloads arriving from JSON tool calls keep working.

Examples

{:ok, todo} = Todo.new(%{id: 1, content: "Write tests"})
{:ok, todo} = Todo.new(%{id: "1", content: "Write tests"})  # coerced

new!(attrs \\ %{})

Create a new TODO item, raising on error.

Examples

todo = Todo.new!(%{id: 1, content: "Deploy to production"})

to_map(todo)

Convert a TODO struct to a map for serialization.

Examples

todo = Todo.new!(%{id: 1, content: "Task"})
map = Todo.to_map(todo)
# => %{"id" => 1, "content" => "Task", "status" => "pending"}