Skip to content

Commit 30deca3

Browse files
committed
feat: add privacy controls for time and timezone in context (#8731)
Signed-off-by: Vsevolod Volkov <[email protected]>
1 parent a8f87d2 commit 30deca3

31 files changed

+292
-9
lines changed

packages/types/src/global-settings.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,17 @@ export const globalSettingsSchema = z.object({
8989
*/
9090
maxDiagnosticMessages: z.number().optional(),
9191

92+
/**
93+
* Whether to include current time in environment details sent with each request
94+
* @default true
95+
*/
96+
includeCurrentTime: z.boolean().optional(),
97+
/**
98+
* Whether to include timezone information when current time is included
99+
* @default false
100+
*/
101+
includeTimezone: z.boolean().optional(),
102+
92103
browserToolEnabled: z.boolean().optional(),
93104
browserViewportSize: z.string().optional(),
94105
screenshotQuality: z.number().optional(),
@@ -311,6 +322,9 @@ export const EVALS_SETTINGS: RooCodeSettings = {
311322
includeDiagnosticMessages: true,
312323
maxDiagnosticMessages: 50,
313324

325+
includeCurrentTime: true,
326+
includeTimezone: false,
327+
314328
language: "en",
315329
telemetrySetting: "enabled",
316330

src/core/environment/getEnvironmentDetails.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export async function getEnvironmentDetails(cline: Task, includeFileDetails: boo
3030
terminalOutputLineLimit = 500,
3131
terminalOutputCharacterLimit = DEFAULT_TERMINAL_OUTPUT_CHARACTER_LIMIT,
3232
maxWorkspaceFiles = 200,
33+
includeCurrentTime = true,
34+
includeTimezone = false,
3335
} = state ?? {}
3436

3537
// It could be useful for cline to know if the user went from one or no
@@ -190,15 +192,21 @@ export async function getEnvironmentDetails(cline: Task, includeFileDetails: boo
190192
details += terminalDetails
191193
}
192194

193-
// Add current time information with timezone.
194-
const now = new Date()
195-
196-
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
197-
const timeZoneOffset = -now.getTimezoneOffset() / 60 // Convert to hours and invert sign to match conventional notation
198-
const timeZoneOffsetHours = Math.floor(Math.abs(timeZoneOffset))
199-
const timeZoneOffsetMinutes = Math.abs(Math.round((Math.abs(timeZoneOffset) - timeZoneOffsetHours) * 60))
200-
const timeZoneOffsetStr = `${timeZoneOffset >= 0 ? "+" : "-"}${timeZoneOffsetHours}:${timeZoneOffsetMinutes.toString().padStart(2, "0")}`
201-
details += `\n\n# Current Time\nCurrent time in ISO 8601 UTC format: ${now.toISOString()}\nUser time zone: ${timeZone}, UTC${timeZoneOffsetStr}`
195+
// Add current time information (only if enabled).
196+
if (includeCurrentTime) {
197+
const now = new Date()
198+
details += `\n\n# Current Time\nCurrent time in ISO 8601 UTC format: ${now.toISOString()}`
199+
200+
// Add timezone information only if both includeCurrentTime and includeTimezone are enabled
201+
if (includeTimezone) {
202+
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
203+
const timeZoneOffset = -now.getTimezoneOffset() / 60 // Convert to hours and invert sign to match conventional notation
204+
const timeZoneOffsetHours = Math.floor(Math.abs(timeZoneOffset))
205+
const timeZoneOffsetMinutes = Math.abs(Math.round((Math.abs(timeZoneOffset) - timeZoneOffsetHours) * 60))
206+
const timeZoneOffsetStr = `${timeZoneOffset >= 0 ? "+" : "-"}${timeZoneOffsetHours}:${timeZoneOffsetMinutes.toString().padStart(2, "0")}`
207+
details += `\nUser time zone: ${timeZone}, UTC${timeZoneOffsetStr}`
208+
}
209+
}
202210

203211
// Add context tokens information.
204212
const { contextTokens, totalCost } = getApiMetrics(cline.clineMessages)

src/core/webview/ClineProvider.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1808,6 +1808,8 @@ export class ClineProvider
18081808
followupAutoApproveTimeoutMs,
18091809
includeDiagnosticMessages,
18101810
maxDiagnosticMessages,
1811+
includeCurrentTime,
1812+
includeTimezone,
18111813
includeTaskHistoryInEnhance,
18121814
taskSyncEnabled,
18131815
remoteControlEnabled,
@@ -1957,6 +1959,8 @@ export class ClineProvider
19571959
followupAutoApproveTimeoutMs: followupAutoApproveTimeoutMs ?? 60000,
19581960
includeDiagnosticMessages: includeDiagnosticMessages ?? true,
19591961
maxDiagnosticMessages: maxDiagnosticMessages ?? 50,
1962+
includeCurrentTime: includeCurrentTime ?? true,
1963+
includeTimezone: includeTimezone ?? false,
19601964
includeTaskHistoryInEnhance: includeTaskHistoryInEnhance ?? true,
19611965
taskSyncEnabled,
19621966
remoteControlEnabled,
@@ -2168,6 +2172,8 @@ export class ClineProvider
21682172
profileThresholds: stateValues.profileThresholds ?? {},
21692173
includeDiagnosticMessages: stateValues.includeDiagnosticMessages ?? true,
21702174
maxDiagnosticMessages: stateValues.maxDiagnosticMessages ?? 50,
2175+
includeCurrentTime: stateValues.includeCurrentTime ?? true,
2176+
includeTimezone: stateValues.includeTimezone ?? false,
21712177
includeTaskHistoryInEnhance: stateValues.includeTaskHistoryInEnhance ?? true,
21722178
taskSyncEnabled,
21732179
remoteControlEnabled: (() => {

src/core/webview/__tests__/ClineProvider.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,8 @@ describe("ClineProvider", () => {
543543
maxReadFileLine: 500,
544544
maxImageFileSize: 5,
545545
maxTotalImageSize: 20,
546+
includeCurrentTime: true,
547+
includeTimezone: false,
546548
cloudUserInfo: null,
547549
organizationAllowList: ORGANIZATION_ALLOW_ALL,
548550
autoCondenseContext: true,

src/core/webview/webviewMessageHandler.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1613,6 +1613,14 @@ export const webviewMessageHandler = async (
16131613
await updateGlobalState("maxDiagnosticMessages", message.value ?? 50)
16141614
await provider.postStateToWebview()
16151615
break
1616+
case "includeCurrentTime":
1617+
await updateGlobalState("includeCurrentTime", message.bool ?? true)
1618+
await provider.postStateToWebview()
1619+
break
1620+
case "includeTimezone":
1621+
await updateGlobalState("includeTimezone", message.bool ?? false)
1622+
await provider.postStateToWebview()
1623+
break
16161624
case "setHistoryPreviewCollapsed": // Add the new case handler
16171625
await updateGlobalState("historyPreviewCollapsed", message.bool ?? false)
16181626
// No need to call postStateToWebview here as the UI already updated optimistically

src/shared/ExtensionMessage.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,8 @@ export type ExtensionState = Pick<
285285
| "profileThresholds"
286286
| "includeDiagnosticMessages"
287287
| "maxDiagnosticMessages"
288+
| "includeCurrentTime"
289+
| "includeTimezone"
288290
| "openRouterImageGenerationSelectedModel"
289291
| "includeTaskHistoryInEnhance"
290292
| "reasoningBlockCollapsed"

src/shared/WebviewMessage.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,8 @@ export interface WebviewMessage {
175175
| "maxConcurrentFileReads"
176176
| "includeDiagnosticMessages"
177177
| "maxDiagnosticMessages"
178+
| "includeCurrentTime"
179+
| "includeTimezone"
178180
| "searchFiles"
179181
| "toggleApiConfigPin"
180182
| "setHistoryPreviewCollapsed"

webview-ui/src/components/settings/ContextManagementSettings.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ type ContextManagementSettingsProps = HTMLAttributes<HTMLDivElement> & {
2727
includeDiagnosticMessages?: boolean
2828
maxDiagnosticMessages?: number
2929
writeDelayMs: number
30+
includeCurrentTime?: boolean
31+
includeTimezone?: boolean
3032
setCachedStateField: SetCachedStateField<
3133
| "autoCondenseContext"
3234
| "autoCondenseContextPercent"
@@ -41,6 +43,8 @@ type ContextManagementSettingsProps = HTMLAttributes<HTMLDivElement> & {
4143
| "includeDiagnosticMessages"
4244
| "maxDiagnosticMessages"
4345
| "writeDelayMs"
46+
| "includeCurrentTime"
47+
| "includeTimezone"
4448
>
4549
}
4650

@@ -60,6 +64,8 @@ export const ContextManagementSettings = ({
6064
includeDiagnosticMessages,
6165
maxDiagnosticMessages,
6266
writeDelayMs,
67+
includeCurrentTime = true,
68+
includeTimezone = false,
6369
className,
6470
...props
6571
}: ContextManagementSettingsProps) => {
@@ -356,6 +362,35 @@ export const ContextManagementSettings = ({
356362
{t("settings:contextManagement.diagnostics.delayAfterWrite.description")}
357363
</div>
358364
</div>
365+
366+
<div>
367+
<VSCodeCheckbox
368+
checked={includeCurrentTime}
369+
onChange={(e: any) => setCachedStateField("includeCurrentTime", e.target.checked)}
370+
data-testid="include-current-time-checkbox">
371+
<label className="block font-medium mb-1">
372+
{t("settings:contextManagement.timeInfo.includeCurrentTime.label")}
373+
</label>
374+
</VSCodeCheckbox>
375+
<div className="text-vscode-descriptionForeground text-sm mt-1 mb-3">
376+
{t("settings:contextManagement.timeInfo.includeCurrentTime.description")}
377+
</div>
378+
</div>
379+
380+
<div>
381+
<VSCodeCheckbox
382+
checked={includeTimezone}
383+
disabled={!includeCurrentTime}
384+
onChange={(e: any) => setCachedStateField("includeTimezone", e.target.checked)}
385+
data-testid="include-timezone-checkbox">
386+
<label className="block font-medium mb-1">
387+
{t("settings:contextManagement.timeInfo.includeTimezone.label")}
388+
</label>
389+
</VSCodeCheckbox>
390+
<div className="text-vscode-descriptionForeground text-sm mt-1 mb-3">
391+
{t("settings:contextManagement.timeInfo.includeTimezone.description")}
392+
</div>
393+
</div>
359394
</Section>
360395
<Section className="pt-2">
361396
<VSCodeCheckbox

webview-ui/src/components/settings/SettingsView.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
191191
followupAutoApproveTimeoutMs,
192192
includeDiagnosticMessages,
193193
maxDiagnosticMessages,
194+
includeCurrentTime,
195+
includeTimezone,
194196
includeTaskHistoryInEnhance,
195197
openRouterImageApiKey,
196198
openRouterImageGenerationSelectedModel,
@@ -372,6 +374,8 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
372374
vscode.postMessage({ type: "maxConcurrentFileReads", value: cachedState.maxConcurrentFileReads ?? 5 })
373375
vscode.postMessage({ type: "includeDiagnosticMessages", bool: includeDiagnosticMessages })
374376
vscode.postMessage({ type: "maxDiagnosticMessages", value: maxDiagnosticMessages ?? 50 })
377+
vscode.postMessage({ type: "includeCurrentTime", bool: includeCurrentTime ?? true })
378+
vscode.postMessage({ type: "includeTimezone", bool: includeTimezone ?? false })
375379
vscode.postMessage({ type: "currentApiConfigName", text: currentApiConfigName })
376380
vscode.postMessage({ type: "updateExperimental", values: experiments })
377381
vscode.postMessage({ type: "alwaysAllowModeSwitch", bool: alwaysAllowModeSwitch })
@@ -744,6 +748,8 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
744748
includeDiagnosticMessages={includeDiagnosticMessages}
745749
maxDiagnosticMessages={maxDiagnosticMessages}
746750
writeDelayMs={writeDelayMs}
751+
includeCurrentTime={includeCurrentTime}
752+
includeTimezone={includeTimezone}
747753
setCachedStateField={setCachedStateField}
748754
/>
749755
)}

webview-ui/src/components/settings/__tests__/SettingsView.change-detection.spec.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ describe("SettingsView - Change Detection Fix", () => {
183183
followupAutoApproveTimeoutMs: undefined,
184184
includeDiagnosticMessages: false,
185185
maxDiagnosticMessages: 50,
186+
includeCurrentTime: true,
187+
includeTimezone: false,
186188
includeTaskHistoryInEnhance: true,
187189
openRouterImageApiKey: undefined,
188190
openRouterImageGenerationSelectedModel: undefined,

0 commit comments

Comments
 (0)