UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

111 lines (110 loc) • 7.49 kB
import { Type } from "typebox"; import { errorResult } from "../core/tools.js"; import { toolFace } from "../core/tool-catalog-entries.js"; export function createTodoWriteTool() { const lists = new Map(); return { name: "TodoWrite", ...toolFace("todo-write"), description: "Create and update a task list for the current session. The list is rendered to the user as your working plan.\n" + "\n" + '- Each todo has `content`, `status` ("pending" | "in_progress" | "completed"), and `activeForm` (present-tense label shown while in progress).\n' + "- Send the full list each call; it replaces the previous one.\n" + "- Keep one item `in_progress` at a time and mark it `completed` when done.", descriptionClassic: "Use this tool to create and manage a structured task list for your current coding session. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.\n" + "It also helps the user understand the progress of the task and overall progress of their requests.\n" + "\n" + "## When to Use This Tool\n" + "Use this tool proactively in these scenarios:\n" + "\n" + "1. Complex multi-step tasks - When a task requires 3 or more distinct steps or actions\n" + "2. Non-trivial and complex tasks - Tasks that require careful planning or multiple operations\n" + "3. User explicitly requests todo list - When the user directly asks you to use the todo list\n" + "4. User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)\n" + "5. After receiving new instructions - Immediately capture user requirements as todos\n" + "6. When you start working on a task - Mark it as in_progress BEFORE beginning work. Ideally you should only have one todo as in_progress at a time\n" + "7. After completing a task - Mark it as completed and add any new follow-up tasks discovered during implementation\n" + "\n" + "## When NOT to Use This Tool\n" + "\n" + "Skip using this tool when:\n" + "1. There is only a single, straightforward task\n" + "2. The task is trivial and tracking it provides no organizational benefit\n" + "3. The task can be completed in less than 3 trivial steps\n" + "4. The task is purely conversational or informational\n" + "\n" + "NOTE that you should not use this tool if there is only one trivial task to do. In this case you are better off just doing the task directly.\n" + "\n" + "## Task States and Management\n" + "\n" + "1. **Task States**: Use these states to track progress:\n" + " - pending: Task not yet started\n" + " - in_progress: Currently working on (limit to ONE task at a time)\n" + " - completed: Task finished successfully\n" + "\n" + " **IMPORTANT**: Task descriptions must have two forms:\n" + ' - content: The imperative form describing what needs to be done (e.g., "Run tests", "Build the project")\n' + ' - activeForm: The present continuous form shown during execution (e.g., "Running tests", "Building the project")\n' + "\n" + "2. **Task Management**:\n" + " - Update task status in real-time as you work\n" + " - Mark tasks complete IMMEDIATELY after finishing (don't batch completions)\n" + " - Exactly ONE task must be in_progress at any time (not less, not more)\n" + " - Complete current tasks before starting new ones\n" + " - Remove tasks that are no longer relevant from the list entirely\n" + "\n" + "3. **Task Completion Requirements**:\n" + " - ONLY mark a task as completed when you have FULLY accomplished it\n" + " - If you encounter errors, blockers, or cannot finish, keep the task as in_progress\n" + " - When blocked, create a new task describing what needs to be resolved\n" + " - Never mark a task as completed if:\n" + " - Tests are failing\n" + " - Implementation is partial\n" + " - You encountered unresolved errors\n" + " - You couldn't find necessary files or dependencies\n" + "\n" + "4. **Task Breakdown**:\n" + " - Create specific, actionable items\n" + " - Break complex tasks into smaller, manageable steps\n" + " - Use clear, descriptive task names\n" + " - Always provide both forms:\n" + ' - content: "Fix authentication bug"\n' + ' - activeForm: "Fixing authentication bug"\n' + "\n" + "When in doubt, use this tool. Being proactive with task management demonstrates attentiveness and ensures you complete all requirements successfully.\n" + "\n" + "Pass the COMPLETE todo list each call — it replaces the prior list.", parameters: Type.Object({ todos: Type.Array(Type.Object({ content: Type.String({ description: "The task, in imperative form (e.g. \"Run the tests\")." }), status: Type.Union([Type.Literal("pending"), Type.Literal("in_progress"), Type.Literal("completed")]), activeForm: Type.String({ minLength: 1, description: "Present-continuous form shown while in progress (e.g. \"Running the tests\")." }), })), }), execute: (args, ctx) => { const key = ctx?.sessionId ?? ""; const shape = (t) => ({ content: t.content, status: t.status, activeForm: t.activeForm }); const oldTodos = (lists.get(key) ?? []).map(shape); const written = args.todos; for (let i = 0; i < written.length; i++) { const activeForm = written[i].activeForm; if (typeof activeForm !== "string" || activeForm.length === 0) { const what = activeForm === undefined ? "is missing" : "has an empty or non-string"; return errorResult(`Error (TodoWrite): todo ${i + 1} of ${written.length} ${what} \`activeForm\` — every todo needs the ` + `present-continuous label shown while it runs (e.g. content "Run the tests" → activeForm "Running the ` + `tests"), alongside its imperative \`content\`. Nothing was stored; resend the COMPLETE list with the ` + `label filled in on every item.`); } } const allDone = written.every((t) => t.status === "completed"); lists.set(key, allDone ? [] : written); const newTodos = written.map(shape); const open = written.filter((t) => t.status !== "completed").length; return { content: `Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. ` + `Please proceed with the current tasks if applicable (${written.length} total, ${open} open)`, details: { type: "todo", oldTodos, newTodos }, }; }, }; }