SpyBara
Go Premium

agent-sdk/todo-tracking.md 2026-09-11 23:01 UTC to 2026-09-12 03:02 UTC

This page contains 2 additions and 4 deletions.

2026
Thu 10 23:00 Sat 12 03:02

Отслеживание задач

Отслеживайте задачи в сеансах Agent SDK и отображайте прогресс Claude в вашем приложении с помощью структурированных вызовов инструментов

Claude Code предоставляет инструменты отслеживания задач по умолчанию только на моделях, перечисленных в разделе Доступность моделей. Более новые модели отслеживают многошаговую работу без письменного списка задач, поэтому на них вам не нужно ничего из этой страницы, чтобы Claude работал с многошаговыми задачами.

В сеансе, который имеет инструменты отслеживания задач, Claude ведет письменный список задач, обновляя статус каждого элемента по мере работы. Вы видите каждое изменение в потоке сообщений как структурированный вызов инструмента. Подключите сеанс только когда ваше приложение читает эти вызовы инструментов, будь то для логирования активности задач или для отображения собственного индикатора прогресса.

Доступность моделей

На модели, которая не имеет инструментов по умолчанию, если вы не подключите сеанс, вы не увидите блоков tool_use для них в потоке сообщений. Agent SDK применяет эти значения по умолчанию через двоичный файл Claude Code, который он включает. Если вы указываете pathToClaudeCodeExecutable (TypeScript) или cli_path (Python) на вашу собственную установку Claude Code, вы получаете те инструменты, которые предоставляет эта установка, в соответствии с её собственными значениями по умолчанию. Чтобы увидеть точный набор в работающем сеансе, проверьте, какие инструменты доступны. Чтобы подключить сеанс, выполните одно из следующих действий:

  • Назовите один из инструментов в опции allowedTools (TypeScript) или allowed_tools (Python)
  • Перечислите инструменты в опции tools, которая ограничивает встроенные инструменты сеанса только теми, которые она называет. Включите нужные вам инструменты вместе с другими встроенными инструментами, которые вы используете
  • Установите CLAUDE_CODE_ENABLE_TODO_TOOLS=1 в опции env, как это делают примеры на этой странице. В TypeScript env заменяет окружение подпроцесса, поэтому распределите ...process.env для сохранения унаследованных переменных. В Python env объединяется с унаследованным окружением

Жизненный цикл задач

Claude перемещает каждую задачу через предсказуемый жизненный цикл:

  1. Созданы: Claude добавляет задачу как pending когда выявляет задачу
  2. Активированы: Claude устанавливает задачу в in_progress когда начинает работу
  3. Завершены: Claude отмечает её завершённой когда задача успешно завершается
  4. Удалены: Claude удаляет задачу, которая ей больше не нужна, установив status: "deleted" в вызове TaskUpdate

Когда Claude создаёт задачи

В сеансе, который имеет инструменты отслеживания задач, Claude создаёт задачи для большинства многошаговой работы, такой как:

  • Сложные многошаговые задачи, требующие трёх или более отдельных действий
  • Списки задач, предоставленные пользователем, когда упоминаются несколько элементов
  • Более длительные операции, которые выигрывают от отслеживания прогресса
  • Явные запросы, когда пользователи просят организовать задачи

Claude может пропустить задачи для очень коротких или одношаговых запросов.

Примеры

Перед запуском этих примеров установите Claude Agent SDK, следуя краткому руководству. Каждый пример на этой странице использует одну и ту же настройку разрешений и поведение выхода:

  • Режим разрешений: примеры подсказок просят Claude выполнить реальную работу над проектом, поэтому каждый пример устанавливает permissionMode: "acceptEdits" (TypeScript) или permission_mode="acceptEdits" (Python) для автоматического одобрения редактирования файлов, которое производит работа. Смотрите Режимы разрешений для альтернатив.
  • Лимит ходов: каждый пример работает до завершения агентом и выдачи его финального сообщения результата. Если сеанс сначала достигает лимита ходов, то сообщение результата имеет подтип error_max_turns. Проверьте subtype, чтобы обнаружить это завершение.
  • Обработка ошибок: эти примеры используют однократные вызовы query(). После выдачи результата error_max_turns, query() выбрасывает ошибку, которая включает Reached maximum number of turns. Каждый пример оборачивает свой цикл в блок try для чистого выхода при возникновении этого события. Смотрите Обработка результата для подтипов результатов.

Мониторинг изменений задач

Следующий пример наблюдает за потоком помощника для блоков tool_use TaskCreate и TaskUpdate и выводит строку + с предметом каждой новой задачи и строку обновления с ID задачи каждого изменения статуса и новым статусом. Используйте эту форму когда вы хотите логирование активности задач вместо отображаемого дисплея. Строки + не включают назначенные ID, поэтому этот логирование не может сопоставить обновления обратно их созданиям. Чтобы сохранить это соответствие, захватите ID как это делает Отображение прогресса в реальном времени.

Потоковый ввод tool_use — это необработанная форма, которую выдала модель. Claude Code исправляет некоторые близкие, но неправильные имена ключей перед выполнением, сопоставляя id или task_id с taskId и active_form с activeForm, но это исправление не отражается в потоке. Читайте поля ввода TaskUpdate защитно, как это делают оба примера на этой странице, а не предполагайте, что каноническое имя всегда присутствует.

import { query } from "@anthropic-ai/claude-agent-sdk";

try {
for await (const message of query({
prompt: "Create a static website with a home page, an about page, and a shared stylesheet, and track progress with todos",
// Keeps the Task tools on models where Claude Code otherwise doesn't provide them.
options: { maxTurns: 15, permissionMode: "acceptEdits", env: { ...process.env, CLAUDE_CODE_ENABLE_TODO_TOOLS: "1" } },
})) {
if (message.type !== "assistant") continue;
for (const block of message.message.content) {
if (block.type !== "tool_use") continue;
if (block.name === "TaskCreate") {
const input = block.input as { subject: string };
console.log(`+ ${input.subject}`);
} else if (block.name === "TaskUpdate") {
const input = block.input as {
taskId?: string;
id?: string;
task_id?: string;
status?: string;
};
const taskId = input.taskId ?? input.id ?? input.task_id;
if (taskId && input.status) console.log(`  ${taskId} -> ${input.status}`);
}
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result.
console.log(`Session ended with an error: ${error}`);
}

Отображение прогресса в реальном времени

Следующий пример наблюдает за потоком помощника для блоков tool_use TaskCreate и TaskUpdate и ведёт карту задач, индексированную по ID задачи в классе TaskTracker, переотображая сводку прогресса при каждом изменении. Сводка подсчитывает завершённые и выполняемые задачи и показывает метку activeForm каждого активного элемента вместо его subject. Используйте эту форму когда ваше приложение ведёт дисплей прогресса вместо логирования каждого события.

Назначенный ID задачи отсутствует во вводе TaskCreate. Claude Code доставляет структурированный вывод каждого инструмента в сообщение пользователя, которое несёт его блок tool_result, в поле tool_use_result. Для TaskCreate, этот объект задокументирован для TypeScript как TaskCreateOutput в разделе Типы вывода инструментов, и в Python поле является простым dict той же формы. Трекер сопоставляет каждый блок tool_result с его вызовом tool_use по tool_use_id и читает task.id из tool_use_result сопоставленного сообщения. Claude может прочитать список обратно с помощью TaskList и полные детали одной задачи с помощью TaskGet.

import { query } from "@anthropic-ai/claude-agent-sdk";

type Task = { subject: string; activeForm?: string; status: string };

class TaskTracker {
private tasks = new Map<string, Task>();
private pendingCreates = new Map<string, { subject: string; activeForm?: string }>();

displayProgress() {
if (this.tasks.size === 0) {
console.log("\nProgress: no open tasks\n");
return;
}

const items = [...this.tasks.values()];
const completed = items.filter((t) => t.status === "completed").length;
const inProgress = items.filter((t) => t.status === "in_progress").length;

console.log(`\nProgress: ${completed}/${this.tasks.size} completed`);
console.log(`Currently working on: ${inProgress} task(s)\n`);

for (const [id, task] of this.tasks) {
const icon =
task.status === "completed" ? "✅" : task.status === "in_progress" ? "🔧" : "❌";
const text = task.status === "in_progress" && task.activeForm ? task.activeForm : task.subject;
console.log(`${id}. ${icon} ${text}`);
}
}

handleToolUse(block: { id: string; name: string; input: unknown }) {
if (block.name === "TaskCreate") {
const input = block.input as { subject: string; activeForm?: string; active_form?: string };
this.pendingCreates.set(block.id, {
subject: input.subject,
activeForm: input.activeForm ?? input.active_form,
});
} else if (block.name === "TaskUpdate") {
const input = block.input as {
taskId?: string;
id?: string;
task_id?: string;
status?: string;
activeForm?: string;
active_form?: string;
};
const taskId = input.taskId ?? input.id ?? input.task_id;
if (!taskId) return;
if (input.status === "deleted") {
this.tasks.delete(taskId);
this.displayProgress();
return;
}
const task = this.tasks.get(taskId);
if (!task) return;
if (input.status) task.status = input.status;
const active = input.activeForm ?? input.active_form;
if (active) task.activeForm = active;
this.displayProgress();
}
}

handleToolResult(block: { tool_use_id: string; is_error?: boolean }, result: unknown) {
const create = this.pendingCreates.get(block.tool_use_id);
if (!create) return;
this.pendingCreates.delete(block.tool_use_id);
if (block.is_error) return;
// The result's user message carries the tool's structured output as
// tool_use_result; for TaskCreate that's TaskCreateOutput,
// { task: { id, subject } }.
const out = result as { task?: { id: string } };
if (!out?.task?.id) return;
this.tasks.set(out.task.id, { ...create, status: "pending" });
this.displayProgress();
}

async trackQuery(prompt: string) {
try {
for await (const message of query({
prompt,
options: { maxTurns: 20, permissionMode: "acceptEdits", env: { ...process.env, CLAUDE_CODE_ENABLE_TODO_TOOLS: "1" } },
})) {
if (message.type === "assistant") {
for (const block of message.message.content) {
if (block.type === "tool_use") this.handleToolUse(block);
}
}
if (message.type === "user" && Array.isArray(message.message.content)) {
for (const block of message.message.content) {
if (block.type === "tool_result") this.handleToolResult(block, message.tool_use_result);
}
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result,
// such as when the maxTurns limit is hit.
console.log(`Session ended with an error: ${error}`);
}
}
}

// Usage
const tracker = new TaskTracker();
await tracker.trackQuery("Build a complete authentication system with todos");