Skip to main content
The TodoTools collection manages the plan (the Planning panel in the UI): the agent’s own execution ledger of what it has done and what remains. One instance is created per thread, bound to its thread_id, so every tool targets the right conversation without thread_id being an LLM-facing parameter. The plan is an arbitrarily-nested, depth-first (pre-order) tree. Every step is addressed by a stable 1-based flat index (its position in the pre-order list, exactly what list_todos prints). The dotted outline numbers (1, 1.1, 1.1.1, 2) are display-only; the tools always take the flat index. Every mutating tool is batch-shaped: it takes a list so the agent can act on many steps in one call (pass a one-element list to act on a single step). There are deliberately no singular variants. Shared conventions (the @workspace_tool contract, the {success, summary} JSON envelope, and the injected action_and_reasoning argument) are documented in Tools. Every tool below also receives the injected action_and_reasoning argument described there; it is not listed as an argument row. Source: backend/app/tools/todos.py

add_todos

Add one or more steps to the plan as a nested outline, either appended at the end or nested under an existing step. Description shown to the agent:
Add todo steps to the plan in one call, passed as a nested outline (each step has text and a depth). Pass parent_index to nest the whole outline as sub-steps under an existing todo step; omit it to append at the end as top-level steps. Use this for every add, pass a one-element list to add a single step.
Arguments Each item in steps is a PlanStep object: Returns: a confirmation string with the number of steps added, e.g. added 3 todo steps. This becomes the summary of the success envelope. Implementation: maps each PlanStep to {text, depth} and calls WorkspaceRepository.add_todos_bulk(thread_id, items, parent_index=...), which inserts the outline into the pre-order tree at the chosen position. Source: backend/app/tools/todos.py.

toggle_todos

Flip one or more steps between done and not-done. Description shown to the agent:
Toggle one or more todo steps done / not-done by their 1-based indices (pass a list; a single step is a one-element list).
Arguments Returns: a summary of the resulting statuses, e.g. toggled 2 todo steps: #1 now done, #2 now pending, or no matching todo steps found when no index is in range. Implementation: reads the current plan, resolves each in-range index to its row id up front (so the indices reference the plan as it stood at call time), then calls repo.toggle_todo(todo_id) per target. Source: backend/app/tools/todos.py.

update_todos

Rewrite the text of one or more existing steps, keeping their position, depth, and done status. Description shown to the agent:
Rewrite the text of one or more existing todo steps. Pass a list of {index, text}; a single edit is a one-element list.
Arguments Each item in updates is a TodoTextUpdate object: Returns: a confirmation string with the number of steps updated, e.g. updated 2 todo steps, or no matching todo steps found when no index is in range. Implementation: resolves indices against the plan at call time (before any edit, so they do not shift as steps are rewritten), then calls repo.update_todo_text(todo_id, text) per target. Source: backend/app/tools/todos.py.

remove_todos

Delete one or more steps; removing a parent step also removes all of its sub-steps. Description shown to the agent:
Delete one or more todo steps by their 1-based indices (pass a list; a single step is a one-element list). NOTE: removing a parent todo step also removes ALL of its sub-steps. Use when the plan changes and todo steps no longer apply.
Arguments Returns: a confirmation string with the total number of steps removed, e.g. removed 4 todo steps, or no matching todo steps found when no index is in range. Implementation: resolves all target row ids up front, then calls repo.remove_todo_subtree(thread_id, todo_id) per target, which cascades to the step’s descendants. Because removal cascades, an index that named a sub-step already taken out by its parent is simply skipped. Source: backend/app/tools/todos.py.

list_todos

Read the current plan with its numbers, nesting, and statuses. Rarely needed, since the plan is injected into the agent’s context every turn. Description shown to the agent:
List the current plan with numbers, nesting, and statuses.
Arguments None (the agent still passes the injected action_and_reasoning). Returns: newline-separated lines of the form #N D.D [x] text, where N is the 1-based flat index that every todo tool expects and D.D is the dotted outline number reflecting nesting ([x] marks done, [ ] pending), or (no todo steps yet) when the plan is empty. Implementation: reads repo.list_todos(thread_id) and renders each row through compute_dotted_numbers (in _planning_format.py) to produce the dotted outline numbers. Source: backend/app/tools/todos.py.