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

Todos verfolgen

Verfolgen Sie Todos in Agent SDK-Sitzungen und rendern Sie Claudes Fortschritt in Ihrer Anwendung aus strukturierten Tool-Aufrufen

Claude Code stellt die Task-Tracking-Tools standardmäßig nur auf den unter Modellverfügbarkeit aufgelisteten Modellen bereit. Neuere Modelle verfolgen mehrstufige Arbeiten ohne eine schriftliche Todo-Liste, daher benötigen Sie auf diesen nichts auf dieser Seite, damit Claude mehrstufige Aufgaben durcharbeitet.

In einer Sitzung, die die Task-Tracking-Tools hat, führt Claude eine schriftliche Todo-Liste, aktualisiert den Status jedes Elements während der Arbeit. Sie sehen jede Änderung im Nachrichtenstrom als strukturierten Tool-Aufruf. Aktivieren Sie eine Sitzung nur, wenn Ihre Anwendung diese Tool-Aufrufe liest, sei es zum Protokollieren von Task-Aktivitäten oder zum Rendern einer eigenen Fortschrittsanzeige.

Modellverfügbarkeit

Bei einem Modell, das die Tools standardmäßig nicht hat, sehen Sie keine tool_use-Blöcke für diese im Nachrichtenstrom, es sei denn, Sie aktivieren eine Sitzung. Das Agent SDK wendet diese Standardeinstellungen über die Claude Code-Binärdatei an, die es bündelt. Wenn Sie pathToClaudeCodeExecutable (TypeScript) oder cli_path (Python) auf Ihre eigene Claude Code-Installation verweisen, erhalten Sie die Tools, die diese Installation bereitstellt, unter ihren eigenen Standardeinstellungen. Um die genaue Menge in einer laufenden Sitzung zu sehen, überprüfen Sie, welche Tools verfügbar sind. Um eine Sitzung zu aktivieren, führen Sie eines der folgenden Verfahren durch:

  • Nennen Sie eines der Tools in der Option allowedTools (TypeScript) oder allowed_tools (Python)
  • Listen Sie die Tools in der Option tools auf, die die integrierten Tools der Sitzung auf die beschriebenen beschränkt. Fügen Sie die gewünschten Tools neben den anderen integrierten Tools ein, die Sie verwenden
  • Setzen Sie CLAUDE_CODE_ENABLE_TODO_TOOLS=1 in der Option env, wie die Beispiele auf dieser Seite. In TypeScript ersetzt env die Subprocess-Umgebung, daher verteilen Sie ...process.env, um vererbte Variablen zu behalten. In Python wird env auf die vererbte Umgebung zusammengeführt

Todo-Lebenszyklus

Claude bewegt jedes Todo durch einen vorhersehbaren Lebenszyklus:

  1. Erstellt: Claude fügt das Todo als pending hinzu, wenn es eine Aufgabe identifiziert
  2. Aktiviert: Claude setzt das Todo auf in_progress, wenn es die Arbeit beginnt
  3. Abgeschlossen: Claude markiert es als abgeschlossen, wenn die Aufgabe erfolgreich beendet wird
  4. Entfernt: Claude löscht ein Todo, das es nicht mehr benötigt, indem es status: "deleted" in einem TaskUpdate-Aufruf setzt

Wann Claude Todos erstellt

In einer Sitzung, die die Task-Tracking-Tools hat, erstellt Claude Todos für die meisten mehrstufigen Arbeiten, wie zum Beispiel:

  • Komplexe mehrstufige Aufgaben, die drei oder mehr unterschiedliche Aktionen erfordern
  • Von Benutzern bereitgestellte Aufgabenlisten, wenn mehrere Elemente erwähnt werden
  • Längere Operationen, die von der Fortschrittsverfolgung profitieren
  • Explizite Anfragen, wenn Benutzer um Todo-Organisation bitten

Claude kann Todos für sehr kurze oder einstufige Anfragen überspringen.

Beispiele

Bevor Sie diese Beispiele ausführen, installieren Sie das Claude Agent SDK, indem Sie dem Schnellstart folgen. Jedes Beispiel auf dieser Seite teilt die gleiche Berechtigungseinrichtung und das gleiche Beendigungsverhalten:

  • Berechtigungsmodus: Die Beispiel-Prompts bitten Claude, echte Arbeit an einem Projekt zu leisten, daher setzt jedes Beispiel permissionMode: "acceptEdits" (TypeScript) oder permission_mode="acceptEdits" (Python), um die Dateibearbeitungen, die die Arbeit erzeugt, automatisch zu genehmigen. Siehe Berechtigungsmodi für die Alternativen.
  • Turnus-Limit: Jedes Beispiel wird ausgeführt, bis der Agent fertig ist und seine endgültige Ergebnismeldung liefert. Wenn eine Sitzung zuerst ihr Turnus-Limit erreicht, hat diese Ergebnismeldung den Subtyp error_max_turns. Überprüfen Sie subtype, um dieses Ende zu erkennen.
  • Fehlerbehandlung: Diese Beispiele verwenden Single-Shot-query()-Aufrufe. Nach dem Liefern eines error_max_turns-Ergebnisses wirft query() einen Fehler aus, der Reached maximum number of turns enthält. Jedes Beispiel umhüllt seine Schleife in einem Try-Block, um sauber zu beenden, wenn dies geschieht. Siehe Handle the result für die Ergebnis-Subtypen.

Überwachen Sie Todo-Änderungen

Das folgende Beispiel beobachtet den Assistent-Stream auf TaskCreate- und TaskUpdate-tool_use-Blöcke und druckt eine +-Zeile mit dem Betreff jeder neuen Aufgabe und eine Update-Zeile mit der Task-ID und dem neuen Status jeder Statusänderung. Verwenden Sie diese Form, wenn Sie ein Protokoll der Task-Aktivität statt einer gerenderten Anzeige möchten. Die +-Zeilen enthalten nicht die zugewiesenen IDs, daher kann dieses Protokoll Updates nicht zurück zu ihren Erstellungen abgleichen. Um diese Korrelation zu behalten, erfassen Sie die IDs wie Zeigen Sie den Fortschritt in Echtzeit an.

Die gestreamte tool_use-Eingabe ist die rohe Form, die das Modell ausgegeben hat. Claude Code repariert einige nahezu korrekte, aber fehlerhafte Schlüsselnamen vor der Ausführung, indem es id oder task_id auf taskId und active_form auf activeForm abbildet, aber diese Reparatur wird nicht im Stream widergespiegelt. Lesen Sie TaskUpdate-Eingabefelder defensiv, wie beide Beispiele auf dieser Seite, anstatt anzunehmen, dass der kanonische Name immer vorhanden ist.

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

Zeigen Sie den Fortschritt in Echtzeit an

Das folgende Beispiel beobachtet den Assistent-Stream auf TaskCreate- und TaskUpdate-tool_use-Blöcke und führt eine Zuordnung von Tasks mit Task-ID in einer TaskTracker-Klasse, wobei eine Fortschrittszusammenfassung bei jeder Änderung neu gerendert wird. Die Zusammenfassung zählt abgeschlossene und laufende Tasks und zeigt das activeForm-Label jedes aktiven Elements anstelle seines subject. Verwenden Sie diese Form, wenn Ihre Anwendung eine Fortschrittsanzeige führt, anstatt jedes Ereignis zu protokollieren.

Die zugewiesene Task-ID befindet sich nicht in der TaskCreate-Eingabe. Claude Code liefert die strukturierte Ausgabe jedes Tools in der Benutzermeldung, die seinen tool_result-Block trägt, im Feld tool_use_result. Für TaskCreate ist dieses Objekt für TypeScript als TaskCreateOutput unter Tool Output Types dokumentiert, und in Python ist das Feld ein einfaches Dict der gleichen Form. Der Tracker paart jeden tool_result-Block mit seinem tool_use-Aufruf nach tool_use_id und liest task.id aus der gepaarten Meldung des tool_use_result. Claude kann die Liste mit TaskList zurücklesen und die vollständigen Details einer Task mit 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");