Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export class ChatTodoListWidget extends Disposable {
}

const todoList = this.chatTodoListService.getTodos(sessionId);
if (todoList.length > 0) {
if (todoList.length > 2) {
this.renderTodoList(todoList);
this.domNode.style.display = 'block';
} else {
Expand Down
40 changes: 38 additions & 2 deletions src/vs/workbench/contrib/chat/common/tools/manageTodoListTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ export function createManageTodoListToolData(writeOnly: boolean): IToolData {
}
};

const requiredFields = ['todoList'];
// Only require the full todoList when operating in write-only mode.
// In read/write mode, the write path validates todoList at runtime, so it's not schema-required.
const requiredFields = writeOnly ? ['todoList'] : [] as string[];

if (!writeOnly) {
baseProperties.operation = {
Expand Down Expand Up @@ -231,9 +233,25 @@ export class ManageTodoListTool extends Disposable implements IToolImpl {
status: parsedTodo.status
}));

const existingTodos = this.chatTodoListService.getTodos(chatSessionId);
const changes = this.calculateTodoChanges(existingTodos, todoList);

this.chatTodoListService.setTodos(chatSessionId, todoList);
const statusCounts = this.calculateStatusCounts(todoList);

// Build warnings
const warnings: string[] = [];
if (todoList.length < 3) {
warnings.push('Warning: Small todo list (<3 items). This task might not need a todo list.');
}
else if (todoList.length > 10) {
warnings.push('Warning: Large todo list (>10 items). Consider keeping the list focused and actionable.');
}

if (changes > 3) {
warnings.push('Warning: Did you mean to update so many todos at the same time? Consider working on them one by one.');
}

this.telemetryService.publicLog2<TodoListToolInvokedEvent, TodoListToolInvokedClassification>(
'todoListToolInvoked',
{
Expand All @@ -248,7 +266,7 @@ export class ManageTodoListTool extends Disposable implements IToolImpl {
return {
content: [{
kind: 'text',
value: 'Successfully wrote todo list'
value: `Successfully wrote todo list${warnings.length ? '\n\n' + warnings.join('\n') : ''}`
}]
};
}
Expand Down Expand Up @@ -288,6 +306,24 @@ export class ManageTodoListTool extends Disposable implements IToolImpl {
return lines.join('\n');
}).join('\n');
}

private calculateTodoChanges(oldList: IChatTodo[], newList: IChatTodo[]): number {
// Assume arrays are equivalent in order; compare index-by-index
let modified = 0;
const minLen = Math.min(oldList.length, newList.length);
for (let i = 0; i < minLen; i++) {
const o = oldList[i];
const n = newList[i];
if (o.title !== n.title || (o.description ?? '') !== (n.description ?? '') || o.status !== n.status) {
modified++;
}
}

const added = Math.max(0, newList.length - oldList.length);
const removed = Math.max(0, oldList.length - newList.length);
const totalChanges = added + removed + modified;
return totalChanges;
}
}

type TodoListToolInvokedEvent = {
Expand Down
Loading