From 60f0a036a717a65fb35be4819785ea098b927d14 Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Wed, 23 Jul 2025 14:41:33 +0200 Subject: [PATCH 1/6] Fix token usage / cost often being underreported --- src/core/task/Task.ts | 67 ++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index ccd24b7d71..930aa328ba 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -1360,7 +1360,11 @@ export class Task extends EventEmitter { this.isStreaming = true try { - for await (const chunk of stream) { + const iterator = stream[Symbol.asyncIterator]() + let item = await iterator.next() + while (!item.done) { + const chunk = item.value + item = await iterator.next() if (!chunk) { // Sometimes chunk is undefined, no idea that can cause // it, but this workaround seems to fix it. @@ -1423,16 +1427,51 @@ export class Task extends EventEmitter { break } - // PREV: We need to let the request finish for openrouter to - // get generation details. - // UPDATE: It's better UX to interrupt the request at the - // cost of the API cost not being retrieved. if (this.didAlreadyUseTool) { assistantMessage += "\n\n[Response interrupted by a tool use result. Only one tool may be used at a time and should be placed at the end of the message.]" break } } + + const drainStreamInBackgroundToFindAllUsage = async () => { + let usageFound = false + while (!item.done) { + const chunk = item.value + item = await iterator.next() + if (chunk && chunk.type === "usage") { + usageFound = true + inputTokens += chunk.inputTokens + outputTokens += chunk.outputTokens + cacheWriteTokens += chunk.cacheWriteTokens ?? 0 + cacheReadTokens += chunk.cacheReadTokens ?? 0 + totalCost = chunk.totalCost + } + } + if (usageFound) { + updateApiReqMsg() + } + if (inputTokens > 0 || outputTokens > 0 || cacheWriteTokens > 0 || cacheReadTokens > 0) { + TelemetryService.instance.captureLlmCompletion(this.taskId, { + inputTokens, + outputTokens, + cacheWriteTokens, + cacheReadTokens, + cost: + totalCost ?? + calculateApiCostAnthropic( + this.api.getModel().info, + inputTokens, + outputTokens, + cacheWriteTokens, + cacheReadTokens, + ), + }) + } else { + console.warn(`Suspicious: request ${lastApiReqIndex} is complete, but no usage info was found.`) + } + } + drainStreamInBackgroundToFindAllUsage() // no await so it runs in the background } catch (error) { // Abandoned happens when extension is no longer waiting for the // Cline instance to finish aborting (error is thrown here when @@ -1466,24 +1505,6 @@ export class Task extends EventEmitter { this.isStreaming = false } - if (inputTokens > 0 || outputTokens > 0 || cacheWriteTokens > 0 || cacheReadTokens > 0) { - TelemetryService.instance.captureLlmCompletion(this.taskId, { - inputTokens, - outputTokens, - cacheWriteTokens, - cacheReadTokens, - cost: - totalCost ?? - calculateApiCostAnthropic( - this.api.getModel().info, - inputTokens, - outputTokens, - cacheWriteTokens, - cacheReadTokens, - ), - }) - } - // Need to call here in case the stream was aborted. if (this.abort || this.abandoned) { throw new Error(`[RooCode#recursivelyMakeRooRequests] task ${this.taskId}.${this.instanceId} aborted`) From c4b14c596c1c542dfc62c53b65325047b32d256b Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Fri, 25 Jul 2025 14:52:15 -0500 Subject: [PATCH 2/6] fix: address PR review feedback for underreported token usage - Add error handling in drainStreamInBackgroundToFindAllUsage with try-catch - Add 30-second timeout to prevent potential memory leaks from hanging streams - Include model ID in console warning for better debugging - Fix race condition by moving updateApiReqMsg() call inside background task - Ensure saveClineMessages() is called after usage data is updated --- src/core/task/Task.ts | 106 ++++++++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 35 deletions(-) diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 930aa328ba..6a6862469c 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -1435,43 +1435,78 @@ export class Task extends EventEmitter { } const drainStreamInBackgroundToFindAllUsage = async () => { - let usageFound = false - while (!item.done) { - const chunk = item.value - item = await iterator.next() - if (chunk && chunk.type === "usage") { - usageFound = true - inputTokens += chunk.inputTokens - outputTokens += chunk.outputTokens - cacheWriteTokens += chunk.cacheWriteTokens ?? 0 - cacheReadTokens += chunk.cacheReadTokens ?? 0 - totalCost = chunk.totalCost + const timeoutMs = 30000 // 30 second timeout + const startTime = Date.now() + + try { + let usageFound = false + while (!item.done) { + // Check for timeout + if (Date.now() - startTime > timeoutMs) { + console.warn(`Background usage collection timed out after ${timeoutMs}ms`) + break + } + + const chunk = item.value + item = await iterator.next() + if (chunk && chunk.type === "usage") { + usageFound = true + inputTokens += chunk.inputTokens + outputTokens += chunk.outputTokens + cacheWriteTokens += chunk.cacheWriteTokens ?? 0 + cacheReadTokens += chunk.cacheReadTokens ?? 0 + totalCost = chunk.totalCost + } + } + if (usageFound) { + updateApiReqMsg() + await this.saveClineMessages() + } + if (inputTokens > 0 || outputTokens > 0 || cacheWriteTokens > 0 || cacheReadTokens > 0) { + TelemetryService.instance.captureLlmCompletion(this.taskId, { + inputTokens, + outputTokens, + cacheWriteTokens, + cacheReadTokens, + cost: + totalCost ?? + calculateApiCostAnthropic( + this.api.getModel().info, + inputTokens, + outputTokens, + cacheWriteTokens, + cacheReadTokens, + ), + }) + } else { + const modelId = getModelId(this.apiConfiguration) + console.warn( + `Suspicious: request ${lastApiReqIndex} is complete, but no usage info was found. Model: ${modelId}`, + ) + } + } catch (error) { + console.error("Error draining stream for usage data:", error) + // Still try to capture whatever usage data we have collected so far + if (inputTokens > 0 || outputTokens > 0 || cacheWriteTokens > 0 || cacheReadTokens > 0) { + TelemetryService.instance.captureLlmCompletion(this.taskId, { + inputTokens, + outputTokens, + cacheWriteTokens, + cacheReadTokens, + cost: + totalCost ?? + calculateApiCostAnthropic( + this.api.getModel().info, + inputTokens, + outputTokens, + cacheWriteTokens, + cacheReadTokens, + ), + }) } - } - if (usageFound) { - updateApiReqMsg() - } - if (inputTokens > 0 || outputTokens > 0 || cacheWriteTokens > 0 || cacheReadTokens > 0) { - TelemetryService.instance.captureLlmCompletion(this.taskId, { - inputTokens, - outputTokens, - cacheWriteTokens, - cacheReadTokens, - cost: - totalCost ?? - calculateApiCostAnthropic( - this.api.getModel().info, - inputTokens, - outputTokens, - cacheWriteTokens, - cacheReadTokens, - ), - }) - } else { - console.warn(`Suspicious: request ${lastApiReqIndex} is complete, but no usage info was found.`) } } - drainStreamInBackgroundToFindAllUsage() // no await so it runs in the background + const backgroundDrainPromise = drainStreamInBackgroundToFindAllUsage() // Store promise reference } catch (error) { // Abandoned happens when extension is no longer waiting for the // Cline instance to finish aborting (error is thrown here when @@ -1534,7 +1569,8 @@ export class Task extends EventEmitter { presentAssistantMessage(this) } - updateApiReqMsg() + // Note: updateApiReqMsg() is now called from within drainStreamInBackgroundToFindAllUsage + // to avoid race conditions with the background task await this.saveClineMessages() await this.providerRef.deref()?.postStateToWebview() From e5acc42b1806020d9992914cb606f2d6cdcf73b2 Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Sat, 26 Jul 2025 14:02:02 -0500 Subject: [PATCH 3/6] fix: improve token usage collection reliability and fix race conditions - Handle unhandled promise rejection by adding .catch() to background task - Fix race condition by using local variables in background task before atomically updating shared state - Eliminate duplicate telemetry capture logic with captureUsageData helper function - Make usage collection timeout configurable via CLINE_USAGE_COLLECTION_TIMEOUT env var (default: 30s) - Improve code organization and readability - Include model ID in all warning messages for better debugging --- src/core/task/Task.ts | 149 ++++++++++++++++++++++++++++++------------ 1 file changed, 106 insertions(+), 43 deletions(-) diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 6a6862469c..ac56d7ff05 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -1434,16 +1434,75 @@ export class Task extends EventEmitter { } } + // Create a copy of current token values to avoid race conditions + const currentTokens = { + input: inputTokens, + output: outputTokens, + cacheWrite: cacheWriteTokens, + cacheRead: cacheReadTokens, + total: totalCost, + } + const drainStreamInBackgroundToFindAllUsage = async () => { - const timeoutMs = 30000 // 30 second timeout + // Make timeout configurable via environment variable or use default + const timeoutMs = parseInt(process.env.CLINE_USAGE_COLLECTION_TIMEOUT || "30000", 10) const startTime = Date.now() + const modelId = getModelId(this.apiConfiguration) + + // Local variables to accumulate usage data without affecting the main flow + let bgInputTokens = currentTokens.input + let bgOutputTokens = currentTokens.output + let bgCacheWriteTokens = currentTokens.cacheWrite + let bgCacheReadTokens = currentTokens.cacheRead + let bgTotalCost = currentTokens.total + + // Helper function to capture telemetry and update messages + const captureUsageData = async (tokens: { + input: number + output: number + cacheWrite: number + cacheRead: number + total?: number + }) => { + if (tokens.input > 0 || tokens.output > 0 || tokens.cacheWrite > 0 || tokens.cacheRead > 0) { + // Update the shared variables atomically + inputTokens = tokens.input + outputTokens = tokens.output + cacheWriteTokens = tokens.cacheWrite + cacheReadTokens = tokens.cacheRead + totalCost = tokens.total + + // Update the API request message with the latest usage data + updateApiReqMsg() + await this.saveClineMessages() + + // Capture telemetry + TelemetryService.instance.captureLlmCompletion(this.taskId, { + inputTokens: tokens.input, + outputTokens: tokens.output, + cacheWriteTokens: tokens.cacheWrite, + cacheReadTokens: tokens.cacheRead, + cost: + tokens.total ?? + calculateApiCostAnthropic( + this.api.getModel().info, + tokens.input, + tokens.output, + tokens.cacheWrite, + tokens.cacheRead, + ), + }) + } + } try { let usageFound = false while (!item.done) { // Check for timeout if (Date.now() - startTime > timeoutMs) { - console.warn(`Background usage collection timed out after ${timeoutMs}ms`) + console.warn( + `Background usage collection timed out after ${timeoutMs}ms for model: ${modelId}`, + ) break } @@ -1451,35 +1510,37 @@ export class Task extends EventEmitter { item = await iterator.next() if (chunk && chunk.type === "usage") { usageFound = true - inputTokens += chunk.inputTokens - outputTokens += chunk.outputTokens - cacheWriteTokens += chunk.cacheWriteTokens ?? 0 - cacheReadTokens += chunk.cacheReadTokens ?? 0 - totalCost = chunk.totalCost + bgInputTokens += chunk.inputTokens + bgOutputTokens += chunk.outputTokens + bgCacheWriteTokens += chunk.cacheWriteTokens ?? 0 + bgCacheReadTokens += chunk.cacheReadTokens ?? 0 + bgTotalCost = chunk.totalCost } } + if (usageFound) { - updateApiReqMsg() - await this.saveClineMessages() - } - if (inputTokens > 0 || outputTokens > 0 || cacheWriteTokens > 0 || cacheReadTokens > 0) { - TelemetryService.instance.captureLlmCompletion(this.taskId, { - inputTokens, - outputTokens, - cacheWriteTokens, - cacheReadTokens, - cost: - totalCost ?? - calculateApiCostAnthropic( - this.api.getModel().info, - inputTokens, - outputTokens, - cacheWriteTokens, - cacheReadTokens, - ), + await captureUsageData({ + input: bgInputTokens, + output: bgOutputTokens, + cacheWrite: bgCacheWriteTokens, + cacheRead: bgCacheReadTokens, + total: bgTotalCost, + }) + } else if ( + bgInputTokens > 0 || + bgOutputTokens > 0 || + bgCacheWriteTokens > 0 || + bgCacheReadTokens > 0 + ) { + // We have some usage data even if we didn't find a usage chunk + await captureUsageData({ + input: bgInputTokens, + output: bgOutputTokens, + cacheWrite: bgCacheWriteTokens, + cacheRead: bgCacheReadTokens, + total: bgTotalCost, }) } else { - const modelId = getModelId(this.apiConfiguration) console.warn( `Suspicious: request ${lastApiReqIndex} is complete, but no usage info was found. Model: ${modelId}`, ) @@ -1487,26 +1548,27 @@ export class Task extends EventEmitter { } catch (error) { console.error("Error draining stream for usage data:", error) // Still try to capture whatever usage data we have collected so far - if (inputTokens > 0 || outputTokens > 0 || cacheWriteTokens > 0 || cacheReadTokens > 0) { - TelemetryService.instance.captureLlmCompletion(this.taskId, { - inputTokens, - outputTokens, - cacheWriteTokens, - cacheReadTokens, - cost: - totalCost ?? - calculateApiCostAnthropic( - this.api.getModel().info, - inputTokens, - outputTokens, - cacheWriteTokens, - cacheReadTokens, - ), + if ( + bgInputTokens > 0 || + bgOutputTokens > 0 || + bgCacheWriteTokens > 0 || + bgCacheReadTokens > 0 + ) { + await captureUsageData({ + input: bgInputTokens, + output: bgOutputTokens, + cacheWrite: bgCacheWriteTokens, + cacheRead: bgCacheReadTokens, + total: bgTotalCost, }) } } } - const backgroundDrainPromise = drainStreamInBackgroundToFindAllUsage() // Store promise reference + + // Start the background task and handle any errors + const backgroundDrainPromise = drainStreamInBackgroundToFindAllUsage().catch((error) => { + console.error("Background usage collection failed:", error) + }) } catch (error) { // Abandoned happens when extension is no longer waiting for the // Cline instance to finish aborting (error is thrown here when @@ -1570,7 +1632,8 @@ export class Task extends EventEmitter { } // Note: updateApiReqMsg() is now called from within drainStreamInBackgroundToFindAllUsage - // to avoid race conditions with the background task + // to ensure usage data is captured even when the stream is interrupted. The background task + // uses local variables to accumulate usage data before atomically updating the shared state. await this.saveClineMessages() await this.providerRef.deref()?.postStateToWebview() From 986ee81045107c2c57eb85d02670e157b69333eb Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Sat, 26 Jul 2025 18:57:10 -0500 Subject: [PATCH 4/6] fix: improve token usage collection with proper error handling and race condition fixes - Add proper error handling for background promise with .catch() - Fix race condition by using local variables in background task - Make timeout configurable via DEFAULT_USAGE_COLLECTION_TIMEOUT_MS constant - Extract captureUsageData helper for better code organization - Ensure model ID is included in warning messages - Update comments to reflect the actual implementation --- packages/types/src/global-settings.ts | 7 ++ src/core/task/Task.ts | 93 +++++++++++++++++---------- 2 files changed, 66 insertions(+), 34 deletions(-) diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index 906949919a..35ebbb88a4 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -29,6 +29,13 @@ export const DEFAULT_WRITE_DELAY_MS = 1000 */ export const DEFAULT_TERMINAL_OUTPUT_CHARACTER_LIMIT = 50_000 +/** + * Default timeout for background usage collection in milliseconds. + * This timeout prevents the background task from running indefinitely + * when collecting usage data from streaming API responses. + */ +export const DEFAULT_USAGE_COLLECTION_TIMEOUT_MS = 30_000 + /** * GlobalSettings */ diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index ac56d7ff05..e97b7662ff 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -19,6 +19,7 @@ import { type ClineSay, type ToolProgressStatus, DEFAULT_CONSECUTIVE_MISTAKE_LIMIT, + DEFAULT_USAGE_COLLECTION_TIMEOUT_MS, type HistoryItem, TelemetryEventName, TodoItem, @@ -1274,6 +1275,10 @@ export class Task extends EventEmitter { // of prices in tasks from history (it's worth removing a few months // from now). const updateApiReqMsg = (cancelReason?: ClineApiReqCancelReason, streamingFailedMessage?: string) => { + if (lastApiReqIndex < 0 || !this.clineMessages[lastApiReqIndex]) { + return + } + const existingData = JSON.parse(this.clineMessages[lastApiReqIndex].text || "{}") this.clineMessages[lastApiReqIndex].text = JSON.stringify({ ...existingData, @@ -1443,9 +1448,8 @@ export class Task extends EventEmitter { total: totalCost, } - const drainStreamInBackgroundToFindAllUsage = async () => { - // Make timeout configurable via environment variable or use default - const timeoutMs = parseInt(process.env.CLINE_USAGE_COLLECTION_TIMEOUT || "30000", 10) + const drainStreamInBackgroundToFindAllUsage = async (apiReqIndex: number) => { + const timeoutMs = DEFAULT_USAGE_COLLECTION_TIMEOUT_MS const startTime = Date.now() const modelId = getModelId(this.apiConfiguration) @@ -1457,13 +1461,16 @@ export class Task extends EventEmitter { let bgTotalCost = currentTokens.total // Helper function to capture telemetry and update messages - const captureUsageData = async (tokens: { - input: number - output: number - cacheWrite: number - cacheRead: number - total?: number - }) => { + const captureUsageData = async ( + tokens: { + input: number + output: number + cacheWrite: number + cacheRead: number + total?: number + }, + messageIndex: number = apiReqIndex, + ) => { if (tokens.input > 0 || tokens.output > 0 || tokens.cacheWrite > 0 || tokens.cacheRead > 0) { // Update the shared variables atomically inputTokens = tokens.input @@ -1476,6 +1483,12 @@ export class Task extends EventEmitter { updateApiReqMsg() await this.saveClineMessages() + // Update the specific message in the webview + const apiReqMessage = this.clineMessages[messageIndex] + if (apiReqMessage) { + await this.updateClineMessage(apiReqMessage) + } + // Capture telemetry TelemetryService.instance.captureLlmCompletion(this.taskId, { inputTokens: tokens.input, @@ -1497,17 +1510,20 @@ export class Task extends EventEmitter { try { let usageFound = false + let chunkCount = 0 while (!item.done) { // Check for timeout if (Date.now() - startTime > timeoutMs) { console.warn( - `Background usage collection timed out after ${timeoutMs}ms for model: ${modelId}`, + `[Background Usage Collection] Timed out after ${timeoutMs}ms for model: ${modelId}, processed ${chunkCount} chunks`, ) break } const chunk = item.value item = await iterator.next() + chunkCount++ + if (chunk && chunk.type === "usage") { usageFound = true bgInputTokens += chunk.inputTokens @@ -1519,13 +1535,16 @@ export class Task extends EventEmitter { } if (usageFound) { - await captureUsageData({ - input: bgInputTokens, - output: bgOutputTokens, - cacheWrite: bgCacheWriteTokens, - cacheRead: bgCacheReadTokens, - total: bgTotalCost, - }) + await captureUsageData( + { + input: bgInputTokens, + output: bgOutputTokens, + cacheWrite: bgCacheWriteTokens, + cacheRead: bgCacheReadTokens, + total: bgTotalCost, + }, + lastApiReqIndex, + ) } else if ( bgInputTokens > 0 || bgOutputTokens > 0 || @@ -1533,16 +1552,19 @@ export class Task extends EventEmitter { bgCacheReadTokens > 0 ) { // We have some usage data even if we didn't find a usage chunk - await captureUsageData({ - input: bgInputTokens, - output: bgOutputTokens, - cacheWrite: bgCacheWriteTokens, - cacheRead: bgCacheReadTokens, - total: bgTotalCost, - }) + await captureUsageData( + { + input: bgInputTokens, + output: bgOutputTokens, + cacheWrite: bgCacheWriteTokens, + cacheRead: bgCacheReadTokens, + total: bgTotalCost, + }, + lastApiReqIndex, + ) } else { console.warn( - `Suspicious: request ${lastApiReqIndex} is complete, but no usage info was found. Model: ${modelId}`, + `[Background Usage Collection] Suspicious: request ${apiReqIndex} is complete, but no usage info was found. Model: ${modelId}`, ) } } catch (error) { @@ -1554,19 +1576,22 @@ export class Task extends EventEmitter { bgCacheWriteTokens > 0 || bgCacheReadTokens > 0 ) { - await captureUsageData({ - input: bgInputTokens, - output: bgOutputTokens, - cacheWrite: bgCacheWriteTokens, - cacheRead: bgCacheReadTokens, - total: bgTotalCost, - }) + await captureUsageData( + { + input: bgInputTokens, + output: bgOutputTokens, + cacheWrite: bgCacheWriteTokens, + cacheRead: bgCacheReadTokens, + total: bgTotalCost, + }, + lastApiReqIndex, + ) } } } // Start the background task and handle any errors - const backgroundDrainPromise = drainStreamInBackgroundToFindAllUsage().catch((error) => { + drainStreamInBackgroundToFindAllUsage(lastApiReqIndex).catch((error) => { console.error("Background usage collection failed:", error) }) } catch (error) { From e8a6ac5fa1ff18cca2038de4e035d1e25d0202c1 Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Mon, 28 Jul 2025 12:31:01 -0500 Subject: [PATCH 5/6] fix: combine redundant conditional branches in drainStreamInBackgroundToFindAllUsage - Merged if (usageFound) and else if blocks that both called captureUsageData with identical parameters into a single condition - Eliminates code duplication and improves maintainability --- src/core/task/Task.ts | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index e97b7662ff..e449421172 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -1509,14 +1509,21 @@ export class Task extends EventEmitter { } try { + // Continue processing the original stream from where the main loop left off let usageFound = false let chunkCount = 0 + + // Use the same iterator that the main loop was using while (!item.done) { // Check for timeout if (Date.now() - startTime > timeoutMs) { console.warn( `[Background Usage Collection] Timed out after ${timeoutMs}ms for model: ${modelId}, processed ${chunkCount} chunks`, ) + // Clean up the iterator before breaking + if (iterator.return) { + await iterator.return(undefined) + } break } @@ -1534,24 +1541,14 @@ export class Task extends EventEmitter { } } - if (usageFound) { - await captureUsageData( - { - input: bgInputTokens, - output: bgOutputTokens, - cacheWrite: bgCacheWriteTokens, - cacheRead: bgCacheReadTokens, - total: bgTotalCost, - }, - lastApiReqIndex, - ) - } else if ( + if ( + usageFound || bgInputTokens > 0 || bgOutputTokens > 0 || bgCacheWriteTokens > 0 || bgCacheReadTokens > 0 ) { - // We have some usage data even if we didn't find a usage chunk + // We have usage data either from a usage chunk or accumulated tokens await captureUsageData( { input: bgInputTokens, From fa01bd98949669a719580c3848d5910cea87f0df Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Thu, 7 Aug 2025 14:57:15 +0200 Subject: [PATCH 6/6] Update Task.ts --- src/core/task/Task.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 2e57778724..df2c22bae9 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -22,8 +22,6 @@ import { type ClineAsk, type BlockingAsk, type ToolProgressStatus, - DEFAULT_CONSECUTIVE_MISTAKE_LIMIT, - DEFAULT_USAGE_COLLECTION_TIMEOUT_MS, type HistoryItem, RooCodeEventName, TelemetryEventName, @@ -31,6 +29,7 @@ import { getApiProtocol, getModelId, DEFAULT_CONSECUTIVE_MISTAKE_LIMIT, + DEFAULT_USAGE_COLLECTION_TIMEOUT_MS, isBlockingAsk, } from "@roo-code/types" import { TelemetryService } from "@roo-code/telemetry"