Skip to content

Commit 203c94c

Browse files
beyondkmpPayne Fuclaude
authored
feat(agent): add custom environment variables to agent configuration (#13357)
### What this PR does Fix #13343 Before this PR: There was no way to configure custom environment variables for agents. Users could not pass additional env vars to the Claude Code SDK process at runtime. After this PR: Users can set custom environment variables (KEY=value pairs) for agents via a TextArea in both the AgentModal (create/edit popup) and AdvancedSettings (agent settings page). These env vars are merged into the Claude Code SDK process environment at runtime, with a security blocklist preventing overrides of critical system variables. ### Why we need it and why it was done in this way Users need the ability to customize the agent runtime environment (e.g., setting `DEBUG=true`, custom API keys for MCP tools, or tool-specific config). The implementation follows existing patterns: - The `env_vars` field is added to `AgentConfigurationSchema` as an optional `Record<string, string>` — no database migration needed since configuration uses `.loose()` and is stored as JSON text. - The AgentModal uses inline parse/serialize helpers for the KEY=value text format. - The AdvancedSettings follows the existing commit-on-blur pattern (same as `maxTurns`). - A blocklist in ClaudeCodeService prevents users from overriding critical variables (API keys, Electron flags, etc.). The following alternatives were considered: - A key-value pair editor UI — rejected in favor of a simpler TextArea matching the existing code tools page pattern. ### Breaking changes None. ### Special notes for your reviewer - The `env_vars` field uses `.optional().default({})` in Zod, consistent with `permission_mode` and `max_turns`. - Security: A blocklist of 12 critical env var keys (ANTHROPIC_API_KEY, ELECTRON_RUN_AS_NODE, etc.) is enforced server-side with a warning log when blocked. - User env vars are applied **after** system env vars so they can override shell env but NOT critical Anthropic/Electron vars. ### Checklist - [x] PR: The PR description is expressive enough and will help future contributors - [x] Code: Write code that humans can understand and Keep it simple - [x] Refactor: You have left the code cleaner than you found it (Boy Scout Rule) - [ ] Upgrade: Impact of this change on upgrade flows was considered and addressed if required - [x] Documentation: A user-guide update was considered and is present (link) or not required. - [ ] Self-review: I have reviewed my own code before requesting review from others ### Release note ```release-note Add custom environment variables support to agent configuration. Users can now set KEY=value pairs in the agent create/edit modal and advanced settings, which are passed to the Claude Code SDK runtime environment. ``` --------- Signed-off-by: Payne Fu <payne@Paynes-MacBook-Air.local> Co-authored-by: Payne Fu <payne@Paynes-MacBook-Air.local> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent bec5ea4 commit 203c94c

File tree

17 files changed

+282
-4
lines changed

17 files changed

+282
-4
lines changed

src/main/services/agents/services/claudecode/index.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,37 @@ class ClaudeCodeService implements AgentServiceInterface {
164164
...(customGitBashPath ? { CLAUDE_CODE_GIT_BASH_PATH: customGitBashPath } : {})
165165
}
166166

167+
// Merge user-defined environment variables from session configuration
168+
const userEnvVars = session.configuration?.env_vars
169+
if (userEnvVars && typeof userEnvVars === 'object') {
170+
const BLOCKED_ENV_KEYS = new Set([
171+
'ANTHROPIC_API_KEY',
172+
'ANTHROPIC_AUTH_TOKEN',
173+
'ANTHROPIC_BASE_URL',
174+
'ANTHROPIC_MODEL',
175+
'ANTHROPIC_DEFAULT_OPUS_MODEL',
176+
'ANTHROPIC_DEFAULT_SONNET_MODEL',
177+
'ANTHROPIC_DEFAULT_HAIKU_MODEL',
178+
'ELECTRON_RUN_AS_NODE',
179+
'ELECTRON_NO_ATTACH_CONSOLE',
180+
'CLAUDE_CONFIG_DIR',
181+
'CLAUDE_CODE_USE_BEDROCK',
182+
'CLAUDE_CODE_GIT_BASH_PATH',
183+
'NODE_OPTIONS',
184+
'__PROTO__',
185+
'CONSTRUCTOR',
186+
'PROTOTYPE'
187+
])
188+
for (const [key, value] of Object.entries(userEnvVars)) {
189+
const upperKey = key.toUpperCase()
190+
if (BLOCKED_ENV_KEYS.has(upperKey)) {
191+
logger.warn('Blocked user env var override for system-critical variable', { key })
192+
} else if (typeof value === 'string') {
193+
env[key] = value
194+
}
195+
}
196+
}
197+
167198
const errorChunks: string[] = []
168199

169200
const sessionAllowedTools = new Set<string>(session.allowed_tools ?? [])

src/renderer/src/components/Popups/agent/AgentModal.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import type {
1717
UpdateAgentForm
1818
} from '@renderer/types'
1919
import { AgentConfigurationSchema, isAgentType } from '@renderer/types'
20+
import { parseKeyValueString, serializeKeyValueString } from '@renderer/utils/env'
2021
import type { GitBashPathInfo } from '@shared/config/constant'
2122
import { Button, Input, Modal, Select } from 'antd'
2223
import type { ChangeEvent, FormEvent } from 'react'
@@ -166,6 +167,27 @@ const PopupContainer: React.FC<Props> = ({ agent, afterSubmit, resolve }) => {
166167
}))
167168
}, [])
168169

170+
const [envVarsText, setEnvVarsText] = useState(() => serializeKeyValueString(form.configuration?.env_vars ?? {}))
171+
172+
useEffect(() => {
173+
if (open) {
174+
setEnvVarsText(serializeKeyValueString(buildAgentForm(agent).configuration?.env_vars ?? {}))
175+
}
176+
}, [agent, open])
177+
178+
const onEnvVarsChange = useCallback((e: ChangeEvent<HTMLTextAreaElement>) => {
179+
const text = e.target.value
180+
setEnvVarsText(text)
181+
const parsed = parseKeyValueString(text)
182+
setForm((prev) => ({
183+
...prev,
184+
configuration: {
185+
...AgentConfigurationSchema.parse(prev.configuration ?? {}),
186+
env_vars: parsed
187+
}
188+
}))
189+
}, [])
190+
169191
const addAccessiblePath = useCallback(async () => {
170192
try {
171193
const selected = await window.api.file.selectFolder()
@@ -455,6 +477,17 @@ const PopupContainer: React.FC<Props> = ({ agent, afterSubmit, resolve }) => {
455477
<TextArea rows={3} value={form.instructions ?? ''} onChange={onInstChange} />
456478
</FormItem>
457479

480+
<FormItem>
481+
<Label>{t('agent.settings.advance.envVars.label')}</Label>
482+
<TextArea
483+
rows={3}
484+
value={envVarsText}
485+
onChange={onEnvVarsChange}
486+
placeholder={'API_KEY=xxx\nDEBUG=true'}
487+
/>
488+
<HelpText>{t('agent.settings.advance.envVars.helper')}</HelpText>
489+
</FormItem>
490+
458491
{/* <FormItem>
459492
<Label>{t('common.description')}</Label>
460493
<TextArea rows={1} value={form.description ?? ''} onChange={onDescChange} />

src/renderer/src/i18n/locales/en-us.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@
135135
},
136136
"settings": {
137137
"advance": {
138+
"envVars": {
139+
"description": "Set custom environment variables for the agent runtime.",
140+
"helper": "Enter custom environment variables (one per line, format: KEY=value)",
141+
"label": "Environment Variables"
142+
},
138143
"maxTurns": {
139144
"description": "Define how many request/response cycles the agent may complete automatically.",
140145
"helper": "Higher values enable longer autonomous runs; lower values keep sessions short.",

src/renderer/src/i18n/locales/zh-cn.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@
135135
},
136136
"settings": {
137137
"advance": {
138+
"envVars": {
139+
"description": "为智能体运行时设置自定义环境变量。",
140+
"helper": "输入自定义环境变量(每行一个,格式:KEY=value)",
141+
"label": "环境变量"
142+
},
138143
"maxTurns": {
139144
"description": "设定代理自动执行的请求/回复轮次数。",
140145
"helper": "数值越高可自主运行越久;数值越低更易控制。",

src/renderer/src/i18n/locales/zh-tw.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@
135135
},
136136
"settings": {
137137
"advance": {
138+
"envVars": {
139+
"description": "為智慧體執行環境設置自訂環境變數。",
140+
"helper": "輸入自訂環境變數(每行一個,格式:KEY=value)",
141+
"label": "環境變數"
142+
},
138143
"maxTurns": {
139144
"description": "設定代理自動執行的請求/回覆輪次數。",
140145
"helper": "數值越高可自動運作越久;數值越低更容易掌控。",

src/renderer/src/i18n/translate/de-de.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@
135135
},
136136
"settings": {
137137
"advance": {
138+
"envVars": {
139+
"description": "[to be translated]:Set custom environment variables for the agent runtime.",
140+
"helper": "[to be translated]:Enter custom environment variables (one per line, format: KEY=value)",
141+
"label": "[to be translated]:Environment Variables"
142+
},
138143
"maxTurns": {
139144
"description": "Legen Sie die Anzahl der Anfrage-/Antwort-Runden fest, die der Agent automatisch ausführt.",
140145
"helper": "Höhere Werte ermöglichen längere autonome Ausführung; niedrigere Werte bieten bessere Kontrolle.",

src/renderer/src/i18n/translate/el-gr.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@
135135
},
136136
"settings": {
137137
"advance": {
138+
"envVars": {
139+
"description": "[to be translated]:Set custom environment variables for the agent runtime.",
140+
"helper": "[to be translated]:Enter custom environment variables (one per line, format: KEY=value)",
141+
"label": "[to be translated]:Environment Variables"
142+
},
138143
"maxTurns": {
139144
"description": "Ορίστε τον αριθμό γύρων αιτήματος/απάντησης που θα εκτελούνται αυτόματα από τον διαμεσολαβητή.",
140145
"helper": "Όσο υψηλότερη είναι η τιμή, τόσο περισσότερο μπορεί να λειτουργεί αυτόνομα· όσο χαμηλότερη είναι, τόσο πιο εύκολα ελέγχεται.",

src/renderer/src/i18n/translate/es-es.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@
135135
},
136136
"settings": {
137137
"advance": {
138+
"envVars": {
139+
"description": "[to be translated]:Set custom environment variables for the agent runtime.",
140+
"helper": "[to be translated]:Enter custom environment variables (one per line, format: KEY=value)",
141+
"label": "[to be translated]:Environment Variables"
142+
},
138143
"maxTurns": {
139144
"description": "Establece el número de rondas de solicitud/respuesta que el agente ejecutará automáticamente.",
140145
"helper": "Cuanto mayor es el valor, más tiempo puede funcionar de forma autónoma; cuanto menor es el valor, más fácil es de controlar.",

src/renderer/src/i18n/translate/fr-fr.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@
135135
},
136136
"settings": {
137137
"advance": {
138+
"envVars": {
139+
"description": "[to be translated]:Set custom environment variables for the agent runtime.",
140+
"helper": "[to be translated]:Enter custom environment variables (one per line, format: KEY=value)",
141+
"label": "[to be translated]:Environment Variables"
142+
},
138143
"maxTurns": {
139144
"description": "Définir le nombre de cycles de requête/réponse exécutés automatiquement par l'agent.",
140145
"helper": "Une valeur plus élevée permet une autonomie prolongée ; une valeur plus faible facilite le contrôle.",

src/renderer/src/i18n/translate/ja-jp.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@
135135
},
136136
"settings": {
137137
"advance": {
138+
"envVars": {
139+
"description": "[to be translated]:Set custom environment variables for the agent runtime.",
140+
"helper": "[to be translated]:Enter custom environment variables (one per line, format: KEY=value)",
141+
"label": "[to be translated]:Environment Variables"
142+
},
138143
"maxTurns": {
139144
"description": "プロキシが自動的に実行するリクエスト/レスポンスのラウンド数を設定します。",
140145
"helper": "数値が高いほど自律動作の時間が長くなり、数値が低いほど制御しやすくなります。",

0 commit comments

Comments
 (0)