From 0a245c49a7dfdf9a339358847dafe4da4d5992b1 Mon Sep 17 00:00:00 2001 From: Toray Altas Date: Sat, 17 Jan 2026 17:01:24 -0500 Subject: [PATCH] fix: eliminate spurious user edits and remove edit button from notifications - Add system_update_todos message type to distinguish system-generated todo updates from actual user edits - Remove unnecessary edit button from todo list update notifications - Update UI rendering logic to handle system updates appropriately - Update tests to reflect new message type behavior --- packages/types/src/message.ts | 1 + .../history-resume-delegation.spec.ts | 14 +++++++----- src/core/webview/ClineProvider.ts | 4 ++-- src/shared/todo.ts | 3 ++- webview-ui/src/components/chat/ChatRow.tsx | 2 ++ .../chat/UpdateTodoListToolBlock.tsx | 22 ------------------- 6 files changed, 16 insertions(+), 30 deletions(-) diff --git a/packages/types/src/message.ts b/packages/types/src/message.ts index 109cd842ba..dc7d8f2344 100644 --- a/packages/types/src/message.ts +++ b/packages/types/src/message.ts @@ -180,6 +180,7 @@ export const clineSays = [ "sliding_window_truncation", "codebase_search_result", "user_edit_todos", + "system_update_todos", ] as const export const clineSaySchema = z.enum(clineSays) diff --git a/src/__tests__/history-resume-delegation.spec.ts b/src/__tests__/history-resume-delegation.spec.ts index 5934e9fe39..93bff631f6 100644 --- a/src/__tests__/history-resume-delegation.spec.ts +++ b/src/__tests__/history-resume-delegation.spec.ts @@ -537,7 +537,7 @@ describe("History resume delegation - parent metadata transitions", () => { const parentMessagesWithCompletedTodos = [ { type: "say", - say: "user_edit_todos", + say: "system_update_todos", text: JSON.stringify({ tool: "updateTodoList", todos: [ @@ -564,8 +564,10 @@ describe("History resume delegation - parent metadata transitions", () => { expect(saveTaskMessages).toHaveBeenCalled() const savedCall = vi.mocked(saveTaskMessages).mock.calls[0][0] - // Find the user_edit_todos message that was added for the write-back - const todoEditMessages = savedCall.messages.filter((m: any) => m.type === "say" && m.say === "user_edit_todos") + // Find the system_update_todos message that was added for the write-back + const todoEditMessages = savedCall.messages.filter( + (m: any) => m.type === "say" && m.say === "system_update_todos", + ) // Should have at least 2 todo edit messages (original + write-back) expect(todoEditMessages.length).toBeGreaterThanOrEqual(1) @@ -627,7 +629,7 @@ describe("History resume delegation - parent metadata transitions", () => { const parentMessagesWithTodos = [ { type: "say", - say: "user_edit_todos", + say: "system_update_todos", text: JSON.stringify({ tool: "updateTodoList", todos: [{ id: "todo-1", content: "Some task", status: "completed" }], @@ -650,7 +652,9 @@ describe("History resume delegation - parent metadata transitions", () => { const savedCall = vi.mocked(saveTaskMessages).mock.calls[0][0] // Find todo edit messages (if any were added beyond the original) - const todoEditMessages = savedCall.messages.filter((m: any) => m.type === "say" && m.say === "user_edit_todos") + const todoEditMessages = savedCall.messages.filter( + (m: any) => m.type === "say" && m.say === "system_update_todos", + ) // Should only have the original todo edit, no write-back because child isn't in childIds // The fallback should NOT be triggered for an unrelated child diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 77dfeb86d1..d6aab711ef 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -3223,7 +3223,7 @@ export class ClineProvider { ts: Date.now(), type: "say", - say: "user_edit_todos", + say: "system_update_todos", text: JSON.stringify({ tool: "updateTodoList", todos, @@ -3376,7 +3376,7 @@ export class ClineProvider parentClineMessages.push({ ts: Date.now(), type: "say", - say: "user_edit_todos", + say: "system_update_todos", text: JSON.stringify({ tool: "updateTodoList", todos, diff --git a/src/shared/todo.ts b/src/shared/todo.ts index d20539049b..81e4559a93 100644 --- a/src/shared/todo.ts +++ b/src/shared/todo.ts @@ -4,7 +4,8 @@ export function getLatestTodo(clineMessages: ClineMessage[]) { const todos = clineMessages .filter( (msg) => - (msg.type === "ask" && msg.ask === "tool") || (msg.type === "say" && msg.say === "user_edit_todos"), + (msg.type === "ask" && msg.ask === "tool") || + (msg.type === "say" && (msg.say === "user_edit_todos" || msg.say === "system_update_todos")), ) .map((msg) => { try { diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 24749bb419..9d9964d43d 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -1418,6 +1418,8 @@ export const ChatRowContent = ({ return case "user_edit_todos": return {}} /> + case "system_update_todos": + return {}} /> case "tool" as any: // Handle say tool messages const sayTool = safeJsonParse(message.text) diff --git a/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx b/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx index e851284505..310b8cd518 100644 --- a/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx +++ b/webview-ui/src/components/chat/UpdateTodoListToolBlock.tsx @@ -179,28 +179,6 @@ const UpdateTodoListToolBlock: React.FC = ({ Todo List Updated
- {editable && ( - - )}