SpyBara
Go Premium

agent-sdk/todo-tracking.md 2026-09-09 22:58 UTC to 2026-09-10 23:00 UTC

This page contains 4 additions and 2 deletions.

2026
Wed 9 22:58 Thu 10 23:00 Sat 12 03:02

Traccia todo

Traccia i todo nelle sessioni di Agent SDK e visualizza i progressi di Claude nella tua applicazione da chiamate di strumenti strutturate

Sui modelli elencati in Disponibilità del modello, Claude traccia il lavoro multi-step senza un elenco todo scritto, e Claude Code esclude i task-tracking tools dalle sessioni per impostazione predefinita. Non hai bisogno di nulla in questa pagina affinché Claude lavori attraverso attività multi-step su questi modelli.

In una sessione che ha i task-tracking tools, Claude mantiene un elenco todo scritto, aggiornando lo stato di ogni elemento mentre lavora. Vedi ogni cambiamento nel flusso dei messaggi come una chiamata di strumento strutturata. Abilita una sessione solo quando la tua applicazione legge quelle chiamate di strumento, sia per registrare l'attività delle attività che per renderizzare il proprio display di progresso.

Disponibilità del modello

Sui modelli elencati, a meno che non abiliti una sessione, non vedi blocchi tool_use per gli strumenti nel flusso dei messaggi. L'Agent SDK applica questi valori predefiniti attraverso il binario Claude Code che raggruppa. Se punti pathToClaudeCodeExecutable (TypeScript) o cli_path (Python) alla tua installazione di Claude Code, ottieni gli strumenti che quella installazione fornisce, secondo i suoi valori predefiniti. Per vedere l'insieme esatto in una sessione in esecuzione, controlla quali strumenti sono disponibili. Per abilitare una sessione, fai uno dei seguenti:

  • Nomina uno degli strumenti nell'opzione allowedTools (TypeScript) o allowed_tools (Python)
  • Elenca gli strumenti nell'opzione tools, che limita gli strumenti integrati della sessione a quelli che nomina. Includi gli strumenti che desideri insieme agli altri strumenti integrati che utilizzi
  • Imposta CLAUDE_CODE_ENABLE_TODO_TOOLS=1 nell'opzione env, come fanno gli esempi in questa pagina. In TypeScript, env sostituisce l'ambiente del sottoprocesso, quindi diffondere ...process.env per mantenere le variabili ereditate. In Python, env viene unito sopra l'ambiente ereditato

Ciclo di vita dei todo

Claude sposta ogni todo attraverso un ciclo di vita prevedibile:

  1. Creato: Claude aggiunge il todo come pending quando identifica un'attività
  2. Attivato: Claude imposta il todo su in_progress quando inizia il lavoro
  3. Completato: Claude lo contrassegna come completato quando l'attività termina con successo
  4. Rimosso: Claude elimina un todo che non ha più bisogno impostando status: "deleted" in una chiamata TaskUpdate

Quando Claude crea i todo

In una sessione che ha i task-tracking tools, Claude crea todo per la maggior parte del lavoro multi-step, come:

  • Attività complesse multi-step che richiedono tre o più azioni distinte
  • Elenchi di attività forniti dall'utente quando vengono menzionati più elementi
  • Operazioni più lunghe che traggono beneficio dal tracciamento dei progressi
  • Richieste esplicite quando gli utenti chiedono l'organizzazione dei todo

Claude potrebbe saltare i todo per richieste molto brevi o a singolo step.

Esempi

Prima di eseguire questi esempi, installa Claude Agent SDK seguendo la guida rapida. Ogni esempio in questa pagina condivide la stessa configurazione di permessi e comportamento di uscita:

  • Modalità di permesso: gli esempi di prompt chiedono a Claude di fare lavoro reale su un progetto, quindi ogni esempio imposta permissionMode: "acceptEdits" (TypeScript) o permission_mode="acceptEdits" (Python) per approvare automaticamente le modifiche ai file che il lavoro produce. Vedi Modalità di permesso per le alternative.
  • Limite di turni: ogni esempio viene eseguito fino a quando l'agente non termina e produce il suo messaggio di risultato finale. Se una sessione raggiunge prima il limite di turni, quel messaggio di risultato ha il sottotipo error_max_turns. Controlla subtype per rilevare quella conclusione.
  • Gestione degli errori: questi esempi utilizzano singole chiamate query(). Dopo aver prodotto un risultato error_max_turns, query() genera un errore che include Reached maximum number of turns. Ogni esempio racchiude il suo ciclo in un blocco try per uscire correttamente quando ciò accade. Vedi Gestire il risultato per i sottotipi di risultato.

Monitora i cambiamenti dei todo

L'esempio seguente osserva il flusso dell'assistente per i blocchi tool_use TaskCreate e TaskUpdate e stampa una riga + con il soggetto di ogni nuovo compito e una riga di aggiornamento con l'ID del compito di ogni cambio di stato e il nuovo stato. Usa questa forma quando desideri un registro dell'attività delle attività piuttosto che un display renderizzato. Le righe + non includono gli ID assegnati, quindi questo registro non può far corrispondere gli aggiornamenti ai loro creati. Per mantenere quella correlazione, cattura gli ID come fa Visualizza i progressi in tempo reale.

L'input tool_use trasmesso è la forma grezza che il modello ha emesso. Claude Code ripara alcuni nomi di chiave quasi corretti ma non del tutto prima dell'esecuzione, mappando id o task_id a taskId e active_form a activeForm, ma questa riparazione non si riflette nel flusso. Leggi i campi di input TaskUpdate in modo difensivo, come fanno entrambi gli esempi in questa pagina, piuttosto che assumere che il nome canonico sia sempre presente.

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

Visualizza i progressi in tempo reale

L'esempio seguente osserva il flusso dell'assistente per i blocchi tool_use TaskCreate e TaskUpdate e mantiene una mappa di attività con chiave dell'ID attività in una classe TaskTracker, renderizzando un riepilogo dei progressi ad ogni cambiamento. Il riepilogo conta le attività completate e in corso e mostra l'etichetta activeForm di ogni elemento attivo al posto del suo subject. Usa questa forma quando la tua applicazione mantiene un display di progresso invece di registrare ogni evento.

L'ID attività assegnato non è nell'input TaskCreate. Claude Code fornisce l'output strutturato di ogni strumento nel messaggio dell'utente che porta il suo blocco tool_result, nel campo tool_use_result. Per TaskCreate, quell'oggetto è documentato per TypeScript come TaskCreateOutput in Tool Output Types, e in Python il campo è un semplice dict della stessa forma. Il tracker abbina ogni blocco tool_result alla sua chiamata tool_use per tool_use_id e legge task.id dal tool_use_result del messaggio abbinato. Claude può leggere l'elenco di nuovo con TaskList e i dettagli completi di un'attività con 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");