From b9fd3361d12144ec35ecf38b5db648f43575aa03 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Thu, 2 Oct 2025 00:26:35 +0000 Subject: [PATCH] feat: add settings to configure time and cost in system prompt - Added includeCurrentTime and includeCurrentCost settings to global-settings schema - Updated getEnvironmentDetails to conditionally include time/cost sections - Added UI controls in ContextManagementSettings component - Added translation keys for new settings - Updated all necessary type definitions and message handlers - Both settings default to true for backward compatibility Fixes #8450 --- packages/types/src/global-settings.ts | 11 ++++++ src/core/environment/getEnvironmentDetails.ts | 31 ++++++++++------- src/core/webview/webviewMessageHandler.ts | 8 +++++ src/shared/ExtensionMessage.ts | 4 +++ src/shared/WebviewMessage.ts | 2 ++ .../settings/ContextManagementSettings.tsx | 34 +++++++++++++++++++ .../src/components/settings/SettingsView.tsx | 6 ++++ .../src/context/ExtensionStateContext.tsx | 20 +++++++++++ webview-ui/src/i18n/locales/en/settings.json | 8 +++++ 9 files changed, 112 insertions(+), 12 deletions(-) diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index 334766c3f0..36fae6f3d9 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -93,6 +93,17 @@ export const globalSettingsSchema = z.object({ autoCondenseContextPercent: z.number().optional(), maxConcurrentFileReads: z.number().optional(), + /** + * Whether to include current time in the environment details + * @default true + */ + includeCurrentTime: z.boolean().optional(), + /** + * Whether to include current cost in the environment details + * @default true + */ + includeCurrentCost: z.boolean().optional(), + /** * Whether to include diagnostic messages (errors, warnings) in tool outputs * @default true diff --git a/src/core/environment/getEnvironmentDetails.ts b/src/core/environment/getEnvironmentDetails.ts index c0139649ab..aceae059a9 100644 --- a/src/core/environment/getEnvironmentDetails.ts +++ b/src/core/environment/getEnvironmentDetails.ts @@ -190,22 +190,29 @@ export async function getEnvironmentDetails(cline: Task, includeFileDetails: boo details += terminalDetails } - // Add current time information with timezone. - const now = new Date() + // Get settings for time and cost display + const { includeCurrentTime = true, includeCurrentCost = true } = state ?? {} - const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone - const timeZoneOffset = -now.getTimezoneOffset() / 60 // Convert to hours and invert sign to match conventional notation - const timeZoneOffsetHours = Math.floor(Math.abs(timeZoneOffset)) - const timeZoneOffsetMinutes = Math.abs(Math.round((Math.abs(timeZoneOffset) - timeZoneOffsetHours) * 60)) - const timeZoneOffsetStr = `${timeZoneOffset >= 0 ? "+" : "-"}${timeZoneOffsetHours}:${timeZoneOffsetMinutes.toString().padStart(2, "0")}` - details += `\n\n# Current Time\nCurrent time in ISO 8601 UTC format: ${now.toISOString()}\nUser time zone: ${timeZone}, UTC${timeZoneOffsetStr}` + // Add current time information with timezone (if enabled). + if (includeCurrentTime) { + const now = new Date() + + const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone + const timeZoneOffset = -now.getTimezoneOffset() / 60 // Convert to hours and invert sign to match conventional notation + const timeZoneOffsetHours = Math.floor(Math.abs(timeZoneOffset)) + const timeZoneOffsetMinutes = Math.abs(Math.round((Math.abs(timeZoneOffset) - timeZoneOffsetHours) * 60)) + const timeZoneOffsetStr = `${timeZoneOffset >= 0 ? "+" : "-"}${timeZoneOffsetHours}:${timeZoneOffsetMinutes.toString().padStart(2, "0")}` + details += `\n\n# Current Time\nCurrent time in ISO 8601 UTC format: ${now.toISOString()}\nUser time zone: ${timeZone}, UTC${timeZoneOffsetStr}` + } + + // Add context tokens information (if enabled). + if (includeCurrentCost) { + const { contextTokens, totalCost } = getApiMetrics(cline.clineMessages) + details += `\n\n# Current Cost\n${totalCost !== null ? `$${totalCost.toFixed(2)}` : "(Not available)"}` + } - // Add context tokens information. - const { contextTokens, totalCost } = getApiMetrics(cline.clineMessages) const { id: modelId } = cline.api.getModel() - details += `\n\n# Current Cost\n${totalCost !== null ? `$${totalCost.toFixed(2)}` : "(Not available)"}` - // Add current mode and any mode-specific warnings. const { mode, diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 38b51c7123..e32b818a96 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -1658,6 +1658,14 @@ export const webviewMessageHandler = async ( await updateGlobalState("includeDiagnosticMessages", includeValue) await provider.postStateToWebview() break + case "includeCurrentTime": + await updateGlobalState("includeCurrentTime", message.bool ?? true) + await provider.postStateToWebview() + break + case "includeCurrentCost": + await updateGlobalState("includeCurrentCost", message.bool ?? true) + await provider.postStateToWebview() + break case "maxDiagnosticMessages": await updateGlobalState("maxDiagnosticMessages", message.value ?? 50) await provider.postStateToWebview() diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 42adea6d39..43097d4dc2 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -294,6 +294,8 @@ export type ExtensionState = Pick< | "openRouterImageGenerationSelectedModel" | "includeTaskHistoryInEnhance" | "reasoningBlockCollapsed" + | "includeCurrentTime" + | "includeCurrentCost" > & { version: string clineMessages: ClineMessage[] @@ -352,6 +354,8 @@ export type ExtensionState = Pick< openRouterImageApiKey?: string openRouterUseMiddleOutTransform?: boolean messageQueue?: QueuedMessage[] + includeCurrentTime?: boolean + includeCurrentCost?: boolean lastShownAnnouncementId?: string apiModelId?: string mcpServers?: McpServer[] diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index e460f20384..9c47518628 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -177,6 +177,8 @@ export interface WebviewMessage { | "maxConcurrentFileReads" | "includeDiagnosticMessages" | "maxDiagnosticMessages" + | "includeCurrentTime" + | "includeCurrentCost" | "searchFiles" | "toggleApiConfigPin" | "setHistoryPreviewCollapsed" diff --git a/webview-ui/src/components/settings/ContextManagementSettings.tsx b/webview-ui/src/components/settings/ContextManagementSettings.tsx index 88484e1d63..217b205dd4 100644 --- a/webview-ui/src/components/settings/ContextManagementSettings.tsx +++ b/webview-ui/src/components/settings/ContextManagementSettings.tsx @@ -27,6 +27,8 @@ type ContextManagementSettingsProps = HTMLAttributes & { includeDiagnosticMessages?: boolean maxDiagnosticMessages?: number writeDelayMs: number + includeCurrentTime?: boolean + includeCurrentCost?: boolean setCachedStateField: SetCachedStateField< | "autoCondenseContext" | "autoCondenseContextPercent" @@ -41,6 +43,8 @@ type ContextManagementSettingsProps = HTMLAttributes & { | "includeDiagnosticMessages" | "maxDiagnosticMessages" | "writeDelayMs" + | "includeCurrentTime" + | "includeCurrentCost" > } @@ -60,6 +64,8 @@ export const ContextManagementSettings = ({ includeDiagnosticMessages, maxDiagnosticMessages, writeDelayMs, + includeCurrentTime, + includeCurrentCost, className, ...props }: ContextManagementSettingsProps) => { @@ -356,6 +362,34 @@ export const ContextManagementSettings = ({ {t("settings:contextManagement.diagnostics.delayAfterWrite.description")} + +
+ setCachedStateField("includeCurrentTime", e.target.checked)} + data-testid="include-current-time-checkbox"> + + +
+ {t("settings:contextManagement.includeCurrentTime.description")} +
+
+ +
+ setCachedStateField("includeCurrentCost", e.target.checked)} + data-testid="include-current-cost-checkbox"> + + +
+ {t("settings:contextManagement.includeCurrentCost.description")} +
+
(({ onDone, t openRouterImageApiKey, openRouterImageGenerationSelectedModel, reasoningBlockCollapsed, + includeCurrentTime, + includeCurrentCost, } = cachedState const apiConfiguration = useMemo(() => cachedState.apiConfiguration ?? {}, [cachedState.apiConfiguration]) @@ -386,6 +388,8 @@ const SettingsView = forwardRef(({ onDone, t vscode.postMessage({ type: "updateSupportPrompt", values: customSupportPrompts || {} }) vscode.postMessage({ type: "includeTaskHistoryInEnhance", bool: includeTaskHistoryInEnhance ?? true }) vscode.postMessage({ type: "setReasoningBlockCollapsed", bool: reasoningBlockCollapsed ?? true }) + vscode.postMessage({ type: "includeCurrentTime", bool: includeCurrentTime ?? true }) + vscode.postMessage({ type: "includeCurrentCost", bool: includeCurrentCost ?? true }) vscode.postMessage({ type: "upsertApiConfiguration", text: currentApiConfigName, apiConfiguration }) vscode.postMessage({ type: "telemetrySetting", text: telemetrySetting }) vscode.postMessage({ type: "profileThresholds", values: profileThresholds }) @@ -747,6 +751,8 @@ const SettingsView = forwardRef(({ onDone, t includeDiagnosticMessages={includeDiagnosticMessages} maxDiagnosticMessages={maxDiagnosticMessages} writeDelayMs={writeDelayMs} + includeCurrentTime={includeCurrentTime} + includeCurrentCost={includeCurrentCost} setCachedStateField={setCachedStateField} /> )} diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index 05868c31a5..7c68795040 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -161,6 +161,10 @@ export interface ExtensionStateContextType extends ExtensionState { setMaxDiagnosticMessages: (value: number) => void includeTaskHistoryInEnhance?: boolean setIncludeTaskHistoryInEnhance: (value: boolean) => void + includeCurrentTime?: boolean + setIncludeCurrentTime: (value: boolean) => void + includeCurrentCost?: boolean + setIncludeCurrentCost: (value: boolean) => void } export const ExtensionStateContext = createContext(undefined) @@ -270,6 +274,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode maxDiagnosticMessages: 50, openRouterImageApiKey: "", openRouterImageGenerationSelectedModel: "", + includeCurrentTime: true, + includeCurrentCost: true, }) const [didHydrateState, setDidHydrateState] = useState(false) @@ -290,6 +296,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode }) const [includeTaskHistoryInEnhance, setIncludeTaskHistoryInEnhance] = useState(true) const [prevCloudIsAuthenticated, setPrevCloudIsAuthenticated] = useState(false) + const [includeCurrentTime, setIncludeCurrentTime] = useState(true) + const [includeCurrentCost, setIncludeCurrentCost] = useState(true) const setListApiConfigMeta = useCallback( (value: ProviderSettingsEntry[]) => setState((prevState) => ({ ...prevState, listApiConfigMeta: value })), @@ -327,6 +335,14 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode if ((newState as any).includeTaskHistoryInEnhance !== undefined) { setIncludeTaskHistoryInEnhance((newState as any).includeTaskHistoryInEnhance) } + // Update includeCurrentTime if present in state message + if ((newState as any).includeCurrentTime !== undefined) { + setIncludeCurrentTime((newState as any).includeCurrentTime) + } + // Update includeCurrentCost if present in state message + if ((newState as any).includeCurrentCost !== undefined) { + setIncludeCurrentCost((newState as any).includeCurrentCost) + } // Handle marketplace data if present in state message if (newState.marketplaceItems !== undefined) { setMarketplaceItems(newState.marketplaceItems) @@ -575,6 +591,10 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode }, includeTaskHistoryInEnhance, setIncludeTaskHistoryInEnhance, + includeCurrentTime, + setIncludeCurrentTime, + includeCurrentCost, + setIncludeCurrentCost, } return {children} diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index f030c9125e..7c84a57607 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -614,6 +614,14 @@ "profileDescription": "Custom threshold for this profile only (overrides global default)", "inheritDescription": "This profile inherits the global default threshold ({{threshold}}%)", "usesGlobal": "(uses global {{threshold}}%)" + }, + "includeCurrentTime": { + "label": "Include current time in context", + "description": "When enabled, the current time and timezone information will be included in the system prompt. Disable this if models are stopping work due to time concerns." + }, + "includeCurrentCost": { + "label": "Include current cost in context", + "description": "When enabled, the current API usage cost will be included in the system prompt. Disable this if models are stopping work due to cost concerns." } }, "terminal": {