diff --git a/src/core/assistant-message/presentAssistantMessage.ts b/src/core/assistant-message/presentAssistantMessage.ts index 7bc29ac95a..693327a022 100644 --- a/src/core/assistant-message/presentAssistantMessage.ts +++ b/src/core/assistant-message/presentAssistantMessage.ts @@ -881,11 +881,6 @@ export async function presentAssistantMessage(cline: Task) { }) break case "update_todo_list": - console.log("[TODO-DEBUG]", "presentAssistantMessage dispatching update_todo_list", { - toolUseId: (block as any).id, - partial: block.partial, - params: (block as any).params, - }) await updateTodoListTool.handle(cline, block as ToolUse<"update_todo_list">, { askApproval, handleError, diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 0c1d71b2a4..636c909f05 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -3286,7 +3286,14 @@ export class Task extends EventEmitter implements TaskLike { // IMPORTANT: keep a reference so completion/delegation can await final persisted usage/cost. this.pendingUsageCollectionPromise = drainStreamInBackgroundToFindAllUsage(lastApiReqIndex) .catch((error) => { - console.error("Background usage collection failed:", error) + const err = error instanceof Error ? error : new Error("Background usage collection failed") + TelemetryService.instance.captureException(err, { + location: "Task.pendingUsageCollectionPromise", + taskId: this.taskId, + instanceId: this.instanceId, + lastApiReqIndex, + originalError: error instanceof Error ? undefined : serializeError(error), + }) }) .finally(() => { if (this.pendingUsageCollectionPromise) { diff --git a/src/core/tools/UpdateTodoListTool.ts b/src/core/tools/UpdateTodoListTool.ts index 1f5467034a..c87cc28b0b 100644 --- a/src/core/tools/UpdateTodoListTool.ts +++ b/src/core/tools/UpdateTodoListTool.ts @@ -3,9 +3,6 @@ import { formatResponse } from "../prompts/responses" import { BaseTool, ToolCallbacks } from "./BaseTool" import type { ToolUse } from "../../shared/tools" import crypto from "crypto" -import fs from "fs" -import os from "os" -import path from "path" import { TodoItem, TodoStatus, todoStatusSchema } from "@roo-code/types" import { getLatestTodo } from "../../shared/todo" @@ -25,54 +22,12 @@ export class UpdateTodoListTool extends BaseTool<"update_todo_list"> { } async execute(params: UpdateTodoListParams, task: Task, callbacks: ToolCallbacks): Promise { - console.log("[TODO-DEBUG] execute() STEP 0: ENTERED", { - tool: "update_todo_list", - paramsTodosType: typeof params?.todos, - paramsTodosLength: typeof params?.todos === "string" ? params.todos.length : undefined, - }) - const { pushToolResult, handleError, askApproval, toolProtocol } = callbacks + const { pushToolResult, handleError, askApproval } = callbacks try { - const summarizeTodoForDebug = (t: TodoItem | undefined) => { - if (!t) return undefined - return { - id: typeof t.id === "string" ? t.id : undefined, - status: typeof t.status === "string" ? t.status : undefined, - content: typeof t.content === "string" ? t.content.slice(0, 120) : undefined, - subtaskId: typeof t.subtaskId === "string" ? t.subtaskId : undefined, - tokens: typeof t.tokens === "number" ? t.tokens : undefined, - cost: typeof t.cost === "number" ? t.cost : undefined, - added: typeof t.added === "number" ? t.added : undefined, - removed: typeof t.removed === "number" ? t.removed : undefined, - } - } - - const shouldTodoDebugLog = - process.env.ROO_DEBUG_TODO_METADATA === "1" || - process.env.ROO_DEBUG_TODO_METADATA === "true" || - process.env.ROO_CLI_DEBUG_LOG === "1" - console.log("[TODO-DEBUG] execute() STEP 1: computed debug flags", { - shouldTodoDebugLog, - ROO_DEBUG_TODO_METADATA: process.env.ROO_DEBUG_TODO_METADATA, - ROO_CLI_DEBUG_LOG: process.env.ROO_CLI_DEBUG_LOG, - toolProtocol, - }) - const previousFromMemory = getTodoListForTask(task) - console.log("[TODO-DEBUG] execute() STEP 2: previous todos from memory", { - previousFromMemoryCount: Array.isArray(previousFromMemory) ? previousFromMemory.length : 0, - previousFromMemoryPreview: Array.isArray(previousFromMemory) - ? previousFromMemory.slice(0, 10).map((t) => summarizeTodoForDebug(t)) - : undefined, - }) const previousFromHistory = getLatestTodo(task.clineMessages) as unknown as TodoItem[] | undefined - console.log("[TODO-DEBUG] execute() STEP 3: previous todos from history", { - previousFromHistoryCount: Array.isArray(previousFromHistory) ? previousFromHistory.length : 0, - previousFromHistoryPreview: Array.isArray(previousFromHistory) - ? previousFromHistory.slice(0, 10).map((t) => summarizeTodoForDebug(t)) - : undefined, - }) const historyHasMetadata = Array.isArray(previousFromHistory) && @@ -84,21 +39,6 @@ export class UpdateTodoListTool extends BaseTool<"update_todo_list"> { t?.added !== undefined || t?.removed !== undefined, ) - console.log("[TODO-DEBUG] execute() STEP 4: analyzed history metadata", { - historyHasMetadata, - historyHasSubtaskId: Array.isArray(previousFromHistory) - ? previousFromHistory.some((t) => typeof t?.subtaskId === "string") - : false, - historyHasTokens: Array.isArray(previousFromHistory) - ? previousFromHistory.some((t) => typeof t?.tokens === "number") - : false, - historyHasCost: Array.isArray(previousFromHistory) - ? previousFromHistory.some((t) => typeof t?.cost === "number") - : false, - historyHasLineChanges: Array.isArray(previousFromHistory) - ? previousFromHistory.some((t) => typeof t?.added === "number" || typeof t?.removed === "number") - : false, - }) const previousTodos: TodoItem[] = (previousFromMemory?.length ?? 0) === 0 @@ -108,55 +48,14 @@ export class UpdateTodoListTool extends BaseTool<"update_todo_list"> { : historyHasMetadata ? (previousFromHistory ?? []) : (previousFromMemory ?? []) - console.log("[TODO-DEBUG] execute() STEP 5: selected previousTodos", { - selectedPreviousTodosCount: Array.isArray(previousTodos) ? previousTodos.length : 0, - selectedPreviousTodosWithSubtaskIdCount: Array.isArray(previousTodos) - ? previousTodos.filter((t) => typeof t?.subtaskId === "string").length - : 0, - selectedPreviousTodosPreview: Array.isArray(previousTodos) - ? previousTodos.slice(0, 10).map((t) => summarizeTodoForDebug(t)) - : undefined, - }) const todosRaw = params.todos - if (shouldTodoDebugLog) { - console.log("[TODO-DEBUG]", "UpdateTodoListTool.execute() received params.todos", { - tool: "update_todo_list", - todosRawType: typeof todosRaw, - todosRawLength: typeof todosRaw === "string" ? todosRaw.length : undefined, - todosRawPreview: typeof todosRaw === "string" ? todosRaw.slice(0, 500) : undefined, - }) - console.log("[TODO-DEBUG]", "UpdateTodoListTool.execute() previousTodos summary", { - previousFromMemoryCount: Array.isArray(previousFromMemory) ? previousFromMemory.length : 0, - previousFromHistoryCount: Array.isArray(previousFromHistory) ? previousFromHistory.length : 0, - historyHasMetadata, - selectedPreviousTodosCount: Array.isArray(previousTodos) ? previousTodos.length : 0, - previousTodosWithSubtaskIdCount: Array.isArray(previousTodos) - ? previousTodos.filter((t) => typeof t?.subtaskId === "string").length - : 0, - }) - } let todos: TodoItem[] const jsonParseResult = tryParseTodoItemsJson(todosRaw) if (jsonParseResult.parsed) { todos = jsonParseResult.parsed - console.log("[TODO-DEBUG] execute() STEP 6: parsed todos via JSON", { - parsedCount: todos.length, - parsedPreview: todos.slice(0, 10).map((t) => summarizeTodoForDebug(t)), - }) - if (shouldTodoDebugLog) { - console.log("[TODO-DEBUG]", "UpdateTodoListTool.execute() parsed todos from JSON", { - parsedCount: todos.length, - hasAnySubtaskId: todos.some((t) => typeof t?.subtaskId === "string"), - subtaskIds: todos.map((t) => t?.subtaskId).filter(Boolean), - }) - } } else if (jsonParseResult.error) { - console.log("[TODO-DEBUG] execute() STEP 6: JSON parse/validate error", { - error: jsonParseResult.error, - todosRawPreview: typeof todosRaw === "string" ? todosRaw.slice(0, 500) : undefined, - }) task.consecutiveMistakeCount++ task.recordToolError("update_todo_list") task.didToolFailInCurrentTurn = true @@ -165,52 +64,13 @@ export class UpdateTodoListTool extends BaseTool<"update_todo_list"> { } else { // Backward compatible: fall back to markdown checklist parsing when JSON parsing is not applicable. todos = parseMarkdownChecklist(todosRaw || "") - console.log("[TODO-DEBUG] execute() STEP 6: parsed todos via markdown checklist", { - parsedCount: todos.length, - parsedPreview: todos.slice(0, 10).map((t) => summarizeTodoForDebug(t)), - }) - if (shouldTodoDebugLog) { - console.log("[TODO-DEBUG]", "UpdateTodoListTool.execute() parsed todos from markdown checklist", { - parsedCount: todos.length, - hasAnySubtaskId: todos.some((t) => typeof t?.subtaskId === "string"), - }) - } } // Preserve metadata (subtaskId/tokens/cost) for todos whose content matches an existing todo. // Matching is by exact content string; duplicates are matched in order. - // NOTE: Instrumentation is enabled here (once per tool execute) to detect metadata-preservation failures. - console.log("[TODO-DEBUG] execute() STEP 7: about to call preserveTodoMetadata", { - nextTodosCount: Array.isArray(todos) ? todos.length : 0, - previousTodosCount: Array.isArray(previousTodos) ? previousTodos.length : 0, - enableInstrumentation: true, - }) - const todosWithPreservedMetadata = preserveTodoMetadata(todos, previousTodos, { - enableInstrumentation: true, - }) - console.log("[TODO-DEBUG] execute() STEP 8: returned from preserveTodoMetadata", { - resultCount: todosWithPreservedMetadata.length, - resultPreview: todosWithPreservedMetadata.slice(0, 10).map((t) => summarizeTodoForDebug(t)), - }) - if (shouldTodoDebugLog) { - console.log("[TODO-DEBUG]", "UpdateTodoListTool.execute() after preserveTodoMetadata()", { - nextTodosCount: todosWithPreservedMetadata.length, - todosWithSubtaskIdCount: todosWithPreservedMetadata.filter((t) => typeof t?.subtaskId === "string") - .length, - subtaskIds: todosWithPreservedMetadata.map((t) => t?.subtaskId).filter(Boolean), - hasAnyTokens: todosWithPreservedMetadata.some((t) => typeof t?.tokens === "number"), - hasAnyCost: todosWithPreservedMetadata.some((t) => typeof t?.cost === "number"), - hasAnyLineChanges: todosWithPreservedMetadata.some( - (t) => typeof t?.added === "number" || typeof t?.removed === "number", - ), - }) - } + const todosWithPreservedMetadata = preserveTodoMetadata(todos, previousTodos) const { valid, error } = validateTodos(todosWithPreservedMetadata) - console.log("[TODO-DEBUG] execute() STEP 9: validateTodos", { - valid, - error, - }) if (!valid) { task.consecutiveMistakeCount++ task.recordToolError("update_todo_list") @@ -229,17 +89,6 @@ export class UpdateTodoListTool extends BaseTool<"update_todo_list"> { added: t.added, removed: t.removed, })) - console.log("[TODO-DEBUG] execute() STEP 10: normalizedTodos (pre-approval)", { - normalizedCount: normalizedTodos.length, - normalizedPreview: normalizedTodos.slice(0, 10).map((t) => summarizeTodoForDebug(t)), - }) - if (shouldTodoDebugLog) { - console.log("[TODO-DEBUG]", "UpdateTodoListTool.execute() normalizedTodos (pre-approval)", { - normalizedCount: normalizedTodos.length, - todosWithSubtaskIdCount: normalizedTodos.filter((t) => typeof t?.subtaskId === "string").length, - subtaskIds: normalizedTodos.map((t) => t?.subtaskId).filter(Boolean), - }) - } const approvalMsg = JSON.stringify({ tool: "updateTodoList", @@ -248,57 +97,20 @@ export class UpdateTodoListTool extends BaseTool<"update_todo_list"> { // TodoItem is a flat object shape; a shallow copy is sufficient here. approvedTodoList = normalizedTodos.map((t) => ({ ...t })) - console.log("[TODO-DEBUG] execute() STEP 11: asking approval", { - approvalPayloadLength: approvalMsg.length, - normalizedCount: normalizedTodos.length, - }) const didApprove = await askApproval("tool", approvalMsg) - console.log("[TODO-DEBUG] execute() STEP 12: approval result", { - didApprove, - }) if (!didApprove) { - console.log("[TODO-DEBUG] execute() STEP 13: user declined; returning", {}) pushToolResult("User declined to update the todoList.") return } const isTodoListChanged = approvedTodoList !== undefined && JSON.stringify(normalizedTodos) !== JSON.stringify(approvedTodoList) - console.log("[TODO-DEBUG] execute() STEP 14: checked approval UI edits", { - isTodoListChanged, - }) if (isTodoListChanged) { normalizedTodos = approvedTodoList ?? [] - console.log("[TODO-DEBUG] execute() STEP 15: using user-edited todos", { - editedCount: normalizedTodos.length, - editedPreview: normalizedTodos.slice(0, 10).map((t) => summarizeTodoForDebug(t)), - }) - if (shouldTodoDebugLog) { - console.log("[TODO-DEBUG]", "UpdateTodoListTool.execute() user edited todos in approval UI", { - editedCount: normalizedTodos.length, - todosWithSubtaskIdCount: normalizedTodos.filter((t) => typeof t?.subtaskId === "string").length, - subtaskIds: normalizedTodos.map((t) => t?.subtaskId).filter(Boolean), - }) - } // If the user-edited todo list dropped metadata fields, re-apply metadata preservation against // the previous list (and keep any explicitly provided metadata in the edited list). - // NOTE: Do not instrument here to avoid double-logging within the same update. - console.log("[TODO-DEBUG] execute() STEP 16: about to re-call preserveTodoMetadata (user-edited)", { - enableInstrumentation: false, - }) - normalizedTodos = preserveTodoMetadata(normalizedTodos, previousTodos, { enableInstrumentation: false }) - console.log("[TODO-DEBUG] execute() STEP 17: returned from re-preserve (user-edited)", { - normalizedCount: normalizedTodos.length, - normalizedPreview: normalizedTodos.slice(0, 10).map((t) => summarizeTodoForDebug(t)), - }) - if (shouldTodoDebugLog) { - console.log("[TODO-DEBUG]", "UpdateTodoListTool.execute() normalizedTodos after re-preserve", { - normalizedCount: normalizedTodos.length, - todosWithSubtaskIdCount: normalizedTodos.filter((t) => typeof t?.subtaskId === "string").length, - subtaskIds: normalizedTodos.map((t) => t?.subtaskId).filter(Boolean), - }) - } + normalizedTodos = preserveTodoMetadata(normalizedTodos, previousTodos) task.say( "user_edit_todos", @@ -309,29 +121,15 @@ export class UpdateTodoListTool extends BaseTool<"update_todo_list"> { ) } - console.log("[TODO-DEBUG] execute() STEP 18: setting todoList on task", { - finalTodosCount: normalizedTodos.length, - }) await setTodoListForTask(task, normalizedTodos) - console.log("[TODO-DEBUG] execute() STEP 19: setTodoListForTask completed", { - taskTodoListCount: Array.isArray(task?.todoList) ? task.todoList.length : undefined, - }) if (isTodoListChanged) { const md = todoListToMarkdown(normalizedTodos) - console.log("[TODO-DEBUG] execute() STEP 20: returning tool result (user edits)", { - mdLength: md.length, - }) pushToolResult(formatResponse.toolResult("User edits todo:\n\n" + md)) } else { - console.log("[TODO-DEBUG] execute() STEP 20: returning tool result (no user edits)", {}) pushToolResult(formatResponse.toolResult("Todo list updated successfully.")) } } catch (error) { - console.log("[TODO-DEBUG] execute() STEP 99: caught error", { - error: - error instanceof Error ? { name: error.name, message: error.message, stack: error.stack } : error, - }) await handleError("update todo list", error as Error) } } @@ -350,7 +148,7 @@ export class UpdateTodoListTool extends BaseTool<"update_todo_list"> { } // Avoid log spam: partial updates can stream frequently. - todos = preserveTodoMetadata(todos, previousTodos, { enableInstrumentation: false }) + todos = preserveTodoMetadata(todos, previousTodos) const approvalMsg = JSON.stringify({ tool: "updateTodoList", @@ -445,56 +243,10 @@ function normalizeStatus(status: string | undefined): TodoStatus { * This approach ensures metadata survives status/content changes (which can alter the derived ID) * and handles duplicates deterministically. */ -function preserveTodoMetadata( - nextTodos: TodoItem[], - previousTodos: TodoItem[], - options?: { enableInstrumentation?: boolean }, -): TodoItem[] { - console.log("[TODO-DEBUG] preserveTodoMetadata() STEP 0: ENTERED", { - nextTodosCount: Array.isArray(nextTodos) ? nextTodos.length : 0, - previousTodosCount: Array.isArray(previousTodos) ? previousTodos.length : 0, - enableInstrumentationOption: options?.enableInstrumentation ?? false, - ROO_DEBUG_TODO_METADATA: process.env.ROO_DEBUG_TODO_METADATA, - ROO_CLI_DEBUG_LOG: process.env.ROO_CLI_DEBUG_LOG, - }) - - const shouldTodoDebugLogToConsole = - process.env.ROO_DEBUG_TODO_METADATA === "1" || - process.env.ROO_DEBUG_TODO_METADATA === "true" || - process.env.ROO_CLI_DEBUG_LOG === "1" - +function preserveTodoMetadata(nextTodos: TodoItem[], previousTodos: TodoItem[]): TodoItem[] { const safePrevious = previousTodos ?? [] const safeNext = nextTodos ?? [] - const summarizeTodoForDebug = (t: TodoItem | undefined) => { - if (!t) return undefined - return { - id: typeof t.id === "string" ? t.id : undefined, - status: typeof t.status === "string" ? t.status : undefined, - content: typeof t.content === "string" ? t.content.substring(0, 50) : undefined, - subtaskId: typeof t.subtaskId === "string" ? t.subtaskId : undefined, - tokens: typeof t.tokens === "number" ? t.tokens : undefined, - cost: typeof t.cost === "number" ? t.cost : undefined, - added: typeof t.added === "number" ? t.added : undefined, - removed: typeof t.removed === "number" ? t.removed : undefined, - } - } - - if (shouldTodoDebugLogToConsole) { - console.log("[TODO-DEBUG] preserveTodoMetadata INPUT", { - previousTodosCount: safePrevious.length, - previousTodos: safePrevious.map((t) => summarizeTodoForDebug(t)), - newTodosCount: safeNext.length, - newTodos: safeNext.map((t) => summarizeTodoForDebug(t)), - }) - } - - // Instrumentation must never write to stdout/stderr (CLI TUI) and should be opt-in. - // Gate it behind an env var so we don't write files during normal operation. - const enableInstrumentation = - (options?.enableInstrumentation ?? false) && - (process.env.ROO_CLI_DEBUG_LOG === "1" || process.env.ROO_DEBUG_TODO_METADATA === "1") - // Track which previous todos have been used (by their index) to avoid double-matching const usedPreviousIndices = new Set() @@ -505,22 +257,10 @@ function preserveTodoMetadata( const previousBySubtaskId = new Map>() const previousById = new Map() const previousByContent = new Map>() - - const previousMetadataIndices = new Set() for (let i = 0; i < safePrevious.length; i++) { const prev = safePrevious[i] if (!prev) continue - const hasMetadata = - prev.subtaskId !== undefined || - prev.tokens !== undefined || - prev.cost !== undefined || - prev.added !== undefined || - prev.removed !== undefined - if (hasMetadata) { - previousMetadataIndices.add(i) - } - if (typeof prev.subtaskId === "string") { const list = previousBySubtaskId.get(prev.subtaskId) if (list) list.push({ todo: prev, index: i }) @@ -552,14 +292,6 @@ function preserveTodoMetadata( continue } - if (shouldTodoDebugLogToConsole) { - console.log("[TODO-DEBUG] preserveTodoMetadata ITERATION", { - nextIndex, - next: summarizeTodoForDebug(next), - usedPreviousIndicesCount: usedPreviousIndices.size, - }) - } - let matchedPrev: TodoItem | undefined = undefined let matchedIndex: number | undefined = undefined let matchStrategy: "subtaskId" | "id" | "content" | "none" = "none" @@ -568,22 +300,7 @@ function preserveTodoMetadata( if (typeof next.subtaskId === "string") { const candidates = previousBySubtaskId.get(next.subtaskId) if (candidates) { - if (shouldTodoDebugLogToConsole) { - console.log("[TODO-DEBUG] preserveTodoMetadata subtaskId candidates", { - nextIndex, - subtaskId: next.subtaskId, - candidatesCount: candidates.length, - }) - } for (const candidate of candidates) { - if (shouldTodoDebugLogToConsole) { - console.log("[TODO-DEBUG] preserveTodoMetadata subtaskId candidate", { - nextIndex, - candidateIndex: candidate.index, - candidate: summarizeTodoForDebug(candidate.todo), - candidateAlreadyUsed: usedPreviousIndices.has(candidate.index), - }) - } if (!usedPreviousIndices.has(candidate.index)) { matchedPrev = candidate.todo matchedIndex = candidate.index @@ -609,23 +326,8 @@ function preserveTodoMetadata( const normalizedContent = normalizeTodoContentForId(next.content) const candidates = previousByContent.get(normalizedContent) if (candidates) { - if (shouldTodoDebugLogToConsole) { - console.log("[TODO-DEBUG] preserveTodoMetadata content candidates", { - nextIndex, - normalizedContent: normalizedContent.substring(0, 50), - candidatesCount: candidates.length, - }) - } // Find first unused candidate for (const candidate of candidates) { - if (shouldTodoDebugLogToConsole) { - console.log("[TODO-DEBUG] preserveTodoMetadata content candidate", { - nextIndex, - candidateIndex: candidate.index, - candidate: summarizeTodoForDebug(candidate.todo), - candidateAlreadyUsed: usedPreviousIndices.has(candidate.index), - }) - } if (!usedPreviousIndices.has(candidate.index)) { matchedPrev = candidate.todo matchedIndex = candidate.index @@ -638,23 +340,6 @@ function preserveTodoMetadata( // Mark as used and apply metadata if (matchedPrev && matchedIndex !== undefined) { - const metadataCopiedFromPrev = { - subtaskId: next.subtaskId === undefined ? matchedPrev.subtaskId : undefined, - tokens: next.tokens === undefined ? matchedPrev.tokens : undefined, - cost: next.cost === undefined ? matchedPrev.cost : undefined, - added: next.added === undefined ? matchedPrev.added : undefined, - removed: next.removed === undefined ? matchedPrev.removed : undefined, - } - if (shouldTodoDebugLogToConsole) { - console.log("[TODO-DEBUG] preserveTodoMetadata MATCH", { - nextIndex, - matchStrategy, - matchedIndex, - matchedPrev: summarizeTodoForDebug(matchedPrev), - metadataCopiedFromPrev, - }) - } - usedPreviousIndices.add(matchedIndex) matchedPreviousIndexByNextIndex[nextIndex] = matchedIndex result[nextIndex] = { @@ -668,13 +353,6 @@ function preserveTodoMetadata( continue } - if (shouldTodoDebugLogToConsole) { - console.log("[TODO-DEBUG] preserveTodoMetadata NO_MATCH", { - nextIndex, - next: summarizeTodoForDebug(next), - }) - } - result[nextIndex] = next } @@ -693,7 +371,6 @@ function preserveTodoMetadata( todoStatusSequenceMatchesByIndex(safePrevious, safeNext) if (canApplyIndexRenameCarryover) { - let indexCarryoverCount = 0 for (let i = 0; i < safeNext.length; i++) { if (matchedPreviousIndexByNextIndex[i] !== undefined) continue // already matched by stable strategy if (usedPreviousIndices.has(i)) continue // avoid double-using a previous row @@ -701,22 +378,6 @@ function preserveTodoMetadata( const next = result[i] if (!prev || !next) continue - if (shouldTodoDebugLogToConsole) { - console.log("[TODO-DEBUG] preserveTodoMetadata INDEX_CARRYOVER", { - nextIndex: i, - previousIndex: i, - prev: summarizeTodoForDebug(prev), - next: summarizeTodoForDebug(next), - metadataCopiedFromPrev: { - subtaskId: next.subtaskId === undefined ? prev.subtaskId : undefined, - tokens: next.tokens === undefined ? prev.tokens : undefined, - cost: next.cost === undefined ? prev.cost : undefined, - added: next.added === undefined ? prev.added : undefined, - removed: next.removed === undefined ? prev.removed : undefined, - }, - }) - } - result[i] = { ...next, subtaskId: next.subtaskId ?? prev.subtaskId, @@ -726,15 +387,6 @@ function preserveTodoMetadata( removed: next.removed ?? prev.removed, } usedPreviousIndices.add(i) - indexCarryoverCount++ - } - - if (enableInstrumentation && indexCarryoverCount > 0) { - appendRooCliDebugLog("[Roo-Debug] preserveTodoMetadata: applied index-based rename carryover", { - indexCarryoverCount, - previousTodosCount: safePrevious.length, - nextTodosCount: safeNext.length, - }) } } @@ -772,57 +424,6 @@ function preserveTodoMetadata( result[targetNextIndex] = updatedTarget usedPreviousIndices.add(orphanedPrevIndex) matchedPreviousIndexByNextIndex[targetNextIndex] = orphanedPrevIndex - - if (shouldTodoDebugLogToConsole) { - console.log("[TODO-DEBUG] preserveTodoMetadata ORPHAN_CARRYOVER", { - orphanedContent: orphanedPrev.content?.substring(0, 40), - orphanedSubtaskId: orphanedPrev.subtaskId, - targetContent: updatedTarget.content?.substring(0, 40), - copiedFields: { - subtaskId: updatedTarget.subtaskId, - tokens: updatedTarget.tokens, - cost: updatedTarget.cost, - }, - }) - } - } - - // Lightweight debug instrumentation: detect when previous rows that had metadata could not be - // matched to any next todo row (and therefore their metadata could not be preserved). - // - // Keep payload minimal to avoid logging user content. - if (enableInstrumentation && previousMetadataIndices.size > 0) { - let lostMetadataRowCount = 0 - for (const prevIndex of previousMetadataIndices) { - if (!usedPreviousIndices.has(prevIndex)) { - lostMetadataRowCount++ - } - } - - if (lostMetadataRowCount > 0) { - if (shouldTodoDebugLogToConsole) { - console.log("[TODO-DEBUG] preserveTodoMetadata LOST_METADATA", { - lostMetadataRowCount, - previousMetadataRowCount: previousMetadataIndices.size, - previousTodosCount: safePrevious.length, - nextTodosCount: safeNext.length, - }) - } - // IMPORTANT: do not use console.log here in the CLI TUI. It can corrupt rendering (e.g. dropdowns). - appendRooCliDebugLog("[Roo-Debug] preserveTodoMetadata: previous todo(s) with metadata were not matched", { - lostMetadataRowCount, - previousMetadataRowCount: previousMetadataIndices.size, - previousTodosCount: safePrevious.length, - nextTodosCount: safeNext.length, - }) - } - } - - if (shouldTodoDebugLogToConsole) { - console.log("[TODO-DEBUG] preserveTodoMetadata OUTPUT", { - resultCount: result.length, - resultTodos: result.map((t) => summarizeTodoForDebug(t)), - }) } return result @@ -839,19 +440,6 @@ function todoStatusSequenceMatchesByIndex(previous: TodoItem[], next: TodoItem[] } return true } - -const ROO_CLI_DEBUG_LOG_PATH = path.join(os.tmpdir(), "roo-cli-debug.log") - -function appendRooCliDebugLog(message: string, data?: unknown) { - try { - const timestamp = new Date().toISOString() - const entry = data ? `[${timestamp}] ${message}: ${JSON.stringify(data)}\n` : `[${timestamp}] ${message}\n` - fs.appendFileSync(ROO_CLI_DEBUG_LOG_PATH, entry) - } catch { - // Swallow errors: logging must never break tool execution. - } -} - export function parseMarkdownChecklist(md: string): TodoItem[] { if (typeof md !== "string") return [] const lines = md diff --git a/src/shared/todo.ts b/src/shared/todo.ts index 3f2ca0c3fa..d2a01225d7 100644 --- a/src/shared/todo.ts +++ b/src/shared/todo.ts @@ -2,7 +2,6 @@ import { ClineMessage, TodoItem } from "@roo-code/types" export function getLatestTodo(clineMessages: ClineMessage[]): TodoItem[] { if (!Array.isArray(clineMessages) || clineMessages.length === 0) { - console.log("[TODO-DEBUG]", "getLatestTodo called with empty clineMessages") return [] } @@ -31,21 +30,5 @@ export function getLatestTodo(clineMessages: ClineMessage[]): TodoItem[] { } } - console.log("[TODO-DEBUG]", "getLatestTodo scanned messages", { - totalMessages: clineMessages.length, - candidateMessages: candidateMessages.length, - matchedUpdateTodoListCount, - parseFailureCount, - returnedTodosCount: Array.isArray(lastTodos) ? lastTodos.length : 0, - // Only log lightweight metadata for the last few candidates (avoid dumping full message content) - lastCandidates: candidateMessages.slice(-5).map((m) => ({ - ts: m.ts, - type: m.type, - ask: (m as any).ask, - say: (m as any).say, - textLength: typeof m.text === "string" ? m.text.length : 0, - })), - }) - return Array.isArray(lastTodos) ? lastTodos : [] } diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 3a88223190..9fbcffcfef 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -397,18 +397,8 @@ export const ChatRowContent = ({ if (message.ask !== "tool") return null const parsed = safeJsonParse(message.text) - // TODO debugging: verify tool JSON is actually being parsed in the webview. - if ((parsed as any)?.tool === "updateTodoList") { - console.log("[TODO-DEBUG]", "ChatRow parsed tool JSON", { - messageTs: message.ts, - toolName: (parsed as any)?.tool, - newTodosCount: Array.isArray((parsed as any)?.todos) ? (parsed as any).todos.length : undefined, - parsed, - }) - } - return parsed - }, [message.ask, message.text, message.ts]) + }, [message.ask, message.text]) // Unified diff content (provided by backend when relevant) const unifiedDiff = useMemo(() => { @@ -581,15 +571,6 @@ export const ChatRowContent = ({ // Get previous todos from the latest todos in the task context const previousTodos = getPreviousTodos(clineMessages, message.ts) - console.log("[TODO-DEBUG]", "ChatRow rendering TodoChangeDisplay", { - messageTs: message.ts, - previousTodosCount: Array.isArray(previousTodos) ? previousTodos.length : undefined, - newTodosCount: Array.isArray(todos) ? todos.length : undefined, - previousTodos, - newTodos: todos, - parsedTool: tool, - }) - return } case "newFileCreated": diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index 17ec30d70f..0786699ca1 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -126,13 +126,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction { - // TODO debugging: ensure todo extraction runs and surfaces state that should drive UI. - console.log("[TODO-DEBUG]", "ChatView latestTodos computed", { - messagesCount: Array.isArray(messages) ? messages.length : undefined, - currentTaskTodosCount: Array.isArray(currentTaskTodos) ? currentTaskTodos.length : undefined, - latestTodosCount: Array.isArray(latestTodos) ? latestTodos.length : undefined, - latestTodos, - }) + // Intentionally left blank: keep dependencies so todo extraction logic remains reactive. }, [messages, currentTaskTodos, latestTodos]) const modifiedMessages = useMemo(() => combineApiRequests(combineCommandSequences(messages.slice(1))), [messages]) diff --git a/webview-ui/src/components/chat/TodoChangeDisplay.tsx b/webview-ui/src/components/chat/TodoChangeDisplay.tsx index d2043f435b..e5f9f86043 100644 --- a/webview-ui/src/components/chat/TodoChangeDisplay.tsx +++ b/webview-ui/src/components/chat/TodoChangeDisplay.tsx @@ -26,17 +26,6 @@ function getTodoIcon(status: TodoStatus | null) { } export function TodoChangeDisplay({ previousTodos, newTodos }: TodoChangeDisplayProps) { - console.log("[TODO-DEBUG]", "TodoChangeDisplay compare todos", { - previousTodosCount: Array.isArray(previousTodos) ? previousTodos.length : 0, - newTodosCount: Array.isArray(newTodos) ? newTodos.length : 0, - previousTodos: Array.isArray(previousTodos) - ? previousTodos.map((t) => ({ id: t.id, content: t.content, status: t.status })) - : [], - newTodos: Array.isArray(newTodos) - ? newTodos.map((t) => ({ id: t.id, content: t.content, status: t.status })) - : [], - }) - const isInitialState = previousTodos.length === 0 // Determine which todos to display @@ -45,45 +34,21 @@ export function TodoChangeDisplay({ previousTodos, newTodos }: TodoChangeDisplay if (isInitialState && newTodos.length > 0) { // For initial state, show all todos in their original order todosToDisplay = newTodos - console.log("[TODO-DEBUG]", "TodoChangeDisplay selection: initial state -> show all newTodos", { - todosToDisplayCount: todosToDisplay.length, - }) } else { // For updates, only show changes (completed or started) in their original order todosToDisplay = newTodos.filter((newTodo) => { if (newTodo.status === "completed") { const previousTodo = previousTodos.find((p) => p.id === newTodo.id || p.content === newTodo.content) const include = !previousTodo || previousTodo.status !== "completed" - console.log("[TODO-DEBUG]", "TodoChangeDisplay selection: completed todo", { - newTodo: { id: newTodo.id, content: newTodo.content, status: newTodo.status }, - matchedPreviousTodo: previousTodo - ? { id: previousTodo.id, content: previousTodo.content, status: previousTodo.status } - : undefined, - include, - }) return include } if (newTodo.status === "in_progress") { const previousTodo = previousTodos.find((p) => p.id === newTodo.id || p.content === newTodo.content) const include = !previousTodo || previousTodo.status !== "in_progress" - console.log("[TODO-DEBUG]", "TodoChangeDisplay selection: in_progress todo", { - newTodo: { id: newTodo.id, content: newTodo.content, status: newTodo.status }, - matchedPreviousTodo: previousTodo - ? { id: previousTodo.id, content: previousTodo.content, status: previousTodo.status } - : undefined, - include, - }) return include } - console.log("[TODO-DEBUG]", "TodoChangeDisplay selection: ignored todo (not completed/in_progress)", { - newTodo: { id: newTodo.id, content: newTodo.content, status: newTodo.status }, - }) return false }) - console.log("[TODO-DEBUG]", "TodoChangeDisplay selection result", { - todosToDisplayCount: todosToDisplay.length, - todosToDisplay: todosToDisplay.map((t) => ({ id: t.id, content: t.content, status: t.status })), - }) } // If no todos to display, don't render anything diff --git a/webview-ui/src/components/chat/TodoListDisplay.tsx b/webview-ui/src/components/chat/TodoListDisplay.tsx index 902206d66f..c3da1c717b 100644 --- a/webview-ui/src/components/chat/TodoListDisplay.tsx +++ b/webview-ui/src/components/chat/TodoListDisplay.tsx @@ -41,16 +41,6 @@ export interface TodoListDisplayProps { } export function TodoListDisplay({ todos, subtaskDetails, onSubtaskClick }: TodoListDisplayProps) { - useEffect(() => { - console.log("[TODO-DEBUG]", "TodoListDisplay props received", { - todosCount: Array.isArray(todos) ? todos.length : 0, - todoSubtaskIds: Array.isArray(todos) ? todos.map((t) => t?.subtaskId).filter(Boolean) : [], - subtaskDetailsCount: Array.isArray(subtaskDetails) ? subtaskDetails.length : 0, - subtaskDetailsIds: Array.isArray(subtaskDetails) ? subtaskDetails.map((s) => s?.id).filter(Boolean) : [], - hasOnSubtaskClick: Boolean(onSubtaskClick), - }) - }, [todos, subtaskDetails, onSubtaskClick]) - const [isCollapsed, setIsCollapsed] = useState(true) const ulRef = useRef(null) const itemRefs = useRef<(HTMLLIElement | null)[]>([]) @@ -118,25 +108,10 @@ export function TodoListDisplay({ todos, subtaskDetails, onSubtaskClick }: TodoL const todoStatus = (todo.status as TodoStatus) ?? "pending" const icon = getTodoIcon(todoStatus) const isClickable = Boolean(todo.subtaskId && onSubtaskClick) - console.log("[TODO-DEBUG]", "TodoListDisplay subtask match start", { - todoIndex: idx, - todoId: todo.id, - todoContent: todo.content, - todoSubtaskId: todo.subtaskId, - availableSubtaskDetailIds: Array.isArray(subtaskDetails) - ? subtaskDetails.map((s) => s?.id).filter(Boolean) - : [], - }) const subtaskById = subtaskDetails && todo.subtaskId ? subtaskDetails.find((s) => s.id === todo.subtaskId) : undefined - console.log("[TODO-DEBUG]", "TodoListDisplay subtask match result", { - todoIndex: idx, - todoSubtaskId: todo.subtaskId, - matched: Boolean(subtaskById), - matchedSubtaskId: subtaskById?.id, - }) const displayTokens = todo.tokens ?? subtaskById?.tokens const displayCost = todo.cost ?? subtaskById?.cost const shouldShowCost = typeof displayTokens === "number" && typeof displayCost === "number" @@ -164,33 +139,6 @@ export function TodoListDisplay({ todos, subtaskDetails, onSubtaskClick }: TodoL const shouldShowLineChanges = hasValidSubtaskLink && (canRenderAdded || canRenderRemoved) - console.log("[TODO-DEBUG]", "TodoListDisplay metadata computed", { - todoIndex: idx, - todoSubtaskId: todo.subtaskId, - fromTodo: { - tokens: todo.tokens, - cost: todo.cost, - added: todo.added, - removed: todo.removed, - }, - fromSubtaskDetails: subtaskById - ? { - tokens: subtaskById.tokens, - cost: subtaskById.cost, - added: subtaskById.added, - removed: subtaskById.removed, - } - : undefined, - display: { - displayTokens, - displayCost, - displayAdded, - displayRemoved, - }, - shouldShowCost, - shouldShowLineChanges, - }) - const isAddedPositive = canRenderAdded && (displayAdded as number) > 0 const isRemovedPositive = canRenderRemoved && (displayRemoved as number) > 0 const isAddedZero = canRenderAdded && displayAdded === 0