"""Todo / plan tool — a structured task list the agent maintains and the UI renders. Most of the "organized agent" feel in interactive work. Low risk, auto-approved. The list is held in a `TodoList` the surface can read; `todo_write` replaces it. """ from __future__ import annotations from dataclasses import dataclass, field import aisuite as ai _STATUSES = {"pending", "in_progress", "done"} # Explicit schema — the array-of-objects shape can't be auto-generated reliably, and # providers reject a bare `list` annotation. Registered via `__coworker_schema__`. _TODO_SCHEMA = { "type": "function", "function": { "name": "todo_write", "description": "Replace the task list. Provide the full list of items each call.", "parameters": { "type": "object", "properties": { "items": { "type": "array", "items": { "type": "object", "properties": { "content": {"type": "string"}, "status": { "type": "string", "enum": ["pending", "in_progress", "done"], }, }, "required": ["content", "status"], }, } }, "required": ["items"], }, }, } @dataclass class TodoList: items: list[dict] = field(default_factory=list) def todo_tools(todo: TodoList) -> list: def todo_write(items: list) -> dict: """Replace the task list. Each item is an object with `content` and a `status` of pending, in_progress, or done.""" normalized = [] for entry in items or []: if isinstance(entry, dict): status = entry.get("status", "pending") if status == "completed": # common model alias for our "done" status = "done" normalized.append( { "content": str(entry.get("content", "")), "status": status if status in _STATUSES else "pending", } ) else: normalized.append({"content": str(entry), "status": "pending"}) todo.items = normalized return {"count": len(normalized), "items": normalized} wrapped = ai.tool( todo_write, metadata=ai.ToolMetadata( category="planning", risk_level="low", capabilities=["todo"], ), ) wrapped.__coworker_schema__ = _TODO_SCHEMA return [wrapped]