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
Wed 9 22:58 Thu 10 23:00 Sat 12 03:02

Lacak todos

Lacak todos dalam sesi Agent SDK dan tampilkan kemajuan Claude dalam aplikasi Anda dari panggilan alat terstruktur

Claude Code menyediakan alat pelacakan tugas secara default hanya pada model yang tercantum di bawah Ketersediaan model. Model yang lebih baru melacak pekerjaan multi-langkah tanpa daftar todo tertulis, jadi pada model tersebut Anda tidak memerlukan apa pun di halaman ini agar Claude dapat menyelesaikan tugas multi-langkah.

Dalam sesi yang memiliki alat pelacakan tugas, Claude menyimpan daftar todo tertulis, memperbarui status setiap item saat bekerja. Anda melihat setiap perubahan dalam aliran pesan sebagai panggilan alat terstruktur. Pilih sesi hanya ketika aplikasi Anda membaca panggilan alat tersebut, baik untuk mencatat aktivitas tugas atau untuk merender tampilan kemajuan sendiri.

Ketersediaan model

Pada model yang tidak memiliki alat secara default, kecuali Anda memilih sesi, Anda tidak melihat blok tool_use untuk mereka dalam aliran pesan. Agent SDK menerapkan default ini melalui biner Claude Code yang disertakannya. Jika Anda menunjuk pathToClaudeCodeExecutable (TypeScript) atau cli_path (Python) ke instalasi Claude Code Anda sendiri, Anda mendapatkan alat apa pun yang disediakan instalasi tersebut, di bawah default-nya sendiri. Untuk melihat set yang tepat dalam sesi yang berjalan, periksa alat mana yang tersedia. Untuk memilih sesi, lakukan salah satu dari berikut:

  • Beri nama salah satu alat dalam opsi allowedTools (TypeScript) atau allowed_tools (Python)
  • Daftar alat dalam opsi tools, yang membatasi alat bawaan sesi ke yang dinamainya. Sertakan alat yang Anda inginkan bersama alat bawaan lain yang Anda gunakan
  • Atur CLAUDE_CODE_ENABLE_TODO_TOOLS=1 dalam opsi env, seperti yang dilakukan contoh di halaman ini. Di TypeScript, env menggantikan lingkungan subproses, jadi sebarkan ...process.env untuk menyimpan variabel yang diwariskan. Di Python, env digabungkan di atas lingkungan yang diwariskan

Siklus hidup todo

Claude memindahkan setiap todo melalui siklus hidup yang dapat diprediksi:

  1. Dibuat: Claude menambahkan todo sebagai pending ketika mengidentifikasi tugas
  2. Diaktifkan: Claude menetapkan todo ke in_progress ketika memulai pekerjaan
  3. Diselesaikan: Claude menandainya selesai ketika tugas selesai dengan sukses
  4. Dihapus: Claude menghapus todo yang tidak lagi dibutuhkan dengan menetapkan status: "deleted" dalam panggilan TaskUpdate

Kapan Claude membuat todos

Dalam sesi yang memiliki alat pelacakan tugas, Claude membuat todos untuk sebagian besar pekerjaan multi-langkah, seperti:

  • Tugas multi-langkah yang kompleks memerlukan tiga atau lebih tindakan yang berbeda
  • Daftar tugas yang disediakan pengguna ketika beberapa item disebutkan
  • Operasi yang lebih lama yang mendapat manfaat dari pelacakan kemajuan
  • Permintaan eksplisit ketika pengguna meminta organisasi todo

Claude dapat melewatkan todos untuk permintaan yang sangat singkat atau satu langkah.

Contoh

Sebelum menjalankan contoh-contoh ini, instal Claude Agent SDK dengan mengikuti quickstart. Setiap contoh di halaman ini berbagi pengaturan izin dan perilaku keluar yang sama:

  • Mode izin: contoh prompt meminta Claude untuk melakukan pekerjaan nyata pada proyek, jadi setiap contoh menetapkan permissionMode: "acceptEdits" (TypeScript) atau permission_mode="acceptEdits" (Python) untuk menyetujui otomatis pengeditan file yang dihasilkan pekerjaan. Lihat Mode izin untuk alternatifnya.
  • Batas giliran: setiap contoh berjalan sampai agen selesai dan menghasilkan pesan hasil akhirnya. Jika sesi mencapai batas giliran terlebih dahulu, pesan hasil tersebut memiliki subtipe error_max_turns. Periksa subtype untuk mendeteksi penghentian tersebut.
  • Penanganan kesalahan: contoh-contoh ini menggunakan panggilan query() single-shot. Setelah menghasilkan hasil error_max_turns, query() melempar kesalahan yang mencakup Reached maximum number of turns. Setiap contoh membungkus loop-nya dalam blok try untuk keluar dengan bersih ketika itu terjadi. Lihat Handle the result untuk subtipe hasil.

Memantau perubahan todo

Contoh berikut memantau aliran asisten untuk blok tool_use TaskCreate dan TaskUpdate dan mencetak baris + dengan subjek setiap tugas baru dan baris pembaruan dengan ID tugas dan status baru setiap perubahan status. Gunakan bentuk ini ketika Anda menginginkan log aktivitas tugas daripada tampilan yang dirender. Baris + tidak menyertakan ID yang ditugaskan, jadi log ini tidak dapat mencocokkan pembaruan kembali ke pembuatannya. Untuk mempertahankan korelasi tersebut, tangkap ID seperti yang dilakukan Display progress in real time.

Input tool_use yang dialirkan adalah bentuk mentah yang dipancarkan model. Claude Code memperbaiki beberapa nama kunci yang hampir-tetapi-tidak-benar sebelum eksekusi, memetakan id atau task_id ke taskId dan active_form ke activeForm, tetapi perbaikan itu tidak tercermin dalam aliran. Baca bidang input TaskUpdate secara defensif, seperti yang dilakukan kedua contoh di halaman ini, daripada mengasumsikan nama kanonik selalu ada.

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}`);
}

Tampilkan kemajuan secara real-time

Contoh berikut memantau aliran asisten untuk blok tool_use TaskCreate dan TaskUpdate dan menyimpan peta tugas yang dikunci berdasarkan ID tugas dalam kelas TaskTracker, merender ulang ringkasan kemajuan pada setiap perubahan. Ringkasan menghitung tugas yang diselesaikan dan sedang berlangsung dan menampilkan label activeForm setiap item aktif sebagai pengganti subject-nya. Gunakan bentuk ini ketika aplikasi Anda mempertahankan tampilan kemajuan daripada mencatat setiap peristiwa.

ID tugas yang ditugaskan tidak ada dalam input TaskCreate. Claude Code mengirimkan output terstruktur setiap alat pada pesan pengguna yang membawa blok tool_result-nya, dalam bidang tool_use_result. Untuk TaskCreate, objek itu didokumentasikan untuk TypeScript sebagai TaskCreateOutput di bawah Tool Output Types, dan di Python bidangnya adalah dict biasa dari bentuk yang sama. Pelacak memasangkan setiap blok tool_result dengan panggilan tool_use-nya berdasarkan tool_use_id dan membaca task.id dari tool_use_result pesan yang dipasangkan. Claude dapat membaca daftar kembali dengan TaskList dan detail lengkap satu tugas dengan 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");