Add environment variables to executor profiles (#1444)

* The implementation is complete. Here's a summary of what was done:

Successfully added environment variable configuration support to executor profiles. Users can now configure env vars like this:

```json
{
  "CLAUDE_CODE": {
    "DEFAULT": {
      "CLAUDE_CODE": {
        "env": {
          "ANTHROPIC_AUTH_TOKEN": "...",
          "ANTHROPIC_BASE_URL": "https://api.z.ai/api/anthropic",
          "API_TIMEOUT_MS": "3000000",
          "ANTHROPIC_DEFAULT_SONNET_MODEL": "glm-4.6"
        }
      }
    }
  }
}
```

1. **`crates/executors/src/command.rs`**:
   - Added `env: Option<HashMap<String, String>>` to `CmdOverrides` struct
   - Added `apply_env_vars()` helper function

2. **Updated all executors to apply env vars**:
   - `claude.rs` - `spawn_internal()`
   - `codex.rs` - `spawn()`
   - `amp.rs` - `spawn()` and `spawn_follow_up()`
   - `droid.rs` - module-level `spawn()` function
   - `copilot.rs` - both spawn methods
   - `cursor.rs` - both spawn methods
   - `opencode.rs` - both spawn methods
   - `acp/harness.rs` - `spawn_with_command()` and `spawn_follow_up_with_command()` (used by Gemini/Qwen)

3. **Type Generation**: All JSON schemas and TypeScript types now include the `env` field

This allows users to set environment variables for all supported coding agents (Claude Code, Codex, Gemini, Amp, Droid, Copilot, Cursor, OpenCode, Qwen) through profile configuration, eliminating the need for external tools like claude-code-router.

* The implementation is complete. Here's a summary of what was done:

## Summary

### Backend Changes
- Added `env: Option<HashMap<String, String>>` field to `CmdOverrides` struct in `crates/executors/src/command.rs`
- Created `apply_env_vars()` helper function to inject env vars into `tokio::process::Command`
- Updated all 9 executors to apply env vars during spawn:
  - claude.rs, codex.rs, amp.rs, droid.rs, copilot.rs, cursor.rs, opencode.rs
  - gemini.rs and qwen.rs (via ACP harness)
- Modified ACP harness signature to accept `Option<&CmdOverrides>`

### Frontend Changes
- Created `KeyValueField.tsx` - custom RJSF field for editing key-value pairs
- Registered the field in `theme.ts`
- Added `uiSchema` to `ExecutorConfigForm.tsx` to use the custom field for `env`

### Generated Files
- `shared/types.ts` - TypeScript types updated with `env` field
- `shared/schemas/*.json` - All 9 executor schemas include `env` property

The environment variables UI will now appear in Settings > Agent Settings as a key-value editor with "Environment Variables" label and description. Users can add/remove/edit env vars that will be passed to the CLI execution environment.

* cleanup env structs

* fix form

* fmt

* remove mise.toml

* fmt

* Seprate config form per selected variant

---------

Co-authored-by: Louis Knight-Webb <louis@bloop.ai>
Co-authored-by: Solomon <abcpro11051@disroot.org>
This commit is contained in:
Stephan Fitzpatrick
2025-12-07 07:01:12 -08:00
committed by GitHub
parent e1c9c15f43
commit 7da884bc3a
28 changed files with 399 additions and 44 deletions

View File

@@ -1,4 +1,4 @@
use std::path::PathBuf;
use std::{collections::HashMap, path::PathBuf};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -52,6 +52,12 @@ pub struct CmdOverrides {
)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub additional_params: Option<Vec<String>>,
#[schemars(
title = "Environment Variables",
description = "Environment variables to set when running the executor"
)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub env: Option<HashMap<String, String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, TS, JsonSchema)]

View File

@@ -2,6 +2,8 @@ use std::collections::HashMap;
use tokio::process::Command;
use crate::command::CmdOverrides;
/// Environment variables to inject into executor processes
#[derive(Debug, Clone, Default)]
pub struct ExecutionEnv {
@@ -20,6 +22,27 @@ impl ExecutionEnv {
self.vars.insert(key.into(), value.into());
}
/// Merge additional vars into this env. Incoming keys overwrite existing ones.
pub fn merge(&mut self, other: &HashMap<String, String>) {
self.vars
.extend(other.iter().map(|(k, v)| (k.clone(), v.clone())));
}
/// Return a new env with overrides applied. Overrides take precedence.
pub fn with_overrides(mut self, overrides: &HashMap<String, String>) -> Self {
self.merge(overrides);
self
}
/// Return a new env with profile env from CmdOverrides merged in.
pub fn with_profile(self, cmd: &CmdOverrides) -> Self {
if let Some(ref profile_env) = cmd.env {
self.with_overrides(profile_env)
} else {
self
}
}
/// Apply all environment variables to a Command
pub fn apply_to_command(&self, command: &mut Command) {
for (key, value) in &self.vars {
@@ -27,3 +50,25 @@ impl ExecutionEnv {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn profile_overrides_runtime_env() {
let mut base = ExecutionEnv::default();
base.insert("VK_PROJECT_NAME", "runtime");
base.insert("FOO", "runtime");
let mut profile = HashMap::new();
profile.insert("FOO".to_string(), "profile".to_string());
profile.insert("BAR".to_string(), "profile".to_string());
let merged = base.with_overrides(&profile);
assert_eq!(merged.vars.get("VK_PROJECT_NAME").unwrap(), "runtime");
assert_eq!(merged.vars.get("FOO").unwrap(), "profile"); // overrides
assert_eq!(merged.vars.get("BAR").unwrap(), "profile");
}
}

View File

@@ -18,7 +18,7 @@ use workspace_utils::stream_lines::LinesStreamExt;
use super::{AcpClient, SessionManager};
use crate::{
command::CommandParts,
command::{CmdOverrides, CommandParts},
env::ExecutionEnv,
executors::{ExecutorError, ExecutorExitResult, SpawnedChild, acp::AcpEvent},
};
@@ -56,6 +56,7 @@ impl AcpAgentHarness {
prompt: String,
command_parts: CommandParts,
env: &ExecutionEnv,
cmd_overrides: &CmdOverrides,
) -> Result<SpawnedChild, ExecutorError> {
let (program_path, args) = command_parts.into_resolved().await?;
let mut command = Command::new(program_path);
@@ -68,8 +69,9 @@ impl AcpAgentHarness {
.args(&args)
.env("NODE_NO_WARNINGS", "1");
// Apply environment variables
env.apply_to_command(&mut command);
env.clone()
.with_profile(cmd_overrides)
.apply_to_command(&mut command);
let mut child = command.group_spawn()?;
@@ -98,6 +100,7 @@ impl AcpAgentHarness {
session_id: &str,
command_parts: CommandParts,
env: &ExecutionEnv,
cmd_overrides: &CmdOverrides,
) -> Result<SpawnedChild, ExecutorError> {
let (program_path, args) = command_parts.into_resolved().await?;
let mut command = Command::new(program_path);
@@ -110,8 +113,9 @@ impl AcpAgentHarness {
.args(&args)
.env("NODE_NO_WARNINGS", "1");
// Apply environment variables
env.apply_to_command(&mut command);
env.clone()
.with_profile(cmd_overrides)
.apply_to_command(&mut command);
let mut child = command.group_spawn()?;

View File

@@ -65,8 +65,9 @@ impl StandardCodingAgentExecutor for Amp {
.current_dir(current_dir)
.args(&args);
// Apply environment variables
env.apply_to_command(&mut command);
env.clone()
.with_profile(&self.cmd)
.apply_to_command(&mut command);
let mut child = command.group_spawn()?;
@@ -137,8 +138,9 @@ impl StandardCodingAgentExecutor for Amp {
.current_dir(current_dir)
.args(&continue_args);
// Apply environment variables
env.apply_to_command(&mut command);
env.clone()
.with_profile(&self.cmd)
.apply_to_command(&mut command);
let mut child = command.group_spawn()?;

View File

@@ -249,8 +249,9 @@ impl ClaudeCode {
.current_dir(current_dir)
.args(&args);
// Apply environment variables
env.apply_to_command(&mut command);
env.clone()
.with_profile(&self.cmd)
.apply_to_command(&mut command);
// Remove ANTHROPIC_API_KEY if disable_api_key is enabled
if self.disable_api_key.unwrap_or(false) {
@@ -2015,6 +2016,7 @@ mod tests {
cmd: crate::command::CmdOverrides {
base_command_override: None,
additional_params: None,
env: None,
},
approvals_service: None,
disable_api_key: None,

View File

@@ -314,8 +314,9 @@ impl Codex {
.env("NO_COLOR", "1")
.env("RUST_LOG", "error");
// Apply environment variables
env.apply_to_command(&mut process);
env.clone()
.with_profile(&self.cmd)
.apply_to_command(&mut process);
let mut child = process.group_spawn()?;

View File

@@ -122,8 +122,9 @@ impl StandardCodingAgentExecutor for Copilot {
.args(&args)
.env("NODE_NO_WARNINGS", "1");
// Apply environment variables
env.apply_to_command(&mut command);
env.clone()
.with_profile(&self.cmd)
.apply_to_command(&mut command);
let mut child = command.group_spawn()?;
@@ -165,8 +166,9 @@ impl StandardCodingAgentExecutor for Copilot {
.args(&args)
.env("NODE_NO_WARNINGS", "1");
// Apply environment variables
env.apply_to_command(&mut command);
env.clone()
.with_profile(&self.cmd)
.apply_to_command(&mut command);
let mut child = command.group_spawn()?;

View File

@@ -92,8 +92,9 @@ impl StandardCodingAgentExecutor for CursorAgent {
.current_dir(current_dir)
.args(&args);
// Apply environment variables
env.apply_to_command(&mut command);
env.clone()
.with_profile(&self.cmd)
.apply_to_command(&mut command);
let mut child = command.group_spawn()?;
@@ -130,8 +131,9 @@ impl StandardCodingAgentExecutor for CursorAgent {
.current_dir(current_dir)
.args(&args);
// Apply environment variables
env.apply_to_command(&mut command);
env.clone()
.with_profile(&self.cmd)
.apply_to_command(&mut command);
let mut child = command.group_spawn()?;

View File

@@ -110,6 +110,7 @@ async fn spawn_droid(
prompt: &String,
current_dir: &Path,
env: &ExecutionEnv,
cmd_overrides: &crate::command::CmdOverrides,
) -> Result<SpawnedChild, ExecutorError> {
let (program_path, args) = command_parts.into_resolved().await?;
@@ -122,8 +123,9 @@ async fn spawn_droid(
.current_dir(current_dir)
.args(args);
// Apply environment variables
env.apply_to_command(&mut command);
env.clone()
.with_profile(cmd_overrides)
.apply_to_command(&mut command);
let mut child = command.group_spawn()?;
@@ -146,7 +148,7 @@ impl StandardCodingAgentExecutor for Droid {
let droid_command = self.build_command_builder().build_initial()?;
let combined_prompt = self.append_prompt.combine_prompt(prompt);
spawn_droid(droid_command, &combined_prompt, current_dir, env).await
spawn_droid(droid_command, &combined_prompt, current_dir, env, &self.cmd).await
}
async fn spawn_follow_up(
@@ -166,7 +168,7 @@ impl StandardCodingAgentExecutor for Droid {
.build_follow_up(&["--session-id".to_string(), forked_session_id.clone()])?;
let combined_prompt = self.append_prompt.combine_prompt(prompt);
spawn_droid(continue_cmd, &combined_prompt, current_dir, env).await
spawn_droid(continue_cmd, &combined_prompt, current_dir, env, &self.cmd).await
}
fn normalize_logs(&self, msg_store: Arc<MsgStore>, current_dir: &Path) {

View File

@@ -59,7 +59,7 @@ impl StandardCodingAgentExecutor for Gemini {
let combined_prompt = self.append_prompt.combine_prompt(prompt);
let gemini_command = self.build_command_builder().build_initial()?;
harness
.spawn_with_command(current_dir, combined_prompt, gemini_command, env)
.spawn_with_command(current_dir, combined_prompt, gemini_command, env, &self.cmd)
.await
}
@@ -80,6 +80,7 @@ impl StandardCodingAgentExecutor for Gemini {
session_id,
gemini_command,
env,
&self.cmd,
)
.await
}

View File

@@ -154,8 +154,9 @@ impl StandardCodingAgentExecutor for Opencode {
.env("OPENCODE_AUTO_SHARE", "1")
.env("OPENCODE_API", bridge.base_url.clone());
// Apply environment variables
env.apply_to_command(&mut command);
env.clone()
.with_profile(&self.cmd)
.apply_to_command(&mut command);
let mut child = match command.group_spawn() {
Ok(c) => c,
@@ -224,8 +225,9 @@ impl StandardCodingAgentExecutor for Opencode {
.env("OPENCODE_AUTO_SHARE", "1")
.env("OPENCODE_API", bridge.base_url.clone());
// Apply environment variables
env.apply_to_command(&mut command);
env.clone()
.with_profile(&self.cmd)
.apply_to_command(&mut command);
let mut child = match command.group_spawn() {
Ok(c) => c,

View File

@@ -49,7 +49,7 @@ impl StandardCodingAgentExecutor for QwenCode {
let combined_prompt = self.append_prompt.combine_prompt(prompt);
let harness = AcpAgentHarness::with_session_namespace("qwen_sessions");
harness
.spawn_with_command(current_dir, combined_prompt, qwen_command, env)
.spawn_with_command(current_dir, combined_prompt, qwen_command, env, &self.cmd)
.await
}
@@ -70,6 +70,7 @@ impl StandardCodingAgentExecutor for QwenCode {
session_id,
qwen_command,
env,
&self.cmd,
)
.await
}

View File

@@ -1,4 +1,4 @@
import { useMemo, useEffect, useState } from 'react';
import { useMemo, useEffect, useState, useCallback } from 'react';
import Form from '@rjsf/core';
import type { IChangeEvent } from '@rjsf/core';
import { RJSFValidationError } from '@rjsf/utils';
@@ -44,6 +44,38 @@ export function ExecutorConfigForm({
return schemas[executor];
}, [executor]);
// Custom handler for env field updates
const handleEnvChange = useCallback(
(envData: Record<string, string> | undefined) => {
const newFormData = {
...(formData as Record<string, unknown>),
env: envData,
};
setFormData(newFormData);
if (onChange) {
onChange(newFormData);
}
},
[formData, onChange]
);
const uiSchema = useMemo(
() => ({
env: {
'ui:field': 'KeyValueField',
},
}),
[]
);
// Pass the env update handler via formContext
const formContext = useMemo(
() => ({
onEnvChange: handleEnvChange,
}),
[handleEnvChange]
);
useEffect(() => {
setFormData(value || {});
setValidationErrors([]);
@@ -87,7 +119,9 @@ export function ExecutorConfigForm({
<CardContent className="p-0">
<Form
schema={schema}
uiSchema={uiSchema}
formData={formData}
formContext={formContext}
onChange={handleChange}
onSubmit={handleSubmit}
onError={handleError}
@@ -97,6 +131,7 @@ export function ExecutorConfigForm({
showErrorList={false}
widgets={shadcnTheme.widgets}
templates={shadcnTheme.templates}
fields={shadcnTheme.fields}
>
{onSave && (
<div className="flex justify-end pt-4">

View File

@@ -0,0 +1,137 @@
import { FieldProps } from '@rjsf/utils';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Plus, X } from 'lucide-react';
import { useState, useCallback, useMemo } from 'react';
type KeyValueData = Record<string, string>;
interface EnvFormContext {
onEnvChange?: (envData: KeyValueData | undefined) => void;
}
export function KeyValueField({
formData,
disabled,
readonly,
registry,
}: FieldProps<KeyValueData>) {
const [newKey, setNewKey] = useState('');
const [newValue, setNewValue] = useState('');
// Get the custom env change handler from formContext
const formContext = registry.formContext as EnvFormContext | undefined;
// Ensure we have a stable object reference
const data: KeyValueData = useMemo(() => formData ?? {}, [formData]);
const entries = useMemo(() => Object.entries(data), [data]);
// Use the formContext handler to update env correctly
const updateValue = useCallback(
(newData: KeyValueData | undefined) => {
formContext?.onEnvChange?.(newData);
},
[formContext]
);
const handleAdd = useCallback(() => {
const trimmedKey = newKey.trim();
if (trimmedKey) {
updateValue({
...data,
[trimmedKey]: newValue,
});
setNewKey('');
setNewValue('');
}
}, [data, newKey, newValue, updateValue]);
const handleRemove = useCallback(
(key: string) => {
const updated = { ...data };
delete updated[key];
updateValue(Object.keys(updated).length > 0 ? updated : undefined);
},
[data, updateValue]
);
const handleValueChange = useCallback(
(key: string, value: string) => {
updateValue({ ...data, [key]: value });
},
[data, updateValue]
);
const isDisabled = disabled || readonly;
return (
<div className="space-y-3">
{entries.map(([key, value]) => (
<div key={key} className="flex gap-2 items-center">
<Input
value={key}
disabled
className="flex-1 font-mono text-sm"
aria-label="Environment variable key"
/>
<Input
value={value ?? ''}
onChange={(e) => handleValueChange(key, e.target.value)}
disabled={isDisabled}
className="flex-1 font-mono text-sm"
placeholder="Value"
aria-label={`Value for ${key}`}
/>
<Button
type="button"
variant="ghost"
size="sm"
onClick={() => handleRemove(key)}
disabled={isDisabled}
className="h-8 w-8 p-0 shrink-0 text-muted-foreground hover:text-destructive hover:bg-destructive/10"
aria-label={`Remove ${key}`}
>
<X className="w-4 h-4" />
</Button>
</div>
))}
{/* Add new entry row */}
<div className="flex gap-2 items-center">
<Input
value={newKey}
onChange={(e) => setNewKey(e.target.value)}
disabled={isDisabled}
placeholder="KEY"
className="flex-1 font-mono text-sm"
aria-label="New environment variable key"
/>
<Input
value={newValue}
onChange={(e) => setNewValue(e.target.value)}
disabled={isDisabled}
placeholder="value"
className="flex-1 font-mono text-sm"
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
handleAdd();
}
}}
aria-label="New environment variable value"
/>
<Button
type="button"
variant="outline"
size="sm"
onClick={handleAdd}
disabled={isDisabled || !newKey.trim()}
className="h-8 w-8 p-0 shrink-0"
aria-label="Add environment variable"
>
<Plus className="w-4 h-4" />
</Button>
</div>
</div>
);
}

View File

@@ -0,0 +1 @@
export { KeyValueField } from './KeyValueField';

View File

@@ -1,3 +1,9 @@
export { shadcnTheme, customWidgets, customTemplates } from './theme';
export {
shadcnTheme,
customWidgets,
customTemplates,
customFields,
} from './theme';
export * from './widgets';
export * from './templates';
export * from './fields';

View File

@@ -1,4 +1,4 @@
import { RegistryWidgetsType } from '@rjsf/utils';
import { RegistryFieldsType, RegistryWidgetsType } from '@rjsf/utils';
import {
TextWidget,
SelectWidget,
@@ -12,6 +12,7 @@ import {
ObjectFieldTemplate,
FormTemplate,
} from './templates';
import { KeyValueField } from './fields';
export const customWidgets: RegistryWidgetsType = {
TextWidget,
@@ -29,7 +30,12 @@ export const customTemplates = {
FormTemplate,
};
export const customFields: RegistryFieldsType = {
KeyValueField,
};
export const shadcnTheme = {
widgets: customWidgets,
templates: customTemplates,
fields: customFields,
};

View File

@@ -515,6 +515,7 @@ export function AgentSettings() {
selectedConfiguration
]?.[selectedExecutorType] && (
<ExecutorConfigForm
key={`${selectedExecutorType}-${selectedConfiguration}`}
executor={selectedExecutorType}
value={
(executorsMap[selectedExecutorType][

View File

@@ -37,6 +37,17 @@
"items": {
"type": "string"
}
},
"env": {
"title": "Environment Variables",
"description": "Environment variables to set when running the executor",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "string"
}
}
},
"type": "object"

View File

@@ -65,6 +65,17 @@
"items": {
"type": "string"
}
},
"env": {
"title": "Environment Variables",
"description": "Environment variables to set when running the executor",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "string"
}
}
},
"type": "object"

View File

@@ -145,6 +145,17 @@
"items": {
"type": "string"
}
},
"env": {
"title": "Environment Variables",
"description": "Environment variables to set when running the executor",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "string"
}
}
},
"type": "object"

View File

@@ -71,6 +71,17 @@
"items": {
"type": "string"
}
},
"env": {
"title": "Environment Variables",
"description": "Environment variables to set when running the executor",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "string"
}
}
},
"type": "object"

View File

@@ -43,6 +43,17 @@
"items": {
"type": "string"
}
},
"env": {
"title": "Environment Variables",
"description": "Environment variables to set when running the executor",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "string"
}
}
},
"type": "object"

View File

@@ -67,6 +67,17 @@
"items": {
"type": "string"
}
},
"env": {
"title": "Environment Variables",
"description": "Environment variables to set when running the executor",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "string"
}
}
},
"description": "Droid executor configuration",

View File

@@ -41,6 +41,17 @@
"items": {
"type": "string"
}
},
"env": {
"title": "Environment Variables",
"description": "Environment variables to set when running the executor",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "string"
}
}
},
"type": "object"

View File

@@ -41,6 +41,17 @@
"items": {
"type": "string"
}
},
"env": {
"title": "Environment Variables",
"description": "Environment variables to set when running the executor",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "string"
}
}
},
"type": "object"

View File

@@ -35,6 +35,17 @@
"items": {
"type": "string"
}
},
"env": {
"title": "Environment Variables",
"description": "Environment variables to set when running the executor",
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "string"
}
}
},
"type": "object"

View File

@@ -356,13 +356,13 @@ export type ExecutorConfigs = { executors: { [key in BaseCodingAgent]?: Executor
export enum BaseAgentCapability { SESSION_FORK = "SESSION_FORK", SETUP_HELPER = "SETUP_HELPER" }
export type ClaudeCode = { append_prompt: AppendPrompt, claude_code_router?: boolean | null, plan?: boolean | null, approvals?: boolean | null, model?: string | null, dangerously_skip_permissions?: boolean | null, disable_api_key?: boolean | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
export type ClaudeCode = { append_prompt: AppendPrompt, claude_code_router?: boolean | null, plan?: boolean | null, approvals?: boolean | null, model?: string | null, dangerously_skip_permissions?: boolean | null, disable_api_key?: boolean | null, base_command_override?: string | null, additional_params?: Array<string> | null, env?: { [key in string]?: string } | null, };
export type Gemini = { append_prompt: AppendPrompt, model?: string | null, yolo?: boolean | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
export type Gemini = { append_prompt: AppendPrompt, model?: string | null, yolo?: boolean | null, base_command_override?: string | null, additional_params?: Array<string> | null, env?: { [key in string]?: string } | null, };
export type Amp = { append_prompt: AppendPrompt, dangerously_allow_all?: boolean | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
export type Amp = { append_prompt: AppendPrompt, dangerously_allow_all?: boolean | null, base_command_override?: string | null, additional_params?: Array<string> | null, env?: { [key in string]?: string } | null, };
export type Codex = { append_prompt: AppendPrompt, sandbox?: SandboxMode | null, ask_for_approval?: AskForApproval | null, oss?: boolean | null, model?: string | null, model_reasoning_effort?: ReasoningEffort | null, model_reasoning_summary?: ReasoningSummary | null, model_reasoning_summary_format?: ReasoningSummaryFormat | null, profile?: string | null, base_instructions?: string | null, include_apply_patch_tool?: boolean | null, model_provider?: string | null, compact_prompt?: string | null, developer_instructions?: string | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
export type Codex = { append_prompt: AppendPrompt, sandbox?: SandboxMode | null, ask_for_approval?: AskForApproval | null, oss?: boolean | null, model?: string | null, model_reasoning_effort?: ReasoningEffort | null, model_reasoning_summary?: ReasoningSummary | null, model_reasoning_summary_format?: ReasoningSummaryFormat | null, profile?: string | null, base_instructions?: string | null, include_apply_patch_tool?: boolean | null, model_provider?: string | null, compact_prompt?: string | null, developer_instructions?: string | null, base_command_override?: string | null, additional_params?: Array<string> | null, env?: { [key in string]?: string } | null, };
export type SandboxMode = "auto" | "read-only" | "workspace-write" | "danger-full-access";
@@ -374,15 +374,15 @@ export type ReasoningSummary = "auto" | "concise" | "detailed" | "none";
export type ReasoningSummaryFormat = "none" | "experimental";
export type CursorAgent = { append_prompt: AppendPrompt, force?: boolean | null, model?: string | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
export type CursorAgent = { append_prompt: AppendPrompt, force?: boolean | null, model?: string | null, base_command_override?: string | null, additional_params?: Array<string> | null, env?: { [key in string]?: string } | null, };
export type Copilot = { append_prompt: AppendPrompt, model?: string | null, allow_all_tools?: boolean | null, allow_tool?: string | null, deny_tool?: string | null, add_dir?: Array<string> | null, disable_mcp_server?: Array<string> | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
export type Copilot = { append_prompt: AppendPrompt, model?: string | null, allow_all_tools?: boolean | null, allow_tool?: string | null, deny_tool?: string | null, add_dir?: Array<string> | null, disable_mcp_server?: Array<string> | null, base_command_override?: string | null, additional_params?: Array<string> | null, env?: { [key in string]?: string } | null, };
export type Opencode = { append_prompt: AppendPrompt, model?: string | null, agent?: string | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
export type Opencode = { append_prompt: AppendPrompt, model?: string | null, agent?: string | null, base_command_override?: string | null, additional_params?: Array<string> | null, env?: { [key in string]?: string } | null, };
export type QwenCode = { append_prompt: AppendPrompt, yolo?: boolean | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
export type QwenCode = { append_prompt: AppendPrompt, yolo?: boolean | null, base_command_override?: string | null, additional_params?: Array<string> | null, env?: { [key in string]?: string } | null, };
export type Droid = { append_prompt: AppendPrompt, autonomy: Autonomy, model?: string | null, reasoning_effort?: DroidReasoningEffort | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
export type Droid = { append_prompt: AppendPrompt, autonomy: Autonomy, model?: string | null, reasoning_effort?: DroidReasoningEffort | null, base_command_override?: string | null, additional_params?: Array<string> | null, env?: { [key in string]?: string } | null, };
export type Autonomy = "normal" | "low" | "medium" | "high" | "skip-permissions-unsafe";