From 8734faff54c3b229267ce14878cc4c17727b2090 Mon Sep 17 00:00:00 2001 From: Eric Wheeler Date: Thu, 17 Jul 2025 14:58:09 -0700 Subject: [PATCH] perf: optimize environment details by omitting identical records Added prevEnvDetails property to Task class to store previous environment state. Modified getEnvironmentDetails to recursively compare current and previous environment details, returning only the differences. This optimization reduces the size of environment details in responses by omitting any records that are identical to the previous state. Fixes: #5844 Signed-off-by: Eric Wheeler --- src/core/environment/context/metadata.ts | 2 +- src/core/environment/getEnvironmentDetails.ts | 57 ++++++++++++++++++- src/core/task/Task.ts | 1 + 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/src/core/environment/context/metadata.ts b/src/core/environment/context/metadata.ts index 86dde9772c..3d412ba48e 100644 --- a/src/core/environment/context/metadata.ts +++ b/src/core/environment/context/metadata.ts @@ -30,7 +30,7 @@ export async function getMetadataContext(cline: Task) { .padStart(2, "0")}:${offsetMinutes.toString().padStart(2, "0")}` const isoDateWithOffset = now.toISOString().replace(/Z$/, offsetString) const time = { - "@v": isoDateWithOffset, + "@I": isoDateWithOffset, } const { totalCost } = getApiMetrics(cline.clineMessages) diff --git a/src/core/environment/getEnvironmentDetails.ts b/src/core/environment/getEnvironmentDetails.ts index a78dadef7f..6b12448ab1 100644 --- a/src/core/environment/getEnvironmentDetails.ts +++ b/src/core/environment/getEnvironmentDetails.ts @@ -19,7 +19,7 @@ export async function getEnvironmentDetails(task: Task, includeFileDetails: bool getTodoContext(task), ]) - const envDetails = { + const currentEnvDetails = { ...vscodeContext, ...terminalContext, ...fileContext, @@ -28,6 +28,10 @@ export async function getEnvironmentDetails(task: Task, includeFileDetails: bool ...todoContext, } + const diffEnvDetails = _envDiff(currentEnvDetails, task.prevEnvDetails) + + task.prevEnvDetails = currentEnvDetails + const builder = new XMLBuilder({ format: true, // Enable pretty printing indentBy: " ", // Use two spaces for indentation @@ -38,5 +42,54 @@ export async function getEnvironmentDetails(task: Task, includeFileDetails: bool textNodeName: "#text", }) - return builder.build({ environment_details: envDetails }) + return builder.build({ environment_details: diffEnvDetails }) +} + +function _envDiff(current: any, previous: any): any { + if (!previous) return current + + return Object.keys(current).reduce((acc, key) => { + const currentValue = current[key] + const previousValue = previous ? previous[key] : undefined + + if (_objIsEqual(currentValue, previousValue)) { + return acc + } + + // Check for nested objects (but not arrays) + if ( + typeof currentValue === "object" && + currentValue !== null && + !Array.isArray(currentValue) && + typeof previousValue === "object" && + previousValue !== null && + !Array.isArray(previousValue) + ) { + const nestedDiff = _envDiff(currentValue, previousValue) + if (Object.keys(nestedDiff).length > 0) { + acc[key] = nestedDiff + } + } else { + // For primitives, arrays, or if previous was not an object + acc[key] = currentValue + } + + return acc + }, {} as any) +} + +function _objIsEqual(a: any, b: any): boolean { + if (a === b) return true + if (a === null || b === null || typeof a !== "object" || typeof b !== "object") return false + + const keysA = Object.keys(a) + const keysB = Object.keys(b) + + if (keysA.length !== keysB.length) return false + + for (const key of keysA) { + if (!keysB.includes(key) || !_objIsEqual(a[key], b[key])) return false + } + + return true } diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 8a1bf1101d..1c81987e92 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -180,6 +180,7 @@ export class Task extends EventEmitter { // LLM Messages & Chat Messages apiConversationHistory: ApiMessage[] = [] clineMessages: ClineMessage[] = [] + prevEnvDetails?: Record // Ask private askResponse?: ClineAskResponse