diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 70ca64a8f4..2262ce240e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -194,6 +194,11 @@ jobs: source ./bin/activate-hermit just check-acp-schema + - name: Check Config Schema is Up-to-Date + run: | + source ./bin/activate-hermit + just check-config-schema + desktop-lint: name: Test and Lint Electron Desktop App runs-on: macos-latest diff --git a/Justfile b/Justfile index bb4996370c..5d98408c00 100644 --- a/Justfile +++ b/Justfile @@ -17,6 +17,8 @@ check-everything: cd ui/desktop && pnpm run lint:check @echo " → Validating OpenAPI schema..." ./scripts/check-openapi-schema.sh + @echo " → Validating config schema..." + just check-config-schema @echo "" @echo "✅ All style checks passed!" @@ -200,6 +202,26 @@ generate-openapi: @echo "Generating frontend API..." cd ui/desktop && npx @hey-api/openapi-ts +# Generate config.schema.json from Rust types +generate-config-schema: + @echo "Generating config schema..." + cargo run -p goose --bin generate-config-schema + @echo "Config schema generated: crates/goose/config.schema.json" + +# Check if config.schema.json is up-to-date +check-config-schema: generate-config-schema + #!/usr/bin/env bash + set -e + echo "🔍 Checking config schema is up-to-date..." + if ! git diff --exit-code crates/goose/config.schema.json; then + echo "" + echo "❌ Config schema is out of date!" + echo "" + echo "Run 'just generate-config-schema' locally, then commit the changes." + exit 1 + fi + echo "✅ Config schema is up-to-date" + # Check if generated ACP schema and TypeScript types are up-to-date check-acp-schema: generate-acp-types #!/usr/bin/env bash diff --git a/crates/goose-cli/src/commands/configure.rs b/crates/goose-cli/src/commands/configure.rs index 256f8b9639..3e70097b56 100644 --- a/crates/goose-cli/src/commands/configure.rs +++ b/crates/goose-cli/src/commands/configure.rs @@ -1490,7 +1490,11 @@ pub fn configure_keyring_dialog() -> anyhow::Result<()> { ); } - let currently_disabled = config.get_param::("GOOSE_DISABLE_KEYRING").is_ok(); + let currently_disabled = config + .get_param::("GOOSE_DISABLE_KEYRING") + .is_ok_and(|v| { + v.as_bool().unwrap_or(false) || v.as_str().is_some_and(|s| s == "true" || s == "1") + }); let current_status = if currently_disabled { "Disabled (using file-based storage)" diff --git a/crates/goose-server/src/openapi.rs b/crates/goose-server/src/openapi.rs index ae33e32b69..5301562678 100644 --- a/crates/goose-server/src/openapi.rs +++ b/crates/goose-server/src/openapi.rs @@ -391,6 +391,8 @@ derive_utoipa!(IconTheme as IconThemeSchema); super::routes::config_management::upsert_config, super::routes::config_management::remove_config, super::routes::config_management::read_config, + super::routes::config_management::read_typed_config, + super::routes::config_management::patch_typed_config, super::routes::config_management::add_extension, super::routes::config_management::remove_extension, super::routes::config_management::get_extensions, @@ -484,6 +486,9 @@ derive_utoipa!(IconTheme as IconThemeSchema); super::routes::config_management::UpsertConfigQuery, super::routes::config_management::ConfigKeyQuery, super::routes::config_management::ConfigResponse, + goose::config::schema::GooseConfigSchema, + goose::config::schema::GooseConfigUpdate, + goose::slash_commands::SlashCommandMapping, super::routes::config_management::ProvidersResponse, super::routes::config_management::ProviderDetails, super::routes::config_management::SlashCommandsResponse, diff --git a/crates/goose-server/src/routes/config_management.rs b/crates/goose-server/src/routes/config_management.rs index e5203fe419..758873454f 100644 --- a/crates/goose-server/src/routes/config_management.rs +++ b/crates/goose-server/src/routes/config_management.rs @@ -9,6 +9,7 @@ use axum::{ }; use goose::config::declarative_providers::LoadedProvider; use goose::config::paths::Paths; +use goose::config::schema::{GooseConfigSchema, GooseConfigUpdate}; use goose::config::ExtensionEntry; use goose::config::{Config, ConfigError}; use goose::custom_requests::SourceType; @@ -846,9 +847,57 @@ pub async fn configure_provider_oauth( Ok(Json("OAuth configuration completed".to_string())) } +#[utoipa::path( + get, + path = "/config/typed", + responses( + (status = 200, description = "All configuration values (typed)", body = GooseConfigSchema), + (status = 500, description = "Internal server error") + ) +)] +pub async fn read_typed_config() -> Result, ErrorResponse> { + let config = Config::global(); + let typed = GooseConfigSchema::from_config(config); + Ok(Json(typed)) +} + +/// Update configuration values via sparse patch. Only send the fields you want +/// to change — omitted and null fields are both left unchanged (serde cannot +/// distinguish the two). To delete a key, use `POST /config/remove`. +/// +/// Nested objects (`extensions`, `slash_commands`, `experiments`) use whole-value +/// replacement, not deep merge. Secret fields (API keys) are stored in the system +/// keyring, not the config file. +/// +/// **Caution:** `GET /config/typed` returns values merged from env vars, system +/// config, and user config. Sending the full GET response back as a PATCH payload +/// can persist inherited/env values into the user config file. Only send fields +/// the user explicitly changed. +#[utoipa::path( + patch, + path = "/config/typed", + request_body = GooseConfigUpdate, + responses( + (status = 200, description = "Configuration updated", body = GooseConfigSchema), + (status = 500, description = "Internal server error") + ) +)] +pub async fn patch_typed_config( + Json(update): Json, +) -> Result, ErrorResponse> { + let config = Config::global(); + update.apply_to_config(config)?; + let typed = GooseConfigSchema::from_config(config); + Ok(Json(typed)) +} + pub fn routes(state: Arc) -> Router { Router::new() .route("/config", get(read_all_config)) + .route( + "/config/typed", + get(read_typed_config).patch(patch_typed_config), + ) .route("/config/upsert", post(upsert_config)) .route("/config/remove", post(remove_config)) .route("/config/read", post(read_config)) diff --git a/crates/goose/Cargo.toml b/crates/goose/Cargo.toml index 6f6d80007d..97014c06be 100644 --- a/crates/goose/Cargo.toml +++ b/crates/goose/Cargo.toml @@ -259,6 +259,10 @@ path = "src/providers/canonical/build_canonical_models.rs" name = "generate-acp-schema" path = "src/bin/generate_acp_schema.rs" +[[bin]] +name = "generate-config-schema" +path = "src/bin/generate_config_schema.rs" + [package.metadata.cargo-machete] ignored = [ diff --git a/crates/goose/config.schema.json b/crates/goose/config.schema.json new file mode 100644 index 0000000000..e1e142d4ec --- /dev/null +++ b/crates/goose/config.schema.json @@ -0,0 +1,1436 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "GooseConfigSchema", + "description": "JSON Schema representation of Goose's config.yaml.\n\nAll fields are optional. The standalone JSON Schema (`config.schema.json`)\nsets `additionalProperties: true` so config.yaml can carry undocumented\nprovider-specific keys as env-var overrides. However, the typed API\nendpoints only persist fields explicitly declared on this struct — unknown\nkeys in a `PATCH /config/typed` payload are silently dropped by serde.", + "type": "object", + "properties": { + "GOOSE_PROVIDER": { + "type": [ + "string", + "null" + ] + }, + "GOOSE_MODEL": { + "type": [ + "string", + "null" + ] + }, + "GOOSE_MODE": { + "anyOf": [ + { + "$ref": "#/$defs/GooseMode" + }, + { + "type": "null" + } + ] + }, + "GOOSE_MAX_TOKENS": { + "type": [ + "integer", + "null" + ], + "format": "int32" + }, + "GOOSE_CONTEXT_LIMIT": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "GOOSE_INPUT_LIMIT": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "GOOSE_MAX_TURNS": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0 + }, + "GOOSE_MAX_ACTIVE_AGENTS": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "GOOSE_AUTO_COMPACT_THRESHOLD": { + "type": [ + "number", + "null" + ], + "format": "double" + }, + "GOOSE_TOOL_PAIR_SUMMARIZATION": { + "type": [ + "boolean", + "null" + ] + }, + "GOOSE_TOOL_CALL_CUTOFF": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "GOOSE_STREAM_TIMEOUT": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "GOOSE_SEARCH_PATHS": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "GOOSE_DISABLE_SESSION_NAMING": { + "type": [ + "boolean", + "null" + ] + }, + "GOOSE_DISABLE_KEYRING": { + "type": [ + "boolean", + "null" + ] + }, + "GOOSE_TELEMETRY_ENABLED": { + "type": [ + "boolean", + "null" + ] + }, + "GOOSE_DEFAULT_EXTENSION_TIMEOUT": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "GOOSE_PROMPT_EDITOR": { + "type": [ + "string", + "null" + ] + }, + "GOOSE_PROMPT_EDITOR_ALWAYS": { + "type": [ + "boolean", + "null" + ] + }, + "GOOSE_ALLOWLIST": { + "type": [ + "string", + "null" + ] + }, + "GOOSE_SYSTEM_PROMPT_FILE_PATH": { + "type": [ + "string", + "null" + ] + }, + "GOOSE_DEBUG": { + "type": [ + "boolean", + "null" + ] + }, + "GOOSE_SHOW_FULL_OUTPUT": { + "type": [ + "boolean", + "null" + ] + }, + "GOOSE_DISABLE_TOOL_CALL_SUMMARY": { + "type": [ + "boolean", + "null" + ] + }, + "GOOSE_STATUS_HOOK": { + "type": [ + "string", + "null" + ] + }, + "GOOSE_LOCAL_ENABLE_THINKING": { + "type": [ + "boolean", + "null" + ] + }, + "GOOSE_DATABRICKS_CLIENT_REQUEST_ID": { + "type": [ + "boolean", + "null" + ] + }, + "CONTEXT_FILE_NAMES": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "EDIT_MODE": { + "type": [ + "string", + "null" + ] + }, + "RANDOM_THINKING_MESSAGES": { + "type": [ + "boolean", + "null" + ] + }, + "CODE_MODE_TOOL_DISCLOSURE": { + "type": [ + "string", + "null" + ] + }, + "GOOSE_CLIENT_CERT_PATH": { + "type": [ + "string", + "null" + ] + }, + "GOOSE_CLIENT_KEY_PATH": { + "type": [ + "string", + "null" + ] + }, + "GOOSE_CA_CERT_PATH": { + "type": [ + "string", + "null" + ] + }, + "GOOSE_PLANNER_PROVIDER": { + "type": [ + "string", + "null" + ] + }, + "GOOSE_PLANNER_MODEL": { + "type": [ + "string", + "null" + ] + }, + "GOOSE_SUBAGENT_PROVIDER": { + "type": [ + "string", + "null" + ] + }, + "GOOSE_SUBAGENT_MODEL": { + "type": [ + "string", + "null" + ] + }, + "GOOSE_SUBAGENT_MAX_TURNS": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "GOOSE_MAX_BACKGROUND_TASKS": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "GOOSE_RECIPE_GITHUB_REPO": { + "type": [ + "string", + "null" + ] + }, + "GOOSE_RECIPE_RETRY_TIMEOUT_SECONDS": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "GOOSE_RECIPE_ON_FAILURE_TIMEOUT_SECONDS": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "GOOSE_CLI_MIN_PRIORITY": { + "type": [ + "number", + "null" + ], + "format": "float" + }, + "GOOSE_CLI_THEME": { + "type": [ + "string", + "null" + ] + }, + "GOOSE_CLI_LIGHT_THEME": { + "type": [ + "string", + "null" + ] + }, + "GOOSE_CLI_DARK_THEME": { + "type": [ + "string", + "null" + ] + }, + "GOOSE_CLI_SHOW_COST": { + "type": [ + "boolean", + "null" + ] + }, + "GOOSE_CLI_SHOW_THINKING": { + "type": [ + "boolean", + "null" + ] + }, + "GOOSE_CLI_NEWLINE_KEY": { + "type": [ + "string", + "null" + ] + }, + "CLAUDE_CODE_COMMAND": { + "type": [ + "string", + "null" + ] + }, + "GEMINI_CLI_COMMAND": { + "type": [ + "string", + "null" + ] + }, + "CURSOR_AGENT_COMMAND": { + "type": [ + "string", + "null" + ] + }, + "CODEX_COMMAND": { + "type": [ + "string", + "null" + ] + }, + "CODEX_REASONING_EFFORT": { + "type": [ + "string", + "null" + ] + }, + "CODEX_ENABLE_SKILLS": { + "type": [ + "string", + "null" + ] + }, + "CODEX_SKIP_GIT_CHECK": { + "type": [ + "string", + "null" + ] + }, + "CHATGPT_CODEX_REASONING_EFFORT": { + "type": [ + "string", + "null" + ] + }, + "CLAUDE_THINKING_TYPE": { + "type": [ + "string", + "null" + ] + }, + "CLAUDE_THINKING_EFFORT": { + "type": [ + "string", + "null" + ] + }, + "CLAUDE_THINKING_BUDGET": { + "type": [ + "integer", + "null" + ], + "format": "int32" + }, + "GEMINI3_THINKING_LEVEL": { + "type": [ + "string", + "null" + ] + }, + "GEMINI25_THINKING_BUDGET": { + "type": [ + "integer", + "null" + ], + "format": "int32" + }, + "SECURITY_PROMPT_ENABLED": { + "type": [ + "boolean", + "null" + ] + }, + "SECURITY_PROMPT_THRESHOLD": { + "type": [ + "number", + "null" + ], + "format": "double" + }, + "SECURITY_PROMPT_CLASSIFIER_ENABLED": { + "type": [ + "boolean", + "null" + ] + }, + "SECURITY_PROMPT_CLASSIFIER_MODEL": { + "type": [ + "string", + "null" + ] + }, + "SECURITY_PROMPT_CLASSIFIER_ENDPOINT": { + "type": [ + "string", + "null" + ] + }, + "SECURITY_COMMAND_CLASSIFIER_ENABLED": { + "type": [ + "boolean", + "null" + ] + }, + "OPENAI_HOST": { + "type": [ + "string", + "null" + ] + }, + "OPENAI_BASE_URL": { + "type": [ + "string", + "null" + ] + }, + "OPENAI_BASE_PATH": { + "type": [ + "string", + "null" + ] + }, + "OPENAI_ORGANIZATION": { + "type": [ + "string", + "null" + ] + }, + "OPENAI_PROJECT": { + "type": [ + "string", + "null" + ] + }, + "OPENAI_TIMEOUT": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "ANTHROPIC_HOST": { + "type": [ + "string", + "null" + ] + }, + "OLLAMA_HOST": { + "type": [ + "string", + "null" + ] + }, + "OLLAMA_TIMEOUT": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "OLLAMA_STREAM_TIMEOUT": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "OLLAMA_STREAM_USAGE": { + "type": [ + "boolean", + "null" + ] + }, + "DATABRICKS_HOST": { + "type": [ + "string", + "null" + ] + }, + "DATABRICKS_MAX_RETRIES": { + "type": [ + "string", + "null" + ] + }, + "DATABRICKS_INITIAL_RETRY_INTERVAL_MS": { + "type": [ + "string", + "null" + ] + }, + "DATABRICKS_BACKOFF_MULTIPLIER": { + "type": [ + "string", + "null" + ] + }, + "DATABRICKS_MAX_RETRY_INTERVAL_MS": { + "type": [ + "string", + "null" + ] + }, + "AZURE_OPENAI_ENDPOINT": { + "type": [ + "string", + "null" + ] + }, + "AZURE_OPENAI_DEPLOYMENT_NAME": { + "type": [ + "string", + "null" + ] + }, + "AZURE_OPENAI_API_VERSION": { + "type": [ + "string", + "null" + ] + }, + "GOOGLE_HOST": { + "type": [ + "string", + "null" + ] + }, + "GCP_PROJECT_ID": { + "type": [ + "string", + "null" + ] + }, + "GCP_LOCATION": { + "type": [ + "string", + "null" + ] + }, + "GCP_MAX_RETRIES": { + "type": [ + "string", + "null" + ] + }, + "GCP_INITIAL_RETRY_INTERVAL_MS": { + "type": [ + "string", + "null" + ] + }, + "GCP_BACKOFF_MULTIPLIER": { + "type": [ + "string", + "null" + ] + }, + "GCP_MAX_RETRY_INTERVAL_MS": { + "type": [ + "string", + "null" + ] + }, + "AWS_REGION": { + "type": [ + "string", + "null" + ] + }, + "AWS_PROFILE": { + "type": [ + "string", + "null" + ] + }, + "BEDROCK_MAX_RETRIES": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "BEDROCK_INITIAL_RETRY_INTERVAL_MS": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "BEDROCK_BACKOFF_MULTIPLIER": { + "type": [ + "number", + "null" + ], + "format": "double" + }, + "BEDROCK_MAX_RETRY_INTERVAL_MS": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "BEDROCK_ENABLE_CACHING": { + "type": [ + "boolean", + "null" + ] + }, + "SAGEMAKER_ENDPOINT_NAME": { + "type": [ + "string", + "null" + ] + }, + "LITELLM_HOST": { + "type": [ + "string", + "null" + ] + }, + "LITELLM_BASE_PATH": { + "type": [ + "string", + "null" + ] + }, + "LITELLM_TIMEOUT": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "SNOWFLAKE_HOST": { + "type": [ + "string", + "null" + ] + }, + "GITHUB_COPILOT_HOST": { + "type": [ + "string", + "null" + ] + }, + "GITHUB_COPILOT_CLIENT_ID": { + "type": [ + "string", + "null" + ] + }, + "GITHUB_COPILOT_TOKEN_URL": { + "type": [ + "string", + "null" + ] + }, + "XAI_HOST": { + "type": [ + "string", + "null" + ] + }, + "OPENROUTER_HOST": { + "type": [ + "string", + "null" + ] + }, + "VENICE_HOST": { + "type": [ + "string", + "null" + ] + }, + "VENICE_BASE_PATH": { + "type": [ + "string", + "null" + ] + }, + "VENICE_MODELS_PATH": { + "type": [ + "string", + "null" + ] + }, + "TETRATE_HOST": { + "type": [ + "string", + "null" + ] + }, + "AVIAN_HOST": { + "type": [ + "string", + "null" + ] + }, + "otel_exporter_otlp_endpoint": { + "type": [ + "string", + "null" + ] + }, + "otel_exporter_otlp_timeout": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "tunnel_auto_start": { + "type": [ + "boolean", + "null" + ] + }, + "extensions": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "$ref": "#/$defs/ExtensionEntry" + } + }, + "slash_commands": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/$defs/SlashCommandMapping" + } + }, + "experiments": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "boolean" + } + } + }, + "$defs": { + "GooseMode": { + "type": "string", + "enum": [ + "auto", + "approve", + "smart_approve", + "chat" + ] + }, + "ExtensionEntry": { + "description": "Represents the different types of MCP extensions that can be added to the manager", + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": [ + "enabled" + ], + "oneOf": [ + { + "description": "SSE transport is no longer supported - kept only for config file compatibility", + "type": "object", + "properties": { + "name": { + "type": "string", + "default": "" + }, + "description": { + "type": "string", + "default": "" + }, + "uri": { + "type": [ + "string", + "null" + ], + "default": null + }, + "type": { + "type": "string", + "const": "sse" + } + }, + "required": [ + "type" + ] + }, + { + "description": "Standard I/O client with command and arguments", + "type": "object", + "properties": { + "name": { + "description": "The name used to identify this extension", + "type": "string" + }, + "description": { + "type": "string", + "default": "" + }, + "cmd": { + "type": "string" + }, + "args": { + "type": "array", + "items": { + "type": "string" + } + }, + "envs": { + "$ref": "#/$defs/Envs", + "default": {} + }, + "env_keys": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "timeout": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "bundled": { + "type": [ + "boolean", + "null" + ], + "default": null + }, + "available_tools": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "type": { + "type": "string", + "const": "stdio" + } + }, + "required": [ + "type", + "name", + "cmd", + "args" + ] + }, + { + "description": "Built-in extension that is part of the bundled goose MCP server", + "type": "object", + "properties": { + "name": { + "description": "The name used to identify this extension", + "type": "string" + }, + "description": { + "type": "string", + "default": "" + }, + "display_name": { + "type": [ + "string", + "null" + ] + }, + "timeout": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "bundled": { + "type": [ + "boolean", + "null" + ], + "default": null + }, + "available_tools": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "type": { + "type": "string", + "const": "builtin" + } + }, + "required": [ + "type", + "name" + ] + }, + { + "description": "Platform extensions that have direct access to the agent etc and run in the agent process", + "type": "object", + "properties": { + "name": { + "description": "The name used to identify this extension", + "type": "string" + }, + "description": { + "type": "string", + "default": "" + }, + "display_name": { + "type": [ + "string", + "null" + ] + }, + "bundled": { + "type": [ + "boolean", + "null" + ], + "default": null + }, + "available_tools": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "type": { + "type": "string", + "const": "platform" + } + }, + "required": [ + "type", + "name" + ] + }, + { + "description": "Streamable HTTP client with a URI endpoint using MCP Streamable HTTP specification", + "type": "object", + "properties": { + "name": { + "description": "The name used to identify this extension", + "type": "string" + }, + "description": { + "type": "string", + "default": "" + }, + "uri": { + "type": "string" + }, + "envs": { + "$ref": "#/$defs/Envs", + "default": {} + }, + "env_keys": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "default": {} + }, + "timeout": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "socket": { + "description": "Optional Unix domain socket path for HTTP-over-UDS transport.\nWhen set, the HTTP connection is routed through this socket while\n`uri` is used for the Host header and path.\nUse `@name` for Linux abstract sockets.", + "type": [ + "string", + "null" + ], + "default": null + }, + "bundled": { + "type": [ + "boolean", + "null" + ], + "default": null + }, + "available_tools": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "type": { + "type": "string", + "const": "streamable_http" + } + }, + "required": [ + "type", + "name", + "uri" + ] + }, + { + "description": "Frontend-provided tools that will be called through the frontend", + "type": "object", + "properties": { + "name": { + "description": "The name used to identify this extension", + "type": "string" + }, + "description": { + "type": "string", + "default": "" + }, + "tools": { + "description": "The tools provided by the frontend", + "type": "array", + "items": { + "$ref": "#/$defs/Tool" + } + }, + "instructions": { + "description": "Instructions for how to use these tools", + "type": [ + "string", + "null" + ] + }, + "bundled": { + "type": [ + "boolean", + "null" + ], + "default": null + }, + "available_tools": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "type": { + "type": "string", + "const": "frontend" + } + }, + "required": [ + "type", + "name", + "tools" + ] + }, + { + "description": "Inline Python code that will be executed using uvx", + "type": "object", + "properties": { + "name": { + "description": "The name used to identify this extension", + "type": "string" + }, + "description": { + "type": "string", + "default": "" + }, + "code": { + "description": "The Python code to execute", + "type": "string" + }, + "timeout": { + "description": "Timeout in seconds", + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "dependencies": { + "description": "Python package dependencies required by this extension", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + }, + "default": null + }, + "available_tools": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "type": { + "type": "string", + "const": "inline_python" + } + }, + "required": [ + "type", + "name", + "code" + ] + } + ] + }, + "Envs": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "Tool": { + "description": "A tool that can be used by a model.", + "type": "object", + "properties": { + "name": { + "description": "The name of the tool", + "type": "string" + }, + "title": { + "description": "A human-readable title for the tool", + "type": [ + "string", + "null" + ] + }, + "description": { + "description": "A description of what the tool does", + "type": [ + "string", + "null" + ] + }, + "inputSchema": { + "description": "A JSON Schema object defining the expected parameters for the tool", + "type": "object", + "additionalProperties": true + }, + "outputSchema": { + "description": "An optional JSON Schema object defining the structure of the tool's output", + "type": [ + "object", + "null" + ], + "additionalProperties": true + }, + "annotations": { + "description": "Optional additional tool information.", + "anyOf": [ + { + "$ref": "#/$defs/ToolAnnotations" + }, + { + "type": "null" + } + ] + }, + "execution": { + "description": "Execution-related configuration including task support mode.", + "anyOf": [ + { + "$ref": "#/$defs/ToolExecution" + }, + { + "type": "null" + } + ] + }, + "icons": { + "description": "Optional list of icons for the tool", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/$defs/Icon" + } + }, + "_meta": { + "description": "Optional additional metadata for this tool", + "type": [ + "object", + "null" + ], + "additionalProperties": true + } + }, + "required": [ + "name", + "inputSchema" + ] + }, + "ToolAnnotations": { + "description": "Additional properties describing a Tool to clients.\n\nNOTE: all properties in ToolAnnotations are **hints**.\nThey are not guaranteed to provide a faithful description of\ntool behavior (including descriptive properties like `title`).\n\nClients should never make tool use decisions based on ToolAnnotations\nreceived from untrusted servers.", + "type": "object", + "properties": { + "title": { + "description": "A human-readable title for the tool.", + "type": [ + "string", + "null" + ] + }, + "readOnlyHint": { + "description": "If true, the tool does not modify its environment.\n\nDefault: false", + "type": [ + "boolean", + "null" + ] + }, + "destructiveHint": { + "description": "If true, the tool may perform destructive updates to its environment.\nIf false, the tool performs only additive updates.\n\n(This property is meaningful only when `readOnlyHint == false`)\n\nDefault: true\nA human-readable description of the tool's purpose.", + "type": [ + "boolean", + "null" + ] + }, + "idempotentHint": { + "description": "If true, calling the tool repeatedly with the same arguments\nwill have no additional effect on the its environment.\n\n(This property is meaningful only when `readOnlyHint == false`)\n\nDefault: false.", + "type": [ + "boolean", + "null" + ] + }, + "openWorldHint": { + "description": "If true, this tool may interact with an \"open world\" of external\nentities. If false, the tool's domain of interaction is closed.\nFor example, the world of a web search tool is open, whereas that\nof a memory tool is not.\n\nDefault: true", + "type": [ + "boolean", + "null" + ] + } + } + }, + "ToolExecution": { + "description": "Execution-related configuration for a tool.\n\nThis struct contains settings that control how a tool should be executed,\nincluding task support configuration.", + "type": "object", + "properties": { + "taskSupport": { + "description": "Indicates whether this tool supports task-based invocation.\n\nWhen not present or set to `Forbidden`, clients MUST NOT invoke this tool as a task.\nWhen set to `Optional`, clients MAY invoke this tool as a task or normal call.\nWhen set to `Required`, clients MUST invoke this tool as a task.", + "anyOf": [ + { + "$ref": "#/$defs/TaskSupport" + }, + { + "type": "null" + } + ] + } + } + }, + "TaskSupport": { + "description": "Per-tool task support mode as defined in the MCP specification.\n\nThis enum indicates whether a tool supports task-based invocation,\nallowing clients to know how to properly call the tool.\n\nSee [Tool-Level Negotiation](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks#tool-level-negotiation).", + "oneOf": [ + { + "description": "Clients MUST NOT invoke this tool as a task (default behavior).", + "type": "string", + "const": "forbidden" + }, + { + "description": "Clients MAY invoke this tool as either a task or a normal call.", + "type": "string", + "const": "optional" + }, + { + "description": "Clients MUST invoke this tool as a task.", + "type": "string", + "const": "required" + } + ] + }, + "Icon": { + "description": "A URL pointing to an icon resource or a base64-encoded data URI.\n\nClients that support rendering icons MUST support at least the following MIME types:\n- image/png - PNG images (safe, universal compatibility)\n- image/jpeg (and image/jpg) - JPEG images (safe, universal compatibility)\n\nClients that support rendering icons SHOULD also support:\n- image/svg+xml - SVG images (scalable but requires security precautions)\n- image/webp - WebP images (modern, efficient format)", + "type": "object", + "properties": { + "src": { + "description": "A standard URI pointing to an icon resource", + "type": "string" + }, + "mimeType": { + "description": "Optional override if the server's MIME type is missing or generic", + "type": [ + "string", + "null" + ] + }, + "sizes": { + "description": "Size specification, each string should be in WxH format (e.g., `\\\"48x48\\\"`, `\\\"96x96\\\"`) or `\\\"any\\\"` for scalable formats like SVG", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "theme": { + "description": "Optional specifier for the theme this icon is designed for\nIf not provided, the client should assume the icon can be used with any theme.", + "anyOf": [ + { + "$ref": "#/$defs/IconTheme" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "src" + ] + }, + "IconTheme": { + "description": "Icon themes supported by the MCP specification", + "oneOf": [ + { + "description": "Indicates the icon is designed to be used with a light background", + "type": "string", + "const": "light" + }, + { + "description": "Indicates the icon is designed to be used with a dark background", + "type": "string", + "const": "dark" + } + ] + }, + "SlashCommandMapping": { + "type": "object", + "properties": { + "command": { + "type": "string" + }, + "recipe_path": { + "type": "string" + } + }, + "required": [ + "command", + "recipe_path" + ] + } + } +} diff --git a/crates/goose/src/agents/extension.rs b/crates/goose/src/agents/extension.rs index 51349e0115..f8c3d8223e 100644 --- a/crates/goose/src/agents/extension.rs +++ b/crates/goose/src/agents/extension.rs @@ -7,6 +7,7 @@ use crate::config::Config; use rmcp::model::Tool; use rmcp::service::ClientInitializeError; use rmcp::ServiceError as ClientError; +use schemars::JsonSchema; use serde::Deserializer; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -58,7 +59,7 @@ pub enum ExtensionError { pub type ExtensionResult = Result; -#[derive(Debug, Clone, Deserialize, Serialize, Default, ToSchema, PartialEq)] +#[derive(Debug, Clone, Deserialize, Serialize, Default, ToSchema, PartialEq, JsonSchema)] pub struct Envs { /// A map of environment variables to set, e.g. API_KEY -> some_secret, HOST -> host #[serde(default)] @@ -148,7 +149,7 @@ impl Envs { } /// Represents the different types of MCP extensions that can be added to the manager -#[derive(Debug, Clone, Deserialize, Serialize, ToSchema, PartialEq)] +#[derive(Debug, Clone, Deserialize, Serialize, ToSchema, PartialEq, JsonSchema)] #[serde(tag = "type")] pub enum ExtensionConfig { /// SSE transport is no longer supported - kept only for config file compatibility diff --git a/crates/goose/src/bin/generate_config_schema.rs b/crates/goose/src/bin/generate_config_schema.rs new file mode 100644 index 0000000000..d82ae5942a --- /dev/null +++ b/crates/goose/src/bin/generate_config_schema.rs @@ -0,0 +1,28 @@ +use goose::config::GooseConfigSchema; +use std::env; +use std::fs; +use std::path::PathBuf; + +fn main() { + let schema = schemars::schema_for!(GooseConfigSchema); + let json_str = serde_json::to_string_pretty(&schema).expect("failed to serialize schema"); + + let package_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let schema_path = PathBuf::from(&package_dir).join("config.schema.json"); + + let check_mode = env::args().any(|arg| arg == "--check"); + + if check_mode { + let existing = fs::read_to_string(&schema_path).unwrap_or_default(); + if existing.trim() != json_str.trim() { + eprintln!( + "Config schema is out of date. Run `cargo run -p goose --bin generate-config-schema` to regenerate." + ); + std::process::exit(1); + } + eprintln!("Config schema is up to date."); + } else { + fs::write(&schema_path, format!("{json_str}\n")).expect("failed to write schema file"); + eprintln!("Generated config schema at {}", schema_path.display()); + } +} diff --git a/crates/goose/src/config/extensions.rs b/crates/goose/src/config/extensions.rs index 460a6e93eb..ccecbe9b8a 100644 --- a/crates/goose/src/config/extensions.rs +++ b/crates/goose/src/config/extensions.rs @@ -2,6 +2,7 @@ use super::base::Config; use crate::agents::extension::PLATFORM_EXTENSIONS; use crate::agents::ExtensionConfig; use indexmap::IndexMap; +use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_yaml::Mapping; use tracing::warn; @@ -13,7 +14,7 @@ pub const DEFAULT_EXTENSION_DESCRIPTION: &str = ""; pub const DEFAULT_DISPLAY_NAME: &str = "Developer"; const EXTENSIONS_CONFIG_KEY: &str = "extensions"; -#[derive(Debug, Deserialize, Serialize, Clone, ToSchema)] +#[derive(Debug, Deserialize, Serialize, Clone, ToSchema, JsonSchema)] pub struct ExtensionEntry { pub enabled: bool, #[serde(flatten)] diff --git a/crates/goose/src/config/goose_mode.rs b/crates/goose/src/config/goose_mode.rs index dbfe8af128..65df7693fd 100644 --- a/crates/goose/src/config/goose_mode.rs +++ b/crates/goose/src/config/goose_mode.rs @@ -1,3 +1,4 @@ +use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use strum::{Display, EnumMessage, EnumString, IntoStaticStr, VariantNames}; use utoipa::ToSchema; @@ -18,6 +19,7 @@ use utoipa::ToSchema; IntoStaticStr, VariantNames, ToSchema, + JsonSchema, )] #[serde(rename_all = "snake_case")] #[strum(serialize_all = "snake_case")] diff --git a/crates/goose/src/config/mod.rs b/crates/goose/src/config/mod.rs index cd731c2ae3..ea3b26c85c 100644 --- a/crates/goose/src/config/mod.rs +++ b/crates/goose/src/config/mod.rs @@ -6,6 +6,8 @@ pub mod goose_mode; mod migrations; pub mod paths; pub mod permission; +pub mod schema; +pub use schema::{GooseConfigSchema, GooseConfigUpdate}; pub mod search_path; pub mod signup_nanogpt; pub mod signup_openrouter; diff --git a/crates/goose/src/config/schema.rs b/crates/goose/src/config/schema.rs new file mode 100644 index 0000000000..92730455c8 --- /dev/null +++ b/crates/goose/src/config/schema.rs @@ -0,0 +1,1140 @@ +use std::collections::HashMap; + +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; + +use crate::config::base::{Config, ConfigError}; +use crate::config::extensions::ExtensionEntry; +use crate::config::goose_mode::GooseMode; +use crate::slash_commands::SlashCommandMapping; + +/// JSON Schema representation of Goose's config.yaml. +/// +/// All fields are optional. The standalone JSON Schema (`config.schema.json`) +/// sets `additionalProperties: true` so config.yaml can carry undocumented +/// provider-specific keys as env-var overrides. However, the typed API +/// endpoints only persist fields explicitly declared on this struct — unknown +/// keys in a `PATCH /config/typed` payload are silently dropped by serde. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, ToSchema)] +pub struct GooseConfigSchema { + // === Core Goose Settings === + #[serde(rename = "GOOSE_PROVIDER")] + pub goose_provider: Option, + #[serde(rename = "GOOSE_MODEL")] + pub goose_model: Option, + #[serde(rename = "GOOSE_MODE")] + pub goose_mode: Option, + #[serde(rename = "GOOSE_MAX_TOKENS")] + pub goose_max_tokens: Option, + #[serde(rename = "GOOSE_CONTEXT_LIMIT")] + pub goose_context_limit: Option, + #[serde(rename = "GOOSE_INPUT_LIMIT")] + pub goose_input_limit: Option, + #[serde(rename = "GOOSE_MAX_TURNS")] + pub goose_max_turns: Option, + #[serde(rename = "GOOSE_MAX_ACTIVE_AGENTS")] + pub goose_max_active_agents: Option, + #[serde(rename = "GOOSE_AUTO_COMPACT_THRESHOLD")] + pub goose_auto_compact_threshold: Option, + #[serde(rename = "GOOSE_TOOL_PAIR_SUMMARIZATION")] + pub goose_tool_pair_summarization: Option, + #[serde(rename = "GOOSE_TOOL_CALL_CUTOFF")] + pub goose_tool_call_cutoff: Option, + #[serde(rename = "GOOSE_STREAM_TIMEOUT")] + pub goose_stream_timeout: Option, + #[serde(rename = "GOOSE_SEARCH_PATHS")] + pub goose_search_paths: Option>, + #[serde(rename = "GOOSE_DISABLE_SESSION_NAMING")] + pub goose_disable_session_naming: Option, + #[serde(rename = "GOOSE_DISABLE_KEYRING")] + pub goose_disable_keyring: Option, + #[serde(rename = "GOOSE_TELEMETRY_ENABLED")] + pub goose_telemetry_enabled: Option, + #[serde(rename = "GOOSE_DEFAULT_EXTENSION_TIMEOUT")] + pub goose_default_extension_timeout: Option, + #[serde(rename = "GOOSE_PROMPT_EDITOR")] + pub goose_prompt_editor: Option, + #[serde(rename = "GOOSE_PROMPT_EDITOR_ALWAYS")] + pub goose_prompt_editor_always: Option, + #[serde(rename = "GOOSE_ALLOWLIST")] + pub goose_allowlist: Option, + #[serde(rename = "GOOSE_SYSTEM_PROMPT_FILE_PATH")] + pub goose_system_prompt_file_path: Option, + #[serde(rename = "GOOSE_DEBUG")] + pub goose_debug: Option, + #[serde(rename = "GOOSE_SHOW_FULL_OUTPUT")] + pub goose_show_full_output: Option, + #[serde(rename = "GOOSE_DISABLE_TOOL_CALL_SUMMARY")] + pub goose_disable_tool_call_summary: Option, + #[serde(rename = "GOOSE_STATUS_HOOK")] + pub goose_status_hook: Option, + #[serde(rename = "GOOSE_LOCAL_ENABLE_THINKING")] + pub goose_local_enable_thinking: Option, + #[serde(rename = "GOOSE_DATABRICKS_CLIENT_REQUEST_ID")] + pub goose_databricks_client_request_id: Option, + #[serde(rename = "CONTEXT_FILE_NAMES")] + pub context_file_names: Option>, + #[serde(rename = "EDIT_MODE")] + pub edit_mode: Option, + #[serde(rename = "RANDOM_THINKING_MESSAGES")] + pub random_thinking_messages: Option, + #[serde(rename = "CODE_MODE_TOOL_DISCLOSURE")] + pub code_mode_tool_disclosure: Option, + + // === mTLS Settings === + #[serde(rename = "GOOSE_CLIENT_CERT_PATH")] + pub goose_client_cert_path: Option, + #[serde(rename = "GOOSE_CLIENT_KEY_PATH")] + pub goose_client_key_path: Option, + #[serde(rename = "GOOSE_CA_CERT_PATH")] + pub goose_ca_cert_path: Option, + + // === Planner & Subagent Settings === + #[serde(rename = "GOOSE_PLANNER_PROVIDER")] + pub goose_planner_provider: Option, + #[serde(rename = "GOOSE_PLANNER_MODEL")] + pub goose_planner_model: Option, + #[serde(rename = "GOOSE_SUBAGENT_PROVIDER")] + pub goose_subagent_provider: Option, + #[serde(rename = "GOOSE_SUBAGENT_MODEL")] + pub goose_subagent_model: Option, + #[serde(rename = "GOOSE_SUBAGENT_MAX_TURNS")] + pub goose_subagent_max_turns: Option, + #[serde(rename = "GOOSE_MAX_BACKGROUND_TASKS")] + pub goose_max_background_tasks: Option, + + // === Recipe Settings === + #[serde(rename = "GOOSE_RECIPE_GITHUB_REPO")] + pub goose_recipe_github_repo: Option, + #[serde(rename = "GOOSE_RECIPE_RETRY_TIMEOUT_SECONDS")] + pub goose_recipe_retry_timeout_seconds: Option, + #[serde(rename = "GOOSE_RECIPE_ON_FAILURE_TIMEOUT_SECONDS")] + pub goose_recipe_on_failure_timeout_seconds: Option, + + // === CLI Settings === + #[serde(rename = "GOOSE_CLI_MIN_PRIORITY")] + pub goose_cli_min_priority: Option, + #[serde(rename = "GOOSE_CLI_THEME")] + pub goose_cli_theme: Option, + #[serde(rename = "GOOSE_CLI_LIGHT_THEME")] + pub goose_cli_light_theme: Option, + #[serde(rename = "GOOSE_CLI_DARK_THEME")] + pub goose_cli_dark_theme: Option, + #[serde(rename = "GOOSE_CLI_SHOW_COST")] + pub goose_cli_show_cost: Option, + #[serde(rename = "GOOSE_CLI_SHOW_THINKING")] + pub goose_cli_show_thinking: Option, + #[serde(rename = "GOOSE_CLI_NEWLINE_KEY")] + pub goose_cli_newline_key: Option, + + // === AI Agent / Thinking Settings === + #[serde(rename = "CLAUDE_CODE_COMMAND")] + pub claude_code_command: Option, + #[serde(rename = "GEMINI_CLI_COMMAND")] + pub gemini_cli_command: Option, + #[serde(rename = "CURSOR_AGENT_COMMAND")] + pub cursor_agent_command: Option, + #[serde(rename = "CODEX_COMMAND")] + pub codex_command: Option, + #[serde(rename = "CODEX_REASONING_EFFORT")] + pub codex_reasoning_effort: Option, + #[serde(rename = "CODEX_ENABLE_SKILLS")] + pub codex_enable_skills: Option, + #[serde(rename = "CODEX_SKIP_GIT_CHECK")] + pub codex_skip_git_check: Option, + #[serde(rename = "CHATGPT_CODEX_REASONING_EFFORT")] + pub chatgpt_codex_reasoning_effort: Option, + #[serde(rename = "CLAUDE_THINKING_TYPE")] + pub claude_thinking_type: Option, + #[serde(rename = "CLAUDE_THINKING_EFFORT")] + pub claude_thinking_effort: Option, + #[serde(rename = "CLAUDE_THINKING_BUDGET")] + pub claude_thinking_budget: Option, + #[serde(rename = "GEMINI3_THINKING_LEVEL")] + pub gemini3_thinking_level: Option, + #[serde(rename = "GEMINI25_THINKING_BUDGET")] + pub gemini25_thinking_budget: Option, + + // === Security Settings === + #[serde(rename = "SECURITY_PROMPT_ENABLED")] + pub security_prompt_enabled: Option, + #[serde(rename = "SECURITY_PROMPT_THRESHOLD")] + pub security_prompt_threshold: Option, + #[serde(rename = "SECURITY_PROMPT_CLASSIFIER_ENABLED")] + pub security_prompt_classifier_enabled: Option, + #[serde(rename = "SECURITY_PROMPT_CLASSIFIER_MODEL")] + pub security_prompt_classifier_model: Option, + #[serde(rename = "SECURITY_PROMPT_CLASSIFIER_ENDPOINT")] + pub security_prompt_classifier_endpoint: Option, + #[serde(rename = "SECURITY_COMMAND_CLASSIFIER_ENABLED")] + pub security_command_classifier_enabled: Option, + + // === Provider Settings === + #[serde(rename = "OPENAI_HOST")] + pub openai_host: Option, + #[serde(rename = "OPENAI_BASE_URL")] + pub openai_base_url: Option, + #[serde(rename = "OPENAI_BASE_PATH")] + pub openai_base_path: Option, + #[serde(rename = "OPENAI_ORGANIZATION")] + pub openai_organization: Option, + #[serde(rename = "OPENAI_PROJECT")] + pub openai_project: Option, + #[serde(rename = "OPENAI_TIMEOUT")] + pub openai_timeout: Option, + #[serde(rename = "ANTHROPIC_HOST")] + pub anthropic_host: Option, + #[serde(rename = "OLLAMA_HOST")] + pub ollama_host: Option, + #[serde(rename = "OLLAMA_TIMEOUT")] + pub ollama_timeout: Option, + #[serde(rename = "OLLAMA_STREAM_TIMEOUT")] + pub ollama_stream_timeout: Option, + #[serde(rename = "OLLAMA_STREAM_USAGE")] + pub ollama_stream_usage: Option, + #[serde(rename = "DATABRICKS_HOST")] + pub databricks_host: Option, + #[serde(rename = "DATABRICKS_MAX_RETRIES")] + pub databricks_max_retries: Option, + #[serde(rename = "DATABRICKS_INITIAL_RETRY_INTERVAL_MS")] + pub databricks_initial_retry_interval_ms: Option, + #[serde(rename = "DATABRICKS_BACKOFF_MULTIPLIER")] + pub databricks_backoff_multiplier: Option, + #[serde(rename = "DATABRICKS_MAX_RETRY_INTERVAL_MS")] + pub databricks_max_retry_interval_ms: Option, + #[serde(rename = "AZURE_OPENAI_ENDPOINT")] + pub azure_openai_endpoint: Option, + #[serde(rename = "AZURE_OPENAI_DEPLOYMENT_NAME")] + pub azure_openai_deployment_name: Option, + #[serde(rename = "AZURE_OPENAI_API_VERSION")] + pub azure_openai_api_version: Option, + #[serde(rename = "GOOGLE_HOST")] + pub google_host: Option, + #[serde(rename = "GCP_PROJECT_ID")] + pub gcp_project_id: Option, + #[serde(rename = "GCP_LOCATION")] + pub gcp_location: Option, + #[serde(rename = "GCP_MAX_RETRIES")] + pub gcp_max_retries: Option, + #[serde(rename = "GCP_INITIAL_RETRY_INTERVAL_MS")] + pub gcp_initial_retry_interval_ms: Option, + #[serde(rename = "GCP_BACKOFF_MULTIPLIER")] + pub gcp_backoff_multiplier: Option, + #[serde(rename = "GCP_MAX_RETRY_INTERVAL_MS")] + pub gcp_max_retry_interval_ms: Option, + #[serde(rename = "AWS_REGION")] + pub aws_region: Option, + #[serde(rename = "AWS_PROFILE")] + pub aws_profile: Option, + #[serde(rename = "BEDROCK_MAX_RETRIES")] + pub bedrock_max_retries: Option, + #[serde(rename = "BEDROCK_INITIAL_RETRY_INTERVAL_MS")] + pub bedrock_initial_retry_interval_ms: Option, + #[serde(rename = "BEDROCK_BACKOFF_MULTIPLIER")] + pub bedrock_backoff_multiplier: Option, + #[serde(rename = "BEDROCK_MAX_RETRY_INTERVAL_MS")] + pub bedrock_max_retry_interval_ms: Option, + #[serde(rename = "BEDROCK_ENABLE_CACHING")] + pub bedrock_enable_caching: Option, + #[serde(rename = "SAGEMAKER_ENDPOINT_NAME")] + pub sagemaker_endpoint_name: Option, + #[serde(rename = "LITELLM_HOST")] + pub litellm_host: Option, + #[serde(rename = "LITELLM_BASE_PATH")] + pub litellm_base_path: Option, + #[serde(rename = "LITELLM_TIMEOUT")] + pub litellm_timeout: Option, + #[serde(rename = "SNOWFLAKE_HOST")] + pub snowflake_host: Option, + #[serde(rename = "GITHUB_COPILOT_HOST")] + pub github_copilot_host: Option, + #[serde(rename = "GITHUB_COPILOT_CLIENT_ID")] + pub github_copilot_client_id: Option, + #[serde(rename = "GITHUB_COPILOT_TOKEN_URL")] + pub github_copilot_token_url: Option, + #[serde(rename = "XAI_HOST")] + pub xai_host: Option, + #[serde(rename = "OPENROUTER_HOST")] + pub openrouter_host: Option, + #[serde(rename = "VENICE_HOST")] + pub venice_host: Option, + #[serde(rename = "VENICE_BASE_PATH")] + pub venice_base_path: Option, + #[serde(rename = "VENICE_MODELS_PATH")] + pub venice_models_path: Option, + #[serde(rename = "TETRATE_HOST")] + pub tetrate_host: Option, + #[serde(rename = "AVIAN_HOST")] + pub avian_host: Option, + + // === Observability Settings (lowercase keys) === + pub otel_exporter_otlp_endpoint: Option, + pub otel_exporter_otlp_timeout: Option, + + // === Tunnel Settings (lowercase keys) === + pub tunnel_auto_start: Option, + + // Category B: not in ALL_KEYS — these use dedicated module helpers, not config_value! macro + pub extensions: Option>, + pub slash_commands: Option>, + pub experiments: Option>, +} + +impl GooseConfigSchema { + /// All user-facing config keys that get `config_value!` typed accessors. + /// Category B keys (extensions, slash_commands, experiments) are in the struct + /// for schema generation but NOT here — they use dedicated module helpers. + pub const ALL_KEYS: &[&str] = &[ + // Core Goose Settings + "GOOSE_PROVIDER", + "GOOSE_MODEL", + "GOOSE_MODE", + "GOOSE_MAX_TOKENS", + "GOOSE_CONTEXT_LIMIT", + "GOOSE_INPUT_LIMIT", + "GOOSE_MAX_TURNS", + "GOOSE_MAX_ACTIVE_AGENTS", + "GOOSE_AUTO_COMPACT_THRESHOLD", + "GOOSE_TOOL_PAIR_SUMMARIZATION", + "GOOSE_TOOL_CALL_CUTOFF", + "GOOSE_STREAM_TIMEOUT", + "GOOSE_SEARCH_PATHS", + "GOOSE_DISABLE_SESSION_NAMING", + "GOOSE_DISABLE_KEYRING", + "GOOSE_TELEMETRY_ENABLED", + "GOOSE_DEFAULT_EXTENSION_TIMEOUT", + "GOOSE_PROMPT_EDITOR", + "GOOSE_PROMPT_EDITOR_ALWAYS", + "GOOSE_ALLOWLIST", + "GOOSE_SYSTEM_PROMPT_FILE_PATH", + "GOOSE_DEBUG", + "GOOSE_SHOW_FULL_OUTPUT", + "GOOSE_DISABLE_TOOL_CALL_SUMMARY", + "GOOSE_STATUS_HOOK", + "GOOSE_LOCAL_ENABLE_THINKING", + "GOOSE_DATABRICKS_CLIENT_REQUEST_ID", + "CONTEXT_FILE_NAMES", + "EDIT_MODE", + "RANDOM_THINKING_MESSAGES", + "CODE_MODE_TOOL_DISCLOSURE", + // mTLS Settings + "GOOSE_CLIENT_CERT_PATH", + "GOOSE_CLIENT_KEY_PATH", + "GOOSE_CA_CERT_PATH", + // Planner & Subagent Settings + "GOOSE_PLANNER_PROVIDER", + "GOOSE_PLANNER_MODEL", + "GOOSE_SUBAGENT_PROVIDER", + "GOOSE_SUBAGENT_MODEL", + "GOOSE_SUBAGENT_MAX_TURNS", + "GOOSE_MAX_BACKGROUND_TASKS", + // Recipe Settings + "GOOSE_RECIPE_GITHUB_REPO", + "GOOSE_RECIPE_RETRY_TIMEOUT_SECONDS", + "GOOSE_RECIPE_ON_FAILURE_TIMEOUT_SECONDS", + // CLI Settings + "GOOSE_CLI_MIN_PRIORITY", + "GOOSE_CLI_THEME", + "GOOSE_CLI_LIGHT_THEME", + "GOOSE_CLI_DARK_THEME", + "GOOSE_CLI_SHOW_COST", + "GOOSE_CLI_SHOW_THINKING", + "GOOSE_CLI_NEWLINE_KEY", + // AI Agent / Thinking Settings + "CLAUDE_CODE_COMMAND", + "GEMINI_CLI_COMMAND", + "CURSOR_AGENT_COMMAND", + "CODEX_COMMAND", + "CODEX_REASONING_EFFORT", + "CODEX_ENABLE_SKILLS", + "CODEX_SKIP_GIT_CHECK", + "CHATGPT_CODEX_REASONING_EFFORT", + "CLAUDE_THINKING_TYPE", + "CLAUDE_THINKING_EFFORT", + "CLAUDE_THINKING_BUDGET", + "GEMINI3_THINKING_LEVEL", + "GEMINI25_THINKING_BUDGET", + // Security Settings + "SECURITY_PROMPT_ENABLED", + "SECURITY_PROMPT_THRESHOLD", + "SECURITY_PROMPT_CLASSIFIER_ENABLED", + "SECURITY_PROMPT_CLASSIFIER_MODEL", + "SECURITY_PROMPT_CLASSIFIER_ENDPOINT", + "SECURITY_COMMAND_CLASSIFIER_ENABLED", + // Provider Settings + "OPENAI_HOST", + "OPENAI_BASE_URL", + "OPENAI_BASE_PATH", + "OPENAI_ORGANIZATION", + "OPENAI_PROJECT", + "OPENAI_TIMEOUT", + "ANTHROPIC_HOST", + "OLLAMA_HOST", + "OLLAMA_TIMEOUT", + "OLLAMA_STREAM_TIMEOUT", + "OLLAMA_STREAM_USAGE", + "DATABRICKS_HOST", + "DATABRICKS_MAX_RETRIES", + "DATABRICKS_INITIAL_RETRY_INTERVAL_MS", + "DATABRICKS_BACKOFF_MULTIPLIER", + "DATABRICKS_MAX_RETRY_INTERVAL_MS", + "AZURE_OPENAI_ENDPOINT", + "AZURE_OPENAI_DEPLOYMENT_NAME", + "AZURE_OPENAI_API_VERSION", + "GOOGLE_HOST", + "GCP_PROJECT_ID", + "GCP_LOCATION", + "GCP_MAX_RETRIES", + "GCP_INITIAL_RETRY_INTERVAL_MS", + "GCP_BACKOFF_MULTIPLIER", + "GCP_MAX_RETRY_INTERVAL_MS", + "AWS_REGION", + "AWS_PROFILE", + "BEDROCK_MAX_RETRIES", + "BEDROCK_INITIAL_RETRY_INTERVAL_MS", + "BEDROCK_BACKOFF_MULTIPLIER", + "BEDROCK_MAX_RETRY_INTERVAL_MS", + "BEDROCK_ENABLE_CACHING", + "SAGEMAKER_ENDPOINT_NAME", + "LITELLM_HOST", + "LITELLM_BASE_PATH", + "LITELLM_TIMEOUT", + "SNOWFLAKE_HOST", + "GITHUB_COPILOT_HOST", + "GITHUB_COPILOT_CLIENT_ID", + "GITHUB_COPILOT_TOKEN_URL", + "XAI_HOST", + "OPENROUTER_HOST", + "VENICE_HOST", + "VENICE_BASE_PATH", + "VENICE_MODELS_PATH", + "TETRATE_HOST", + "AVIAN_HOST", + // Observability Settings + "otel_exporter_otlp_endpoint", + "otel_exporter_otlp_timeout", + // Tunnel Settings + "tunnel_auto_start", + ]; + + // const fn cannot use == on &str in stable Rust; manual byte comparison required + pub const fn has_key(key: &str) -> bool { + let key_bytes = key.as_bytes(); + let mut i = 0; + while i < Self::ALL_KEYS.len() { + let candidate = Self::ALL_KEYS[i].as_bytes(); + if candidate.len() == key_bytes.len() { + let mut j = 0; + let mut eq = true; + while j < key_bytes.len() { + if candidate[j] != key_bytes[j] { + eq = false; + break; + } + j += 1; + } + if eq { + return true; + } + } + i += 1; + } + false + } + + pub fn from_config(config: &Config) -> Self { + GooseConfigSchema { + goose_provider: config.get_param("GOOSE_PROVIDER").ok(), + goose_model: config.get_param("GOOSE_MODEL").ok(), + goose_mode: config.get_param("GOOSE_MODE").ok(), + goose_max_tokens: config.get_param("GOOSE_MAX_TOKENS").ok(), + goose_context_limit: config.get_param("GOOSE_CONTEXT_LIMIT").ok(), + goose_input_limit: config.get_param("GOOSE_INPUT_LIMIT").ok(), + goose_max_turns: config.get_param("GOOSE_MAX_TURNS").ok(), + goose_max_active_agents: config.get_param("GOOSE_MAX_ACTIVE_AGENTS").ok(), + goose_auto_compact_threshold: config.get_param("GOOSE_AUTO_COMPACT_THRESHOLD").ok(), + goose_tool_pair_summarization: config.get_param("GOOSE_TOOL_PAIR_SUMMARIZATION").ok(), + goose_tool_call_cutoff: config.get_param("GOOSE_TOOL_CALL_CUTOFF").ok(), + goose_stream_timeout: config.get_param("GOOSE_STREAM_TIMEOUT").ok(), + goose_search_paths: config.get_param("GOOSE_SEARCH_PATHS").ok(), + goose_disable_session_naming: config.get_param("GOOSE_DISABLE_SESSION_NAMING").ok(), + goose_disable_keyring: config.get_param("GOOSE_DISABLE_KEYRING").ok(), + goose_telemetry_enabled: config.get_param("GOOSE_TELEMETRY_ENABLED").ok(), + goose_default_extension_timeout: config + .get_param("GOOSE_DEFAULT_EXTENSION_TIMEOUT") + .ok(), + goose_prompt_editor: config.get_param("GOOSE_PROMPT_EDITOR").ok(), + goose_prompt_editor_always: config.get_param("GOOSE_PROMPT_EDITOR_ALWAYS").ok(), + goose_allowlist: config.get_param("GOOSE_ALLOWLIST").ok(), + goose_system_prompt_file_path: config.get_param("GOOSE_SYSTEM_PROMPT_FILE_PATH").ok(), + goose_debug: config.get_param("GOOSE_DEBUG").ok(), + goose_show_full_output: config.get_param("GOOSE_SHOW_FULL_OUTPUT").ok(), + goose_disable_tool_call_summary: config + .get_param("GOOSE_DISABLE_TOOL_CALL_SUMMARY") + .ok(), + goose_status_hook: config.get_param("GOOSE_STATUS_HOOK").ok(), + goose_local_enable_thinking: config.get_param("GOOSE_LOCAL_ENABLE_THINKING").ok(), + goose_databricks_client_request_id: config + .get_param("GOOSE_DATABRICKS_CLIENT_REQUEST_ID") + .ok(), + context_file_names: config.get_param("CONTEXT_FILE_NAMES").ok(), + edit_mode: config.get_param("EDIT_MODE").ok(), + random_thinking_messages: config.get_param("RANDOM_THINKING_MESSAGES").ok(), + code_mode_tool_disclosure: config.get_param("CODE_MODE_TOOL_DISCLOSURE").ok(), + goose_client_cert_path: config.get_param("GOOSE_CLIENT_CERT_PATH").ok(), + goose_client_key_path: config.get_param("GOOSE_CLIENT_KEY_PATH").ok(), + goose_ca_cert_path: config.get_param("GOOSE_CA_CERT_PATH").ok(), + goose_planner_provider: config.get_param("GOOSE_PLANNER_PROVIDER").ok(), + goose_planner_model: config.get_param("GOOSE_PLANNER_MODEL").ok(), + goose_subagent_provider: config.get_param("GOOSE_SUBAGENT_PROVIDER").ok(), + goose_subagent_model: config.get_param("GOOSE_SUBAGENT_MODEL").ok(), + goose_subagent_max_turns: config.get_param("GOOSE_SUBAGENT_MAX_TURNS").ok(), + goose_max_background_tasks: config.get_param("GOOSE_MAX_BACKGROUND_TASKS").ok(), + goose_recipe_github_repo: config.get_param("GOOSE_RECIPE_GITHUB_REPO").ok(), + goose_recipe_retry_timeout_seconds: config + .get_param("GOOSE_RECIPE_RETRY_TIMEOUT_SECONDS") + .ok(), + goose_recipe_on_failure_timeout_seconds: config + .get_param("GOOSE_RECIPE_ON_FAILURE_TIMEOUT_SECONDS") + .ok(), + goose_cli_min_priority: config.get_param("GOOSE_CLI_MIN_PRIORITY").ok(), + goose_cli_theme: config.get_param("GOOSE_CLI_THEME").ok(), + goose_cli_light_theme: config.get_param("GOOSE_CLI_LIGHT_THEME").ok(), + goose_cli_dark_theme: config.get_param("GOOSE_CLI_DARK_THEME").ok(), + goose_cli_show_cost: config.get_param("GOOSE_CLI_SHOW_COST").ok(), + goose_cli_show_thinking: config.get_param("GOOSE_CLI_SHOW_THINKING").ok(), + goose_cli_newline_key: config.get_param("GOOSE_CLI_NEWLINE_KEY").ok(), + claude_code_command: config.get_param("CLAUDE_CODE_COMMAND").ok(), + gemini_cli_command: config.get_param("GEMINI_CLI_COMMAND").ok(), + cursor_agent_command: config.get_param("CURSOR_AGENT_COMMAND").ok(), + codex_command: config.get_param("CODEX_COMMAND").ok(), + codex_reasoning_effort: config.get_param("CODEX_REASONING_EFFORT").ok(), + codex_enable_skills: config.get_param("CODEX_ENABLE_SKILLS").ok(), + codex_skip_git_check: config.get_param("CODEX_SKIP_GIT_CHECK").ok(), + chatgpt_codex_reasoning_effort: config.get_param("CHATGPT_CODEX_REASONING_EFFORT").ok(), + claude_thinking_type: config.get_param("CLAUDE_THINKING_TYPE").ok(), + claude_thinking_effort: config.get_param("CLAUDE_THINKING_EFFORT").ok(), + claude_thinking_budget: config.get_param("CLAUDE_THINKING_BUDGET").ok(), + gemini3_thinking_level: config.get_param("GEMINI3_THINKING_LEVEL").ok(), + gemini25_thinking_budget: config.get_param("GEMINI25_THINKING_BUDGET").ok(), + security_prompt_enabled: config.get_param("SECURITY_PROMPT_ENABLED").ok(), + security_prompt_threshold: config.get_param("SECURITY_PROMPT_THRESHOLD").ok(), + security_prompt_classifier_enabled: config + .get_param("SECURITY_PROMPT_CLASSIFIER_ENABLED") + .ok(), + security_prompt_classifier_model: config + .get_param("SECURITY_PROMPT_CLASSIFIER_MODEL") + .ok(), + security_prompt_classifier_endpoint: config + .get_param("SECURITY_PROMPT_CLASSIFIER_ENDPOINT") + .ok(), + security_command_classifier_enabled: config + .get_param("SECURITY_COMMAND_CLASSIFIER_ENABLED") + .ok(), + openai_host: config.get_param("OPENAI_HOST").ok(), + openai_base_url: config.get_param("OPENAI_BASE_URL").ok(), + openai_base_path: config.get_param("OPENAI_BASE_PATH").ok(), + openai_organization: config.get_param("OPENAI_ORGANIZATION").ok(), + openai_project: config.get_param("OPENAI_PROJECT").ok(), + openai_timeout: config.get_param("OPENAI_TIMEOUT").ok(), + anthropic_host: config.get_param("ANTHROPIC_HOST").ok(), + ollama_host: config.get_param("OLLAMA_HOST").ok(), + ollama_timeout: config.get_param("OLLAMA_TIMEOUT").ok(), + ollama_stream_timeout: config.get_param("OLLAMA_STREAM_TIMEOUT").ok(), + ollama_stream_usage: config.get_param("OLLAMA_STREAM_USAGE").ok(), + databricks_host: config.get_param("DATABRICKS_HOST").ok(), + databricks_max_retries: config.get_param("DATABRICKS_MAX_RETRIES").ok(), + databricks_initial_retry_interval_ms: config + .get_param("DATABRICKS_INITIAL_RETRY_INTERVAL_MS") + .ok(), + databricks_backoff_multiplier: config.get_param("DATABRICKS_BACKOFF_MULTIPLIER").ok(), + databricks_max_retry_interval_ms: config + .get_param("DATABRICKS_MAX_RETRY_INTERVAL_MS") + .ok(), + azure_openai_endpoint: config.get_param("AZURE_OPENAI_ENDPOINT").ok(), + azure_openai_deployment_name: config.get_param("AZURE_OPENAI_DEPLOYMENT_NAME").ok(), + azure_openai_api_version: config.get_param("AZURE_OPENAI_API_VERSION").ok(), + google_host: config.get_param("GOOGLE_HOST").ok(), + gcp_project_id: config.get_param("GCP_PROJECT_ID").ok(), + gcp_location: config.get_param("GCP_LOCATION").ok(), + gcp_max_retries: config.get_param("GCP_MAX_RETRIES").ok(), + gcp_initial_retry_interval_ms: config.get_param("GCP_INITIAL_RETRY_INTERVAL_MS").ok(), + gcp_backoff_multiplier: config.get_param("GCP_BACKOFF_MULTIPLIER").ok(), + gcp_max_retry_interval_ms: config.get_param("GCP_MAX_RETRY_INTERVAL_MS").ok(), + aws_region: config.get_param("AWS_REGION").ok(), + aws_profile: config.get_param("AWS_PROFILE").ok(), + bedrock_max_retries: config.get_param("BEDROCK_MAX_RETRIES").ok(), + bedrock_initial_retry_interval_ms: config + .get_param("BEDROCK_INITIAL_RETRY_INTERVAL_MS") + .ok(), + bedrock_backoff_multiplier: config.get_param("BEDROCK_BACKOFF_MULTIPLIER").ok(), + bedrock_max_retry_interval_ms: config.get_param("BEDROCK_MAX_RETRY_INTERVAL_MS").ok(), + bedrock_enable_caching: config.get_param("BEDROCK_ENABLE_CACHING").ok(), + sagemaker_endpoint_name: config.get_param("SAGEMAKER_ENDPOINT_NAME").ok(), + litellm_host: config.get_param("LITELLM_HOST").ok(), + litellm_base_path: config.get_param("LITELLM_BASE_PATH").ok(), + litellm_timeout: config.get_param("LITELLM_TIMEOUT").ok(), + snowflake_host: config.get_param("SNOWFLAKE_HOST").ok(), + github_copilot_host: config.get_param("GITHUB_COPILOT_HOST").ok(), + github_copilot_client_id: config.get_param("GITHUB_COPILOT_CLIENT_ID").ok(), + github_copilot_token_url: config.get_param("GITHUB_COPILOT_TOKEN_URL").ok(), + xai_host: config.get_param("XAI_HOST").ok(), + openrouter_host: config.get_param("OPENROUTER_HOST").ok(), + venice_host: config.get_param("VENICE_HOST").ok(), + venice_base_path: config.get_param("VENICE_BASE_PATH").ok(), + venice_models_path: config.get_param("VENICE_MODELS_PATH").ok(), + tetrate_host: config.get_param("TETRATE_HOST").ok(), + avian_host: config.get_param("AVIAN_HOST").ok(), + otel_exporter_otlp_endpoint: config.get_param("otel_exporter_otlp_endpoint").ok(), + otel_exporter_otlp_timeout: config.get_param("otel_exporter_otlp_timeout").ok(), + tunnel_auto_start: config.get_param("tunnel_auto_start").ok(), + extensions: config.get_param("extensions").ok(), + slash_commands: config.get_param("slash_commands").ok(), + experiments: config.get_param("experiments").ok(), + } + } + + pub fn apply_to_config(&self, config: &Config) -> Result<(), ConfigError> { + let mut updates: Vec<(String, serde_json::Value)> = Vec::new(); + + macro_rules! push_if_some { + ($field:expr, $key:expr) => { + if let Some(ref v) = $field { + match serde_json::to_value(v) { + Ok(json) => updates.push(($key.to_string(), json)), + Err(e) => tracing::warn!("Failed to serialize config key {}: {}", $key, e), + } + } + }; + } + + push_if_some!(self.goose_provider, "GOOSE_PROVIDER"); + push_if_some!(self.goose_model, "GOOSE_MODEL"); + push_if_some!(self.goose_mode, "GOOSE_MODE"); + push_if_some!(self.goose_max_tokens, "GOOSE_MAX_TOKENS"); + push_if_some!(self.goose_context_limit, "GOOSE_CONTEXT_LIMIT"); + push_if_some!(self.goose_input_limit, "GOOSE_INPUT_LIMIT"); + push_if_some!(self.goose_max_turns, "GOOSE_MAX_TURNS"); + push_if_some!(self.goose_max_active_agents, "GOOSE_MAX_ACTIVE_AGENTS"); + push_if_some!( + self.goose_auto_compact_threshold, + "GOOSE_AUTO_COMPACT_THRESHOLD" + ); + push_if_some!( + self.goose_tool_pair_summarization, + "GOOSE_TOOL_PAIR_SUMMARIZATION" + ); + push_if_some!(self.goose_tool_call_cutoff, "GOOSE_TOOL_CALL_CUTOFF"); + push_if_some!(self.goose_stream_timeout, "GOOSE_STREAM_TIMEOUT"); + push_if_some!(self.goose_search_paths, "GOOSE_SEARCH_PATHS"); + push_if_some!( + self.goose_disable_session_naming, + "GOOSE_DISABLE_SESSION_NAMING" + ); + push_if_some!(self.goose_disable_keyring, "GOOSE_DISABLE_KEYRING"); + push_if_some!(self.goose_telemetry_enabled, "GOOSE_TELEMETRY_ENABLED"); + push_if_some!( + self.goose_default_extension_timeout, + "GOOSE_DEFAULT_EXTENSION_TIMEOUT" + ); + push_if_some!(self.goose_prompt_editor, "GOOSE_PROMPT_EDITOR"); + push_if_some!( + self.goose_prompt_editor_always, + "GOOSE_PROMPT_EDITOR_ALWAYS" + ); + push_if_some!(self.goose_allowlist, "GOOSE_ALLOWLIST"); + push_if_some!( + self.goose_system_prompt_file_path, + "GOOSE_SYSTEM_PROMPT_FILE_PATH" + ); + push_if_some!(self.goose_debug, "GOOSE_DEBUG"); + push_if_some!(self.goose_show_full_output, "GOOSE_SHOW_FULL_OUTPUT"); + push_if_some!( + self.goose_disable_tool_call_summary, + "GOOSE_DISABLE_TOOL_CALL_SUMMARY" + ); + push_if_some!(self.goose_status_hook, "GOOSE_STATUS_HOOK"); + push_if_some!( + self.goose_local_enable_thinking, + "GOOSE_LOCAL_ENABLE_THINKING" + ); + push_if_some!( + self.goose_databricks_client_request_id, + "GOOSE_DATABRICKS_CLIENT_REQUEST_ID" + ); + push_if_some!(self.context_file_names, "CONTEXT_FILE_NAMES"); + push_if_some!(self.edit_mode, "EDIT_MODE"); + push_if_some!(self.random_thinking_messages, "RANDOM_THINKING_MESSAGES"); + push_if_some!(self.code_mode_tool_disclosure, "CODE_MODE_TOOL_DISCLOSURE"); + push_if_some!(self.goose_client_cert_path, "GOOSE_CLIENT_CERT_PATH"); + push_if_some!(self.goose_client_key_path, "GOOSE_CLIENT_KEY_PATH"); + push_if_some!(self.goose_ca_cert_path, "GOOSE_CA_CERT_PATH"); + push_if_some!(self.goose_planner_provider, "GOOSE_PLANNER_PROVIDER"); + push_if_some!(self.goose_planner_model, "GOOSE_PLANNER_MODEL"); + push_if_some!(self.goose_subagent_provider, "GOOSE_SUBAGENT_PROVIDER"); + push_if_some!(self.goose_subagent_model, "GOOSE_SUBAGENT_MODEL"); + push_if_some!(self.goose_subagent_max_turns, "GOOSE_SUBAGENT_MAX_TURNS"); + push_if_some!( + self.goose_max_background_tasks, + "GOOSE_MAX_BACKGROUND_TASKS" + ); + push_if_some!(self.goose_recipe_github_repo, "GOOSE_RECIPE_GITHUB_REPO"); + push_if_some!( + self.goose_recipe_retry_timeout_seconds, + "GOOSE_RECIPE_RETRY_TIMEOUT_SECONDS" + ); + push_if_some!( + self.goose_recipe_on_failure_timeout_seconds, + "GOOSE_RECIPE_ON_FAILURE_TIMEOUT_SECONDS" + ); + push_if_some!(self.goose_cli_min_priority, "GOOSE_CLI_MIN_PRIORITY"); + push_if_some!(self.goose_cli_theme, "GOOSE_CLI_THEME"); + push_if_some!(self.goose_cli_light_theme, "GOOSE_CLI_LIGHT_THEME"); + push_if_some!(self.goose_cli_dark_theme, "GOOSE_CLI_DARK_THEME"); + push_if_some!(self.goose_cli_show_cost, "GOOSE_CLI_SHOW_COST"); + push_if_some!(self.goose_cli_show_thinking, "GOOSE_CLI_SHOW_THINKING"); + push_if_some!(self.goose_cli_newline_key, "GOOSE_CLI_NEWLINE_KEY"); + push_if_some!(self.claude_code_command, "CLAUDE_CODE_COMMAND"); + push_if_some!(self.gemini_cli_command, "GEMINI_CLI_COMMAND"); + push_if_some!(self.cursor_agent_command, "CURSOR_AGENT_COMMAND"); + push_if_some!(self.codex_command, "CODEX_COMMAND"); + push_if_some!(self.codex_reasoning_effort, "CODEX_REASONING_EFFORT"); + push_if_some!(self.codex_enable_skills, "CODEX_ENABLE_SKILLS"); + push_if_some!(self.codex_skip_git_check, "CODEX_SKIP_GIT_CHECK"); + push_if_some!( + self.chatgpt_codex_reasoning_effort, + "CHATGPT_CODEX_REASONING_EFFORT" + ); + push_if_some!(self.claude_thinking_type, "CLAUDE_THINKING_TYPE"); + push_if_some!(self.claude_thinking_effort, "CLAUDE_THINKING_EFFORT"); + push_if_some!(self.claude_thinking_budget, "CLAUDE_THINKING_BUDGET"); + push_if_some!(self.gemini3_thinking_level, "GEMINI3_THINKING_LEVEL"); + push_if_some!(self.gemini25_thinking_budget, "GEMINI25_THINKING_BUDGET"); + push_if_some!(self.security_prompt_enabled, "SECURITY_PROMPT_ENABLED"); + push_if_some!(self.security_prompt_threshold, "SECURITY_PROMPT_THRESHOLD"); + push_if_some!( + self.security_prompt_classifier_enabled, + "SECURITY_PROMPT_CLASSIFIER_ENABLED" + ); + push_if_some!( + self.security_prompt_classifier_model, + "SECURITY_PROMPT_CLASSIFIER_MODEL" + ); + push_if_some!( + self.security_prompt_classifier_endpoint, + "SECURITY_PROMPT_CLASSIFIER_ENDPOINT" + ); + push_if_some!( + self.security_command_classifier_enabled, + "SECURITY_COMMAND_CLASSIFIER_ENABLED" + ); + push_if_some!(self.openai_host, "OPENAI_HOST"); + push_if_some!(self.openai_base_url, "OPENAI_BASE_URL"); + push_if_some!(self.openai_base_path, "OPENAI_BASE_PATH"); + push_if_some!(self.openai_organization, "OPENAI_ORGANIZATION"); + push_if_some!(self.openai_project, "OPENAI_PROJECT"); + push_if_some!(self.openai_timeout, "OPENAI_TIMEOUT"); + push_if_some!(self.anthropic_host, "ANTHROPIC_HOST"); + push_if_some!(self.ollama_host, "OLLAMA_HOST"); + push_if_some!(self.ollama_timeout, "OLLAMA_TIMEOUT"); + push_if_some!(self.ollama_stream_timeout, "OLLAMA_STREAM_TIMEOUT"); + push_if_some!(self.ollama_stream_usage, "OLLAMA_STREAM_USAGE"); + push_if_some!(self.databricks_host, "DATABRICKS_HOST"); + push_if_some!(self.databricks_max_retries, "DATABRICKS_MAX_RETRIES"); + push_if_some!( + self.databricks_initial_retry_interval_ms, + "DATABRICKS_INITIAL_RETRY_INTERVAL_MS" + ); + push_if_some!( + self.databricks_backoff_multiplier, + "DATABRICKS_BACKOFF_MULTIPLIER" + ); + push_if_some!( + self.databricks_max_retry_interval_ms, + "DATABRICKS_MAX_RETRY_INTERVAL_MS" + ); + push_if_some!(self.azure_openai_endpoint, "AZURE_OPENAI_ENDPOINT"); + push_if_some!( + self.azure_openai_deployment_name, + "AZURE_OPENAI_DEPLOYMENT_NAME" + ); + push_if_some!(self.azure_openai_api_version, "AZURE_OPENAI_API_VERSION"); + push_if_some!(self.google_host, "GOOGLE_HOST"); + push_if_some!(self.gcp_project_id, "GCP_PROJECT_ID"); + push_if_some!(self.gcp_location, "GCP_LOCATION"); + push_if_some!(self.gcp_max_retries, "GCP_MAX_RETRIES"); + push_if_some!( + self.gcp_initial_retry_interval_ms, + "GCP_INITIAL_RETRY_INTERVAL_MS" + ); + push_if_some!(self.gcp_backoff_multiplier, "GCP_BACKOFF_MULTIPLIER"); + push_if_some!(self.gcp_max_retry_interval_ms, "GCP_MAX_RETRY_INTERVAL_MS"); + push_if_some!(self.aws_region, "AWS_REGION"); + push_if_some!(self.aws_profile, "AWS_PROFILE"); + push_if_some!(self.bedrock_max_retries, "BEDROCK_MAX_RETRIES"); + push_if_some!( + self.bedrock_initial_retry_interval_ms, + "BEDROCK_INITIAL_RETRY_INTERVAL_MS" + ); + push_if_some!( + self.bedrock_backoff_multiplier, + "BEDROCK_BACKOFF_MULTIPLIER" + ); + push_if_some!( + self.bedrock_max_retry_interval_ms, + "BEDROCK_MAX_RETRY_INTERVAL_MS" + ); + push_if_some!(self.bedrock_enable_caching, "BEDROCK_ENABLE_CACHING"); + push_if_some!(self.sagemaker_endpoint_name, "SAGEMAKER_ENDPOINT_NAME"); + push_if_some!(self.litellm_host, "LITELLM_HOST"); + push_if_some!(self.litellm_base_path, "LITELLM_BASE_PATH"); + push_if_some!(self.litellm_timeout, "LITELLM_TIMEOUT"); + push_if_some!(self.snowflake_host, "SNOWFLAKE_HOST"); + push_if_some!(self.github_copilot_host, "GITHUB_COPILOT_HOST"); + push_if_some!(self.github_copilot_client_id, "GITHUB_COPILOT_CLIENT_ID"); + push_if_some!(self.github_copilot_token_url, "GITHUB_COPILOT_TOKEN_URL"); + push_if_some!(self.xai_host, "XAI_HOST"); + push_if_some!(self.openrouter_host, "OPENROUTER_HOST"); + push_if_some!(self.venice_host, "VENICE_HOST"); + push_if_some!(self.venice_base_path, "VENICE_BASE_PATH"); + push_if_some!(self.venice_models_path, "VENICE_MODELS_PATH"); + push_if_some!(self.tetrate_host, "TETRATE_HOST"); + push_if_some!(self.avian_host, "AVIAN_HOST"); + push_if_some!( + self.otel_exporter_otlp_endpoint, + "otel_exporter_otlp_endpoint" + ); + push_if_some!( + self.otel_exporter_otlp_timeout, + "otel_exporter_otlp_timeout" + ); + push_if_some!(self.tunnel_auto_start, "tunnel_auto_start"); + push_if_some!(self.extensions, "extensions"); + push_if_some!(self.slash_commands, "slash_commands"); + push_if_some!(self.experiments, "experiments"); + + config.set_param_values(&updates) + } +} + +/// Config update payload for `PATCH /config/typed`. +/// +/// Embeds all non-secret fields from [`GooseConfigSchema`] via `#[serde(flatten)]`, +/// plus provider API key fields that route to the system keyring. +/// +/// **Sparse patch semantics:** Only send fields you want to change. Fields set to +/// `null` (or omitted) are left unchanged — serde cannot distinguish the two cases. +/// To delete a key, use `POST /config/remove`. Nested objects (`extensions`, +/// `slash_commands`, `experiments`) use whole-value replacement, not deep merge. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, ToSchema)] +pub struct GooseConfigUpdate { + #[serde(flatten)] + pub config: GooseConfigSchema, + + // === Provider API Keys (secrets, stored in keyring) === + #[serde(rename = "OPENAI_API_KEY")] + pub openai_api_key: Option, + #[serde(rename = "ANTHROPIC_API_KEY")] + pub anthropic_api_key: Option, + #[serde(rename = "GOOGLE_API_KEY")] + pub google_api_key: Option, + #[serde(rename = "DATABRICKS_TOKEN")] + pub databricks_token: Option, + #[serde(rename = "AZURE_OPENAI_API_KEY")] + pub azure_openai_api_key: Option, + #[serde(rename = "OPENROUTER_API_KEY")] + pub openrouter_api_key: Option, + #[serde(rename = "XAI_API_KEY")] + pub xai_api_key: Option, + #[serde(rename = "AVIAN_API_KEY")] + pub avian_api_key: Option, + #[serde(rename = "VENICE_API_KEY")] + pub venice_api_key: Option, + #[serde(rename = "TETRATE_API_KEY")] + pub tetrate_api_key: Option, + #[serde(rename = "LITELLM_API_KEY")] + pub litellm_api_key: Option, + #[serde(rename = "SNOWFLAKE_TOKEN")] + pub snowflake_token: Option, + #[serde(rename = "GROQ_API_KEY")] + pub groq_api_key: Option, + #[serde(rename = "NANOGPT_API_KEY")] + pub nanogpt_api_key: Option, + #[serde(rename = "LITELLM_CUSTOM_HEADERS")] + pub litellm_custom_headers: Option, +} + +impl GooseConfigUpdate { + pub fn apply_to_config(&self, config: &Config) -> Result<(), ConfigError> { + self.config.apply_to_config(config)?; + + let mut secret_updates: Vec<(String, serde_json::Value)> = Vec::new(); + + macro_rules! push_secret { + ($field:expr, $key:expr) => { + if let Some(ref v) = $field { + match serde_json::to_value(v) { + Ok(json) => secret_updates.push(($key.to_string(), json)), + Err(e) => tracing::warn!("Failed to serialize secret key {}: {}", $key, e), + } + } + }; + } + + push_secret!(self.openai_api_key, "OPENAI_API_KEY"); + push_secret!(self.anthropic_api_key, "ANTHROPIC_API_KEY"); + push_secret!(self.google_api_key, "GOOGLE_API_KEY"); + push_secret!(self.databricks_token, "DATABRICKS_TOKEN"); + push_secret!(self.azure_openai_api_key, "AZURE_OPENAI_API_KEY"); + push_secret!(self.openrouter_api_key, "OPENROUTER_API_KEY"); + push_secret!(self.xai_api_key, "XAI_API_KEY"); + push_secret!(self.avian_api_key, "AVIAN_API_KEY"); + push_secret!(self.venice_api_key, "VENICE_API_KEY"); + push_secret!(self.tetrate_api_key, "TETRATE_API_KEY"); + push_secret!(self.litellm_api_key, "LITELLM_API_KEY"); + push_secret!(self.snowflake_token, "SNOWFLAKE_TOKEN"); + push_secret!(self.groq_api_key, "GROQ_API_KEY"); + push_secret!(self.nanogpt_api_key, "NANOGPT_API_KEY"); + push_secret!(self.litellm_custom_headers, "LITELLM_CUSTOM_HEADERS"); + + config.set_secret_values(&secret_updates) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use schemars::schema_for; + + #[test] + fn all_keys_matches_struct_fields() { + let schema = schema_for!(GooseConfigSchema); + let obj = schema.as_object().expect("schema should be an object"); + let properties = obj + .get("properties") + .and_then(|p| p.as_object()) + .expect("schema should have properties"); + + let schema_keys: std::collections::HashSet<&str> = + properties.keys().map(|k| k.as_str()).collect(); + + let category_b: std::collections::HashSet<&str> = + ["extensions", "slash_commands", "experiments"] + .iter() + .copied() + .collect(); + + // Forward: every ALL_KEYS entry must exist in the struct + for key in GooseConfigSchema::ALL_KEYS { + assert!( + schema_keys.contains(key), + "ALL_KEYS contains '{key}' but GooseConfigSchema has no field with serde(rename = \"{key}\")" + ); + } + + // Reverse: every struct field (except Category B) must be in ALL_KEYS + for key in &schema_keys { + if !category_b.contains(key) { + assert!( + GooseConfigSchema::has_key(key), + "GooseConfigSchema has field '{key}' but it's missing from ALL_KEYS (add it, or mark as Category B)" + ); + } + } + + // Category B keys are in the struct but NOT in ALL_KEYS — that's intentional + for key in &category_b { + assert!( + schema_keys.contains(key), + "Category B key '{key}' should be in the schema struct for IDE autocomplete" + ); + assert!( + !GooseConfigSchema::has_key(key), + "Category B key '{key}' should NOT be in ALL_KEYS" + ); + } + } + + #[test] + fn roundtrip_config_values() { + let config_file = tempfile::NamedTempFile::new().unwrap(); + let secrets_file = tempfile::NamedTempFile::new().unwrap(); + let config = + Config::new_with_file_secrets(config_file.path(), secrets_file.path()).unwrap(); + + config + .set_param_values(&[ + ( + "GOOSE_PROVIDER".to_string(), + serde_json::Value::String("anthropic".to_string()), + ), + ( + "GOOSE_MAX_TOKENS".to_string(), + serde_json::Value::Number(4096.into()), + ), + ("GOOSE_DEBUG".to_string(), serde_json::Value::Bool(true)), + ( + "GOOSE_DISABLE_KEYRING".to_string(), + serde_json::Value::Bool(true), + ), + ( + "SECURITY_PROMPT_THRESHOLD".to_string(), + serde_json::json!(0.75), + ), + ( + "GOOSE_MODE".to_string(), + serde_json::Value::String("auto".to_string()), + ), + ( + "GOOSE_SEARCH_PATHS".to_string(), + serde_json::json!(["/usr/local/bin", "/opt/bin"]), + ), + ( + "GOOSE_CONTEXT_LIMIT".to_string(), + serde_json::Value::Number(128000u64.into()), + ), + ("GOOSE_CLI_MIN_PRIORITY".to_string(), serde_json::json!(0.5)), + ]) + .expect("set_param_values should succeed"); + + let typed = GooseConfigSchema::from_config(&config); + assert_eq!(typed.goose_provider.as_deref(), Some("anthropic")); + assert_eq!(typed.goose_max_tokens, Some(4096)); + assert_eq!(typed.goose_debug, Some(true)); + assert_eq!(typed.goose_disable_keyring, Some(true)); + assert_eq!(typed.security_prompt_threshold, Some(0.75)); + assert_eq!(typed.goose_mode, Some(GooseMode::Auto)); + assert_eq!( + typed.goose_search_paths, + Some(vec!["/usr/local/bin".to_string(), "/opt/bin".to_string()]) + ); + assert_eq!(typed.goose_context_limit, Some(128000)); + + // Roundtrip: apply to a fresh config and verify + let config_file2 = tempfile::NamedTempFile::new().unwrap(); + let secrets_file2 = tempfile::NamedTempFile::new().unwrap(); + let config2 = + Config::new_with_file_secrets(config_file2.path(), secrets_file2.path()).unwrap(); + typed + .apply_to_config(&config2) + .expect("apply_to_config should succeed"); + let typed2 = GooseConfigSchema::from_config(&config2); + assert_eq!(typed2.goose_provider, typed.goose_provider); + assert_eq!(typed2.goose_max_tokens, typed.goose_max_tokens); + assert_eq!(typed2.goose_debug, typed.goose_debug); + assert_eq!(typed2.goose_disable_keyring, typed.goose_disable_keyring); + assert_eq!(typed2.goose_mode, typed.goose_mode); + assert_eq!(typed2.goose_search_paths, typed.goose_search_paths); + assert_eq!(typed2.goose_context_limit, typed.goose_context_limit); + } + + #[test] + fn from_config_and_apply_cover_all_string_keys() { + let config_file = tempfile::NamedTempFile::new().unwrap(); + let secrets_file = tempfile::NamedTempFile::new().unwrap(); + let config = + Config::new_with_file_secrets(config_file.path(), secrets_file.path()).unwrap(); + + // Non-string keys need type-appropriate values; get_param fails if + // the stored value can't deserialize to the field's Rust type. + // Test string-typed keys exhaustively, and spot-check typed keys + // in the roundtrip test above. + let non_string_keys: std::collections::HashSet<&str> = [ + "GOOSE_MODE", + "GOOSE_MAX_TOKENS", + "GOOSE_CONTEXT_LIMIT", + "GOOSE_INPUT_LIMIT", + "GOOSE_MAX_TURNS", + "GOOSE_MAX_ACTIVE_AGENTS", + "GOOSE_AUTO_COMPACT_THRESHOLD", + "GOOSE_TOOL_PAIR_SUMMARIZATION", + "GOOSE_TOOL_CALL_CUTOFF", + "GOOSE_STREAM_TIMEOUT", + "GOOSE_SEARCH_PATHS", + "GOOSE_DISABLE_SESSION_NAMING", + "GOOSE_DISABLE_KEYRING", + "GOOSE_TELEMETRY_ENABLED", + "GOOSE_DEFAULT_EXTENSION_TIMEOUT", + "GOOSE_PROMPT_EDITOR_ALWAYS", + "GOOSE_DEBUG", + "GOOSE_SHOW_FULL_OUTPUT", + "GOOSE_DISABLE_TOOL_CALL_SUMMARY", + "GOOSE_LOCAL_ENABLE_THINKING", + "GOOSE_DATABRICKS_CLIENT_REQUEST_ID", + "RANDOM_THINKING_MESSAGES", + "GOOSE_SUBAGENT_MAX_TURNS", + "GOOSE_MAX_BACKGROUND_TASKS", + "GOOSE_RECIPE_RETRY_TIMEOUT_SECONDS", + "GOOSE_RECIPE_ON_FAILURE_TIMEOUT_SECONDS", + "GOOSE_CLI_MIN_PRIORITY", + "GOOSE_CLI_SHOW_COST", + "GOOSE_CLI_SHOW_THINKING", + "CLAUDE_THINKING_BUDGET", + "GEMINI25_THINKING_BUDGET", + "SECURITY_PROMPT_ENABLED", + "SECURITY_PROMPT_THRESHOLD", + "SECURITY_PROMPT_CLASSIFIER_ENABLED", + "SECURITY_COMMAND_CLASSIFIER_ENABLED", + "OPENAI_TIMEOUT", + "OLLAMA_TIMEOUT", + "OLLAMA_STREAM_TIMEOUT", + "OLLAMA_STREAM_USAGE", + "BEDROCK_MAX_RETRIES", + "BEDROCK_INITIAL_RETRY_INTERVAL_MS", + "BEDROCK_BACKOFF_MULTIPLIER", + "BEDROCK_MAX_RETRY_INTERVAL_MS", + "BEDROCK_ENABLE_CACHING", + "LITELLM_TIMEOUT", + "otel_exporter_otlp_timeout", + "tunnel_auto_start", + "CONTEXT_FILE_NAMES", + ] + .iter() + .copied() + .collect(); + + let string_keys: Vec<&&str> = GooseConfigSchema::ALL_KEYS + .iter() + .filter(|k| !non_string_keys.contains(**k)) + .collect(); + + let sentinel = serde_json::Value::String("__test_sentinel__".to_string()); + let updates: Vec<(String, serde_json::Value)> = string_keys + .iter() + .map(|k| (k.to_string(), sentinel.clone())) + .collect(); + config + .set_param_values(&updates) + .expect("set_param_values should succeed"); + + let typed = GooseConfigSchema::from_config(&config); + let json = serde_json::to_value(&typed).expect("serialize schema"); + let obj = json.as_object().expect("schema should be object"); + + for key in &string_keys { + assert!( + obj.get(**key).is_some_and(|v| !v.is_null()), + "from_config did not populate field for key '{}' — check the from_config() body", + key + ); + } + + let config_file2 = tempfile::NamedTempFile::new().unwrap(); + let secrets_file2 = tempfile::NamedTempFile::new().unwrap(); + let config2 = + Config::new_with_file_secrets(config_file2.path(), secrets_file2.path()).unwrap(); + typed + .apply_to_config(&config2) + .expect("apply_to_config should succeed"); + + for key in &string_keys { + let val: Result = config2.get_param(key); + assert!( + val.is_ok(), + "apply_to_config did not persist key '{}' — check the apply_to_config() body", + key + ); + } + } +} diff --git a/crates/goose/src/slash_commands.rs b/crates/goose/src/slash_commands.rs index 5e7065db00..bb8678b2c1 100644 --- a/crates/goose/src/slash_commands.rs +++ b/crates/goose/src/slash_commands.rs @@ -1,15 +1,17 @@ use std::path::PathBuf; use anyhow::Result; +use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use tracing::warn; +use utoipa::ToSchema; use crate::config::Config; use crate::recipe::Recipe; const SLASH_COMMANDS_CONFIG_KEY: &str = "slash_commands"; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, ToSchema)] pub struct SlashCommandMapping { pub command: String, pub recipe_path: String, diff --git a/ui/desktop/openapi.json b/ui/desktop/openapi.json index e0b73df4ca..b6677221db 100644 --- a/ui/desktop/openapi.json +++ b/ui/desktop/openapi.json @@ -1557,6 +1557,62 @@ } } }, + "/config/typed": { + "get": { + "tags": [ + "super::routes::config_management" + ], + "operationId": "read_typed_config", + "responses": { + "200": { + "description": "All configuration values (typed)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GooseConfigSchema" + } + } + } + }, + "500": { + "description": "Internal server error" + } + } + }, + "patch": { + "tags": [ + "super::routes::config_management" + ], + "summary": "Update configuration values via sparse patch. Only send the fields you want", + "description": "to change — omitted and null fields are both left unchanged (serde cannot\ndistinguish the two). To delete a key, use `POST /config/remove`.\n\nNested objects (`extensions`, `slash_commands`, `experiments`) use whole-value\nreplacement, not deep merge. Secret fields (API keys) are stored in the system\nkeyring, not the config file.\n\n**Caution:** `GET /config/typed` returns values merged from env vars, system\nconfig, and user config. Sending the full GET response back as a PATCH payload\ncan persist inherited/env values into the user config file. Only send fields\nthe user explicitly changed.", + "operationId": "patch_typed_config", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GooseConfigUpdate" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Configuration updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GooseConfigSchema" + } + } + } + }, + "500": { + "description": "Internal server error" + } + } + } + }, "/config/upsert": { "post": { "tags": [ @@ -5520,6 +5576,641 @@ } ] }, + "GooseConfigSchema": { + "type": "object", + "description": "JSON Schema representation of Goose's config.yaml.\n\nAll fields are optional. The standalone JSON Schema (`config.schema.json`)\nsets `additionalProperties: true` so config.yaml can carry undocumented\nprovider-specific keys as env-var overrides. However, the typed API\nendpoints only persist fields explicitly declared on this struct — unknown\nkeys in a `PATCH /config/typed` payload are silently dropped by serde.", + "properties": { + "ANTHROPIC_HOST": { + "type": "string", + "nullable": true + }, + "AVIAN_HOST": { + "type": "string", + "nullable": true + }, + "AWS_PROFILE": { + "type": "string", + "nullable": true + }, + "AWS_REGION": { + "type": "string", + "nullable": true + }, + "AZURE_OPENAI_API_VERSION": { + "type": "string", + "nullable": true + }, + "AZURE_OPENAI_DEPLOYMENT_NAME": { + "type": "string", + "nullable": true + }, + "AZURE_OPENAI_ENDPOINT": { + "type": "string", + "nullable": true + }, + "BEDROCK_BACKOFF_MULTIPLIER": { + "type": "number", + "format": "double", + "nullable": true + }, + "BEDROCK_ENABLE_CACHING": { + "type": "boolean", + "nullable": true + }, + "BEDROCK_INITIAL_RETRY_INTERVAL_MS": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "BEDROCK_MAX_RETRIES": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "BEDROCK_MAX_RETRY_INTERVAL_MS": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "CHATGPT_CODEX_REASONING_EFFORT": { + "type": "string", + "nullable": true + }, + "CLAUDE_CODE_COMMAND": { + "type": "string", + "nullable": true + }, + "CLAUDE_THINKING_BUDGET": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "CLAUDE_THINKING_EFFORT": { + "type": "string", + "nullable": true + }, + "CLAUDE_THINKING_TYPE": { + "type": "string", + "nullable": true + }, + "CODEX_COMMAND": { + "type": "string", + "nullable": true + }, + "CODEX_ENABLE_SKILLS": { + "type": "string", + "nullable": true + }, + "CODEX_REASONING_EFFORT": { + "type": "string", + "nullable": true + }, + "CODEX_SKIP_GIT_CHECK": { + "type": "string", + "nullable": true + }, + "CODE_MODE_TOOL_DISCLOSURE": { + "type": "string", + "nullable": true + }, + "CONTEXT_FILE_NAMES": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "CURSOR_AGENT_COMMAND": { + "type": "string", + "nullable": true + }, + "DATABRICKS_BACKOFF_MULTIPLIER": { + "type": "string", + "nullable": true + }, + "DATABRICKS_HOST": { + "type": "string", + "nullable": true + }, + "DATABRICKS_INITIAL_RETRY_INTERVAL_MS": { + "type": "string", + "nullable": true + }, + "DATABRICKS_MAX_RETRIES": { + "type": "string", + "nullable": true + }, + "DATABRICKS_MAX_RETRY_INTERVAL_MS": { + "type": "string", + "nullable": true + }, + "EDIT_MODE": { + "type": "string", + "nullable": true + }, + "GCP_BACKOFF_MULTIPLIER": { + "type": "string", + "nullable": true + }, + "GCP_INITIAL_RETRY_INTERVAL_MS": { + "type": "string", + "nullable": true + }, + "GCP_LOCATION": { + "type": "string", + "nullable": true + }, + "GCP_MAX_RETRIES": { + "type": "string", + "nullable": true + }, + "GCP_MAX_RETRY_INTERVAL_MS": { + "type": "string", + "nullable": true + }, + "GCP_PROJECT_ID": { + "type": "string", + "nullable": true + }, + "GEMINI25_THINKING_BUDGET": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "GEMINI3_THINKING_LEVEL": { + "type": "string", + "nullable": true + }, + "GEMINI_CLI_COMMAND": { + "type": "string", + "nullable": true + }, + "GITHUB_COPILOT_CLIENT_ID": { + "type": "string", + "nullable": true + }, + "GITHUB_COPILOT_HOST": { + "type": "string", + "nullable": true + }, + "GITHUB_COPILOT_TOKEN_URL": { + "type": "string", + "nullable": true + }, + "GOOGLE_HOST": { + "type": "string", + "nullable": true + }, + "GOOSE_ALLOWLIST": { + "type": "string", + "nullable": true + }, + "GOOSE_AUTO_COMPACT_THRESHOLD": { + "type": "number", + "format": "double", + "nullable": true + }, + "GOOSE_CA_CERT_PATH": { + "type": "string", + "nullable": true + }, + "GOOSE_CLIENT_CERT_PATH": { + "type": "string", + "nullable": true + }, + "GOOSE_CLIENT_KEY_PATH": { + "type": "string", + "nullable": true + }, + "GOOSE_CLI_DARK_THEME": { + "type": "string", + "nullable": true + }, + "GOOSE_CLI_LIGHT_THEME": { + "type": "string", + "nullable": true + }, + "GOOSE_CLI_MIN_PRIORITY": { + "type": "number", + "format": "float", + "nullable": true + }, + "GOOSE_CLI_NEWLINE_KEY": { + "type": "string", + "nullable": true + }, + "GOOSE_CLI_SHOW_COST": { + "type": "boolean", + "nullable": true + }, + "GOOSE_CLI_SHOW_THINKING": { + "type": "boolean", + "nullable": true + }, + "GOOSE_CLI_THEME": { + "type": "string", + "nullable": true + }, + "GOOSE_CONTEXT_LIMIT": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "GOOSE_DATABRICKS_CLIENT_REQUEST_ID": { + "type": "boolean", + "nullable": true + }, + "GOOSE_DEBUG": { + "type": "boolean", + "nullable": true + }, + "GOOSE_DEFAULT_EXTENSION_TIMEOUT": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "GOOSE_DISABLE_KEYRING": { + "type": "boolean", + "nullable": true + }, + "GOOSE_DISABLE_SESSION_NAMING": { + "type": "boolean", + "nullable": true + }, + "GOOSE_DISABLE_TOOL_CALL_SUMMARY": { + "type": "boolean", + "nullable": true + }, + "GOOSE_INPUT_LIMIT": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "GOOSE_LOCAL_ENABLE_THINKING": { + "type": "boolean", + "nullable": true + }, + "GOOSE_MAX_ACTIVE_AGENTS": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "GOOSE_MAX_BACKGROUND_TASKS": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "GOOSE_MAX_TOKENS": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "GOOSE_MAX_TURNS": { + "type": "integer", + "format": "int32", + "nullable": true, + "minimum": 0 + }, + "GOOSE_MODE": { + "allOf": [ + { + "$ref": "#/components/schemas/GooseMode" + } + ], + "nullable": true + }, + "GOOSE_MODEL": { + "type": "string", + "nullable": true + }, + "GOOSE_PLANNER_MODEL": { + "type": "string", + "nullable": true + }, + "GOOSE_PLANNER_PROVIDER": { + "type": "string", + "nullable": true + }, + "GOOSE_PROMPT_EDITOR": { + "type": "string", + "nullable": true + }, + "GOOSE_PROMPT_EDITOR_ALWAYS": { + "type": "boolean", + "nullable": true + }, + "GOOSE_PROVIDER": { + "type": "string", + "nullable": true + }, + "GOOSE_RECIPE_GITHUB_REPO": { + "type": "string", + "nullable": true + }, + "GOOSE_RECIPE_ON_FAILURE_TIMEOUT_SECONDS": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "GOOSE_RECIPE_RETRY_TIMEOUT_SECONDS": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "GOOSE_SEARCH_PATHS": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "GOOSE_SHOW_FULL_OUTPUT": { + "type": "boolean", + "nullable": true + }, + "GOOSE_STATUS_HOOK": { + "type": "string", + "nullable": true + }, + "GOOSE_STREAM_TIMEOUT": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "GOOSE_SUBAGENT_MAX_TURNS": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "GOOSE_SUBAGENT_MODEL": { + "type": "string", + "nullable": true + }, + "GOOSE_SUBAGENT_PROVIDER": { + "type": "string", + "nullable": true + }, + "GOOSE_SYSTEM_PROMPT_FILE_PATH": { + "type": "string", + "nullable": true + }, + "GOOSE_TELEMETRY_ENABLED": { + "type": "boolean", + "nullable": true + }, + "GOOSE_TOOL_CALL_CUTOFF": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "GOOSE_TOOL_PAIR_SUMMARIZATION": { + "type": "boolean", + "nullable": true + }, + "LITELLM_BASE_PATH": { + "type": "string", + "nullable": true + }, + "LITELLM_HOST": { + "type": "string", + "nullable": true + }, + "LITELLM_TIMEOUT": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "OLLAMA_HOST": { + "type": "string", + "nullable": true + }, + "OLLAMA_STREAM_TIMEOUT": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "OLLAMA_STREAM_USAGE": { + "type": "boolean", + "nullable": true + }, + "OLLAMA_TIMEOUT": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "OPENAI_BASE_PATH": { + "type": "string", + "nullable": true + }, + "OPENAI_BASE_URL": { + "type": "string", + "nullable": true + }, + "OPENAI_HOST": { + "type": "string", + "nullable": true + }, + "OPENAI_ORGANIZATION": { + "type": "string", + "nullable": true + }, + "OPENAI_PROJECT": { + "type": "string", + "nullable": true + }, + "OPENAI_TIMEOUT": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "OPENROUTER_HOST": { + "type": "string", + "nullable": true + }, + "RANDOM_THINKING_MESSAGES": { + "type": "boolean", + "nullable": true + }, + "SAGEMAKER_ENDPOINT_NAME": { + "type": "string", + "nullable": true + }, + "SECURITY_COMMAND_CLASSIFIER_ENABLED": { + "type": "boolean", + "nullable": true + }, + "SECURITY_PROMPT_CLASSIFIER_ENABLED": { + "type": "boolean", + "nullable": true + }, + "SECURITY_PROMPT_CLASSIFIER_ENDPOINT": { + "type": "string", + "nullable": true + }, + "SECURITY_PROMPT_CLASSIFIER_MODEL": { + "type": "string", + "nullable": true + }, + "SECURITY_PROMPT_ENABLED": { + "type": "boolean", + "nullable": true + }, + "SECURITY_PROMPT_THRESHOLD": { + "type": "number", + "format": "double", + "nullable": true + }, + "SNOWFLAKE_HOST": { + "type": "string", + "nullable": true + }, + "TETRATE_HOST": { + "type": "string", + "nullable": true + }, + "VENICE_BASE_PATH": { + "type": "string", + "nullable": true + }, + "VENICE_HOST": { + "type": "string", + "nullable": true + }, + "VENICE_MODELS_PATH": { + "type": "string", + "nullable": true + }, + "XAI_HOST": { + "type": "string", + "nullable": true + }, + "experiments": { + "type": "object", + "additionalProperties": { + "type": "boolean" + }, + "nullable": true + }, + "extensions": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/ExtensionEntry" + }, + "nullable": true + }, + "otel_exporter_otlp_endpoint": { + "type": "string", + "nullable": true + }, + "otel_exporter_otlp_timeout": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "slash_commands": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SlashCommandMapping" + }, + "nullable": true + }, + "tunnel_auto_start": { + "type": "boolean", + "nullable": true + } + } + }, + "GooseConfigUpdate": { + "allOf": [ + { + "$ref": "#/components/schemas/GooseConfigSchema" + }, + { + "type": "object", + "properties": { + "ANTHROPIC_API_KEY": { + "type": "string", + "nullable": true + }, + "AVIAN_API_KEY": { + "type": "string", + "nullable": true + }, + "AZURE_OPENAI_API_KEY": { + "type": "string", + "nullable": true + }, + "DATABRICKS_TOKEN": { + "type": "string", + "nullable": true + }, + "GOOGLE_API_KEY": { + "type": "string", + "nullable": true + }, + "GROQ_API_KEY": { + "type": "string", + "nullable": true + }, + "LITELLM_API_KEY": { + "type": "string", + "nullable": true + }, + "LITELLM_CUSTOM_HEADERS": { + "type": "string", + "nullable": true + }, + "NANOGPT_API_KEY": { + "type": "string", + "nullable": true + }, + "OPENAI_API_KEY": { + "type": "string", + "nullable": true + }, + "OPENROUTER_API_KEY": { + "type": "string", + "nullable": true + }, + "SNOWFLAKE_TOKEN": { + "type": "string", + "nullable": true + }, + "TETRATE_API_KEY": { + "type": "string", + "nullable": true + }, + "VENICE_API_KEY": { + "type": "string", + "nullable": true + }, + "XAI_API_KEY": { + "type": "string", + "nullable": true + } + } + } + ], + "description": "Config update payload for `PATCH /config/typed`.\n\nEmbeds all non-secret fields from [`GooseConfigSchema`] via `#[serde(flatten)]`,\nplus provider API key fields that route to the system keyring.\n\n**Sparse patch semantics:** Only send fields you want to change. Fields set to\n`null` (or omitted) are left unchanged — serde cannot distinguish the two cases.\nTo delete a key, use `POST /config/remove`. Nested objects (`extensions`,\n`slash_commands`, `experiments`) use whole-value replacement, not deep merge." + }, "GooseMode": { "type": "string", "enum": [ @@ -8279,6 +8970,21 @@ } } }, + "SlashCommandMapping": { + "type": "object", + "required": [ + "command", + "recipe_path" + ], + "properties": { + "command": { + "type": "string" + }, + "recipe_path": { + "type": "string" + } + } + }, "SlashCommandsResponse": { "type": "object", "required": [ diff --git a/ui/desktop/package.json b/ui/desktop/package.json index d2de6f7024..eb16b5c9bf 100644 --- a/ui/desktop/package.json +++ b/ui/desktop/package.json @@ -115,7 +115,7 @@ "@electron/fuses": "^1.8.0", "@eslint/js": "^9.39.2", "@formatjs/cli": "^6.14.0", - "@hey-api/openapi-ts": "^0.93.0", + "@hey-api/openapi-ts": "0.93.1", "@modelcontextprotocol/sdk": "^1.27.0", "@playwright/test": "^1.58.2", "@tailwindcss/line-clamp": "^0.4.4", diff --git a/ui/desktop/src/api/index.ts b/ui/desktop/src/api/index.ts index fd1811a2c9..f00111687d 100644 --- a/ui/desktop/src/api/index.ts +++ b/ui/desktop/src/api/index.ts @@ -1,4 +1,4 @@ // This file is auto-generated by @hey-api/openapi-ts -export { addExtension, agentAddExtension, agentRemoveExtension, callTool, cancelDownload, cancelLocalModelDownload, checkProvider, cleanupProviderCache, configureProviderOauth, confirmToolAction, createCustomProvider, createRecipe, createSchedule, decodeRecipe, deleteLocalModel, deleteModel, deleteRecipe, deleteSchedule, deleteSession, diagnostics, downloadHfModel, downloadModel, encodeRecipe, exportApp, exportSession, forkSession, getCanonicalModelInfo, getCustomProvider, getDictationConfig, getDownloadProgress, getExtensions, getFeatures, getLocalModelDownloadProgress, getModelSettings, getPrompt, getPrompts, getProviderCatalog, getProviderCatalogTemplate, getProviderModels, getRepoFiles, getSession, getSessionExtensions, getSessionInsights, getSlashCommands, getTools, getTunnelStatus, importApp, importSession, importSessionNostr, inspectRunningJob, killRunningJob, listApps, listLocalModels, listModels, listRecipes, listSchedules, listSessions, mcpUiProxy, type Options, parseRecipe, pauseSchedule, providers, readAllConfig, readConfig, readResource, recipeToYaml, removeConfig, removeCustomProvider, removeExtension, reply, resetPrompt, restartAgent, resumeAgent, runNowHandler, savePrompt, saveRecipe, scanRecipe, scheduleRecipe, searchHfModels, searchSessions, sendTelemetryEvent, sessionCancel, sessionEvents, sessionReply, sessionsHandler, setConfigProvider, setRecipeSlashCommand, shareSessionNostr, startAgent, startNanogptSetup, startOpenrouterSetup, startTetrateSetup, startTunnel, status, stopAgent, stopTunnel, syncFeaturedModels, systemInfo, transcribeDictation, unpauseSchedule, updateAgentProvider, updateCustomProvider, updateFromSession, updateModelSettings, updateSchedule, updateSession, updateSessionName, updateSessionUserRecipeValues, updateWorkingDir, upsertConfig, upsertPermissions, validateConfig } from './sdk.gen'; -export type { ActionRequired, ActionRequiredData, AddExtensionData, AddExtensionErrors, AddExtensionRequest, AddExtensionResponse, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponse, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponse, AgentRemoveExtensionResponses, Annotations, Author, AuthorRequest, CallToolData, CallToolError, CallToolErrors, CallToolRequest, CallToolResponse, CallToolResponse2, CallToolResponses, CancelDownloadData, CancelDownloadErrors, CancelDownloadResponses, CancelLocalModelDownloadData, CancelLocalModelDownloadErrors, CancelLocalModelDownloadResponses, CancelRequest, ChatRequest, CheckProviderData, CheckProviderRequest, CleanupProviderCacheData, CleanupProviderCacheErrors, CleanupProviderCacheResponse, CleanupProviderCacheResponses, ClientOptions, CommandType, ConfigKey, ConfigKeyQuery, ConfigResponse, ConfigureProviderOauthData, ConfigureProviderOauthErrors, ConfigureProviderOauthResponses, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionRequest, ConfirmToolActionResponses, Content, ContentBlock, Conversation, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponse, CreateCustomProviderResponse2, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeRequest, CreateRecipeResponse, CreateRecipeResponse2, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleRequest, CreateScheduleResponse, CreateScheduleResponses, CspMetadata, DeclarativeProviderConfig, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeRequest, DecodeRecipeResponse, DecodeRecipeResponse2, DecodeRecipeResponses, DeleteLocalModelData, DeleteLocalModelErrors, DeleteLocalModelResponses, DeleteModelData, DeleteModelErrors, DeleteModelResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeRequest, DeleteRecipeResponse, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponse, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponse, DiagnosticsResponses, DictationProvider, DictationProviderStatus, DownloadHfModelData, DownloadHfModelErrors, DownloadHfModelResponse, DownloadHfModelResponses, DownloadModelData, DownloadModelErrors, DownloadModelRequest, DownloadModelResponses, DownloadProgress, DownloadStatus, EmbeddedResource, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeRequest, EncodeRecipeResponse, EncodeRecipeResponse2, EncodeRecipeResponses, Envs, EnvVarConfig, ErrorResponse, ExportAppData, ExportAppError, ExportAppErrors, ExportAppResponse, ExportAppResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponse, ExportSessionResponses, ExtensionConfig, ExtensionData, ExtensionEntry, ExtensionLoadResult, ExtensionQuery, ExtensionResponse, FeaturesResponse, ForkRequest, ForkResponse, ForkSessionData, ForkSessionErrors, ForkSessionResponse, ForkSessionResponses, FrontendToolRequest, GetCanonicalModelInfoData, GetCanonicalModelInfoResponse, GetCanonicalModelInfoResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponse, GetCustomProviderResponses, GetDictationConfigData, GetDictationConfigResponse, GetDictationConfigResponses, GetDownloadProgressData, GetDownloadProgressErrors, GetDownloadProgressResponse, GetDownloadProgressResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponse, GetExtensionsResponses, GetFeaturesData, GetFeaturesResponse, GetFeaturesResponses, GetLocalModelDownloadProgressData, GetLocalModelDownloadProgressErrors, GetLocalModelDownloadProgressResponse, GetLocalModelDownloadProgressResponses, GetModelSettingsData, GetModelSettingsErrors, GetModelSettingsResponse, GetModelSettingsResponses, GetPromptData, GetPromptErrors, GetPromptResponse, GetPromptResponses, GetPromptsData, GetPromptsResponse, GetPromptsResponses, GetProviderCatalogData, GetProviderCatalogErrors, GetProviderCatalogResponse, GetProviderCatalogResponses, GetProviderCatalogTemplateData, GetProviderCatalogTemplateErrors, GetProviderCatalogTemplateResponse, GetProviderCatalogTemplateResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponse, GetProviderModelsResponses, GetRepoFilesData, GetRepoFilesResponse, GetRepoFilesResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponse, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponse, GetSessionInsightsResponses, GetSessionResponse, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponse, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsQuery, GetToolsResponse, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponse, GetTunnelStatusResponses, GooseApp, GooseMode, HfGgufFile, HfModelInfo, HfQuantVariant, Icon, IconTheme, ImageContent, ImportAppData, ImportAppError, ImportAppErrors, ImportAppRequest, ImportAppResponse, ImportAppResponse2, ImportAppResponses, ImportSessionData, ImportSessionErrors, ImportSessionNostrData, ImportSessionNostrErrors, ImportSessionNostrRequest, ImportSessionNostrResponse, ImportSessionNostrResponses, ImportSessionRequest, ImportSessionResponse, ImportSessionResponses, InspectJobResponse, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponse, InspectRunningJobResponses, JsonObject, KillJobResponse, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsError, ListAppsErrors, ListAppsRequest, ListAppsResponse, ListAppsResponse2, ListAppsResponses, ListLocalModelsData, ListLocalModelsResponse, ListLocalModelsResponses, ListModelsData, ListModelsResponse, ListModelsResponses, ListRecipeResponse, ListRecipesData, ListRecipesErrors, ListRecipesResponse, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponse, ListSchedulesResponse2, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponse, ListSessionsResponses, LoadedProvider, LocalModelResponse, McpAppResource, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, Message, MessageContent, MessageEvent, MessageMetadata, ModelCapabilities, ModelConfig, ModelDownloadStatus, ModelInfo, ModelInfoData, ModelInfoQuery, ModelInfoResponse, ModelSettings, ModelTemplate, ParseRecipeData, ParseRecipeError, ParseRecipeErrors, ParseRecipeRequest, ParseRecipeResponse, ParseRecipeResponse2, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponse, PauseScheduleResponses, Permission, PermissionLevel, PermissionsMetadata, PrincipalType, PromptContentResponse, PromptsListResponse, ProviderCatalogEntry, ProviderDetails, ProviderEngine, ProviderMetadata, ProvidersData, ProvidersResponse, ProvidersResponse2, ProvidersResponses, ProviderTemplate, ProviderType, RawAudioContent, RawEmbeddedResource, RawImageContent, RawResource, RawTextContent, ReadAllConfigData, ReadAllConfigResponse, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceRequest, ReadResourceResponse, ReadResourceResponse2, ReadResourceResponses, Recipe, RecipeManifest, RecipeParameter, RecipeParameterInputType, RecipeParameterRequirement, RecipeToYamlData, RecipeToYamlError, RecipeToYamlErrors, RecipeToYamlRequest, RecipeToYamlResponse, RecipeToYamlResponse2, RecipeToYamlResponses, RedactedThinkingContent, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponse, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponse, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionRequest, RemoveExtensionResponse, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponse, ReplyResponses, RepoVariantsResponse, ResetPromptData, ResetPromptErrors, ResetPromptResponse, ResetPromptResponses, ResourceContents, ResourceMetadata, Response, RestartAgentData, RestartAgentErrors, RestartAgentRequest, RestartAgentResponse, RestartAgentResponse2, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentRequest, ResumeAgentResponse, ResumeAgentResponse2, ResumeAgentResponses, RetryConfig, Role, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponse, RunNowHandlerResponses, RunNowResponse, SamplingConfig, SavePromptData, SavePromptErrors, SavePromptRequest, SavePromptResponse, SavePromptResponses, SaveRecipeData, SaveRecipeError, SaveRecipeErrors, SaveRecipeRequest, SaveRecipeResponse, SaveRecipeResponse2, SaveRecipeResponses, ScanRecipeData, ScanRecipeRequest, ScanRecipeResponse, ScanRecipeResponse2, ScanRecipeResponses, ScheduledJob, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeRequest, ScheduleRecipeResponses, SearchHfModelsData, SearchHfModelsErrors, SearchHfModelsResponse, SearchHfModelsResponses, SearchSessionsData, SearchSessionsErrors, SearchSessionsResponse, SearchSessionsResponses, SendTelemetryEventData, SendTelemetryEventResponses, Session, SessionCancelData, SessionCancelResponses, SessionDisplayInfo, SessionEventsData, SessionEventsErrors, SessionEventsResponse, SessionEventsResponses, SessionExtensionsResponse, SessionInsights, SessionListResponse, SessionReplyData, SessionReplyErrors, SessionReplyRequest, SessionReplyResponse, SessionReplyResponse2, SessionReplyResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponse, SessionsHandlerResponses, SessionsQuery, SessionType, SetConfigProviderData, SetProviderRequest, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, SetSlashCommandRequest, Settings, SetupResponse, ShareSessionNostrData, ShareSessionNostrErrors, ShareSessionNostrRequest, ShareSessionNostrResponse, ShareSessionNostrResponse2, ShareSessionNostrResponses, SlashCommand, SlashCommandsResponse, StartAgentData, StartAgentError, StartAgentErrors, StartAgentRequest, StartAgentResponse, StartAgentResponses, StartNanogptSetupData, StartNanogptSetupResponse, StartNanogptSetupResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponse, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponse, StartTetrateSetupResponses, StartTunnelData, StartTunnelError, StartTunnelErrors, StartTunnelResponse, StartTunnelResponses, StatusData, StatusResponse, StatusResponses, StopAgentData, StopAgentErrors, StopAgentRequest, StopAgentResponse, StopAgentResponses, StopTunnelData, StopTunnelError, StopTunnelErrors, StopTunnelResponses, SubRecipe, SuccessCheck, SyncFeaturedModelsData, SyncFeaturedModelsResponses, SystemInfo, SystemInfoData, SystemInfoResponse, SystemInfoResponses, SystemNotificationContent, SystemNotificationType, TaskSupport, TelemetryEventRequest, Template, TextContent, ThinkingContent, TokenState, Tool, ToolAnnotations, ToolConfirmationRequest, ToolExecution, ToolInfo, ToolPermission, ToolRequest, ToolResponse, TranscribeDictationData, TranscribeDictationErrors, TranscribeDictationResponse, TranscribeDictationResponses, TranscribeRequest, TranscribeResponse, TunnelInfo, TunnelState, UiMetadata, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponse, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderRequest, UpdateCustomProviderResponse, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionRequest, UpdateFromSessionResponses, UpdateModelSettingsData, UpdateModelSettingsErrors, UpdateModelSettingsResponse, UpdateModelSettingsResponses, UpdateProviderRequest, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleRequest, UpdateScheduleResponse, UpdateScheduleResponses, UpdateSessionData, UpdateSessionErrors, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameRequest, UpdateSessionNameResponses, UpdateSessionRequest, UpdateSessionResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesError, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesRequest, UpdateSessionUserRecipeValuesResponse, UpdateSessionUserRecipeValuesResponse2, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirRequest, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigQuery, UpsertConfigResponse, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsQuery, UpsertPermissionsResponse, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponse, ValidateConfigResponses, WhisperModelResponse, WindowProps } from './types.gen'; +export { addExtension, agentAddExtension, agentRemoveExtension, callTool, cancelDownload, cancelLocalModelDownload, checkProvider, cleanupProviderCache, configureProviderOauth, confirmToolAction, createCustomProvider, createRecipe, createSchedule, decodeRecipe, deleteLocalModel, deleteModel, deleteRecipe, deleteSchedule, deleteSession, diagnostics, downloadHfModel, downloadModel, encodeRecipe, exportApp, exportSession, forkSession, getCanonicalModelInfo, getCustomProvider, getDictationConfig, getDownloadProgress, getExtensions, getFeatures, getLocalModelDownloadProgress, getModelSettings, getPrompt, getPrompts, getProviderCatalog, getProviderCatalogTemplate, getProviderModels, getRepoFiles, getSession, getSessionExtensions, getSessionInsights, getSlashCommands, getTools, getTunnelStatus, importApp, importSession, importSessionNostr, inspectRunningJob, killRunningJob, listApps, listLocalModels, listModels, listRecipes, listSchedules, listSessions, mcpUiProxy, type Options, parseRecipe, patchTypedConfig, pauseSchedule, providers, readAllConfig, readConfig, readResource, readTypedConfig, recipeToYaml, removeConfig, removeCustomProvider, removeExtension, reply, resetPrompt, restartAgent, resumeAgent, runNowHandler, savePrompt, saveRecipe, scanRecipe, scheduleRecipe, searchHfModels, searchSessions, sendTelemetryEvent, sessionCancel, sessionEvents, sessionReply, sessionsHandler, setConfigProvider, setRecipeSlashCommand, shareSessionNostr, startAgent, startNanogptSetup, startOpenrouterSetup, startTetrateSetup, startTunnel, status, stopAgent, stopTunnel, syncFeaturedModels, systemInfo, transcribeDictation, unpauseSchedule, updateAgentProvider, updateCustomProvider, updateFromSession, updateModelSettings, updateSchedule, updateSession, updateSessionName, updateSessionUserRecipeValues, updateWorkingDir, upsertConfig, upsertPermissions, validateConfig } from './sdk.gen'; +export type { ActionRequired, ActionRequiredData, AddExtensionData, AddExtensionErrors, AddExtensionRequest, AddExtensionResponse, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponse, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponse, AgentRemoveExtensionResponses, Annotations, Author, AuthorRequest, CallToolData, CallToolError, CallToolErrors, CallToolRequest, CallToolResponse, CallToolResponse2, CallToolResponses, CancelDownloadData, CancelDownloadErrors, CancelDownloadResponses, CancelLocalModelDownloadData, CancelLocalModelDownloadErrors, CancelLocalModelDownloadResponses, CancelRequest, ChatRequest, CheckProviderData, CheckProviderRequest, CleanupProviderCacheData, CleanupProviderCacheErrors, CleanupProviderCacheResponse, CleanupProviderCacheResponses, ClientOptions, CommandType, ConfigKey, ConfigKeyQuery, ConfigResponse, ConfigureProviderOauthData, ConfigureProviderOauthErrors, ConfigureProviderOauthResponses, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionRequest, ConfirmToolActionResponses, Content, ContentBlock, Conversation, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponse, CreateCustomProviderResponse2, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeRequest, CreateRecipeResponse, CreateRecipeResponse2, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleRequest, CreateScheduleResponse, CreateScheduleResponses, CspMetadata, DeclarativeProviderConfig, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeRequest, DecodeRecipeResponse, DecodeRecipeResponse2, DecodeRecipeResponses, DeleteLocalModelData, DeleteLocalModelErrors, DeleteLocalModelResponses, DeleteModelData, DeleteModelErrors, DeleteModelResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeRequest, DeleteRecipeResponse, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponse, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponse, DiagnosticsResponses, DictationProvider, DictationProviderStatus, DownloadHfModelData, DownloadHfModelErrors, DownloadHfModelResponse, DownloadHfModelResponses, DownloadModelData, DownloadModelErrors, DownloadModelRequest, DownloadModelResponses, DownloadProgress, DownloadStatus, EmbeddedResource, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeRequest, EncodeRecipeResponse, EncodeRecipeResponse2, EncodeRecipeResponses, Envs, EnvVarConfig, ErrorResponse, ExportAppData, ExportAppError, ExportAppErrors, ExportAppResponse, ExportAppResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponse, ExportSessionResponses, ExtensionConfig, ExtensionData, ExtensionEntry, ExtensionLoadResult, ExtensionQuery, ExtensionResponse, FeaturesResponse, ForkRequest, ForkResponse, ForkSessionData, ForkSessionErrors, ForkSessionResponse, ForkSessionResponses, FrontendToolRequest, GetCanonicalModelInfoData, GetCanonicalModelInfoResponse, GetCanonicalModelInfoResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponse, GetCustomProviderResponses, GetDictationConfigData, GetDictationConfigResponse, GetDictationConfigResponses, GetDownloadProgressData, GetDownloadProgressErrors, GetDownloadProgressResponse, GetDownloadProgressResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponse, GetExtensionsResponses, GetFeaturesData, GetFeaturesResponse, GetFeaturesResponses, GetLocalModelDownloadProgressData, GetLocalModelDownloadProgressErrors, GetLocalModelDownloadProgressResponse, GetLocalModelDownloadProgressResponses, GetModelSettingsData, GetModelSettingsErrors, GetModelSettingsResponse, GetModelSettingsResponses, GetPromptData, GetPromptErrors, GetPromptResponse, GetPromptResponses, GetPromptsData, GetPromptsResponse, GetPromptsResponses, GetProviderCatalogData, GetProviderCatalogErrors, GetProviderCatalogResponse, GetProviderCatalogResponses, GetProviderCatalogTemplateData, GetProviderCatalogTemplateErrors, GetProviderCatalogTemplateResponse, GetProviderCatalogTemplateResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponse, GetProviderModelsResponses, GetRepoFilesData, GetRepoFilesResponse, GetRepoFilesResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponse, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponse, GetSessionInsightsResponses, GetSessionResponse, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponse, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsQuery, GetToolsResponse, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponse, GetTunnelStatusResponses, GooseApp, GooseConfigSchema, GooseConfigUpdate, GooseMode, HfGgufFile, HfModelInfo, HfQuantVariant, Icon, IconTheme, ImageContent, ImportAppData, ImportAppError, ImportAppErrors, ImportAppRequest, ImportAppResponse, ImportAppResponse2, ImportAppResponses, ImportSessionData, ImportSessionErrors, ImportSessionNostrData, ImportSessionNostrErrors, ImportSessionNostrRequest, ImportSessionNostrResponse, ImportSessionNostrResponses, ImportSessionRequest, ImportSessionResponse, ImportSessionResponses, InspectJobResponse, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponse, InspectRunningJobResponses, JsonObject, KillJobResponse, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsError, ListAppsErrors, ListAppsRequest, ListAppsResponse, ListAppsResponse2, ListAppsResponses, ListLocalModelsData, ListLocalModelsResponse, ListLocalModelsResponses, ListModelsData, ListModelsResponse, ListModelsResponses, ListRecipeResponse, ListRecipesData, ListRecipesErrors, ListRecipesResponse, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponse, ListSchedulesResponse2, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponse, ListSessionsResponses, LoadedProvider, LocalModelResponse, McpAppResource, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, Message, MessageContent, MessageEvent, MessageMetadata, ModelCapabilities, ModelConfig, ModelDownloadStatus, ModelInfo, ModelInfoData, ModelInfoQuery, ModelInfoResponse, ModelSettings, ModelTemplate, ParseRecipeData, ParseRecipeError, ParseRecipeErrors, ParseRecipeRequest, ParseRecipeResponse, ParseRecipeResponse2, ParseRecipeResponses, PatchTypedConfigData, PatchTypedConfigErrors, PatchTypedConfigResponse, PatchTypedConfigResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponse, PauseScheduleResponses, Permission, PermissionLevel, PermissionsMetadata, PrincipalType, PromptContentResponse, PromptsListResponse, ProviderCatalogEntry, ProviderDetails, ProviderEngine, ProviderMetadata, ProvidersData, ProvidersResponse, ProvidersResponse2, ProvidersResponses, ProviderTemplate, ProviderType, RawAudioContent, RawEmbeddedResource, RawImageContent, RawResource, RawTextContent, ReadAllConfigData, ReadAllConfigResponse, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceRequest, ReadResourceResponse, ReadResourceResponse2, ReadResourceResponses, ReadTypedConfigData, ReadTypedConfigErrors, ReadTypedConfigResponse, ReadTypedConfigResponses, Recipe, RecipeManifest, RecipeParameter, RecipeParameterInputType, RecipeParameterRequirement, RecipeToYamlData, RecipeToYamlError, RecipeToYamlErrors, RecipeToYamlRequest, RecipeToYamlResponse, RecipeToYamlResponse2, RecipeToYamlResponses, RedactedThinkingContent, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponse, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponse, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionRequest, RemoveExtensionResponse, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponse, ReplyResponses, RepoVariantsResponse, ResetPromptData, ResetPromptErrors, ResetPromptResponse, ResetPromptResponses, ResourceContents, ResourceMetadata, Response, RestartAgentData, RestartAgentErrors, RestartAgentRequest, RestartAgentResponse, RestartAgentResponse2, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentRequest, ResumeAgentResponse, ResumeAgentResponse2, ResumeAgentResponses, RetryConfig, Role, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponse, RunNowHandlerResponses, RunNowResponse, SamplingConfig, SavePromptData, SavePromptErrors, SavePromptRequest, SavePromptResponse, SavePromptResponses, SaveRecipeData, SaveRecipeError, SaveRecipeErrors, SaveRecipeRequest, SaveRecipeResponse, SaveRecipeResponse2, SaveRecipeResponses, ScanRecipeData, ScanRecipeRequest, ScanRecipeResponse, ScanRecipeResponse2, ScanRecipeResponses, ScheduledJob, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeRequest, ScheduleRecipeResponses, SearchHfModelsData, SearchHfModelsErrors, SearchHfModelsResponse, SearchHfModelsResponses, SearchSessionsData, SearchSessionsErrors, SearchSessionsResponse, SearchSessionsResponses, SendTelemetryEventData, SendTelemetryEventResponses, Session, SessionCancelData, SessionCancelResponses, SessionDisplayInfo, SessionEventsData, SessionEventsErrors, SessionEventsResponse, SessionEventsResponses, SessionExtensionsResponse, SessionInsights, SessionListResponse, SessionReplyData, SessionReplyErrors, SessionReplyRequest, SessionReplyResponse, SessionReplyResponse2, SessionReplyResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponse, SessionsHandlerResponses, SessionsQuery, SessionType, SetConfigProviderData, SetProviderRequest, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, SetSlashCommandRequest, Settings, SetupResponse, ShareSessionNostrData, ShareSessionNostrErrors, ShareSessionNostrRequest, ShareSessionNostrResponse, ShareSessionNostrResponse2, ShareSessionNostrResponses, SlashCommand, SlashCommandMapping, SlashCommandsResponse, StartAgentData, StartAgentError, StartAgentErrors, StartAgentRequest, StartAgentResponse, StartAgentResponses, StartNanogptSetupData, StartNanogptSetupResponse, StartNanogptSetupResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponse, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponse, StartTetrateSetupResponses, StartTunnelData, StartTunnelError, StartTunnelErrors, StartTunnelResponse, StartTunnelResponses, StatusData, StatusResponse, StatusResponses, StopAgentData, StopAgentErrors, StopAgentRequest, StopAgentResponse, StopAgentResponses, StopTunnelData, StopTunnelError, StopTunnelErrors, StopTunnelResponses, SubRecipe, SuccessCheck, SyncFeaturedModelsData, SyncFeaturedModelsResponses, SystemInfo, SystemInfoData, SystemInfoResponse, SystemInfoResponses, SystemNotificationContent, SystemNotificationType, TaskSupport, TelemetryEventRequest, Template, TextContent, ThinkingContent, TokenState, Tool, ToolAnnotations, ToolConfirmationRequest, ToolExecution, ToolInfo, ToolPermission, ToolRequest, ToolResponse, TranscribeDictationData, TranscribeDictationErrors, TranscribeDictationResponse, TranscribeDictationResponses, TranscribeRequest, TranscribeResponse, TunnelInfo, TunnelState, UiMetadata, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponse, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderRequest, UpdateCustomProviderResponse, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionRequest, UpdateFromSessionResponses, UpdateModelSettingsData, UpdateModelSettingsErrors, UpdateModelSettingsResponse, UpdateModelSettingsResponses, UpdateProviderRequest, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleRequest, UpdateScheduleResponse, UpdateScheduleResponses, UpdateSessionData, UpdateSessionErrors, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameRequest, UpdateSessionNameResponses, UpdateSessionRequest, UpdateSessionResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesError, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesRequest, UpdateSessionUserRecipeValuesResponse, UpdateSessionUserRecipeValuesResponse2, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirRequest, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigQuery, UpsertConfigResponse, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsQuery, UpsertPermissionsResponse, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponse, ValidateConfigResponses, WhisperModelResponse, WindowProps } from './types.gen'; diff --git a/ui/desktop/src/api/sdk.gen.ts b/ui/desktop/src/api/sdk.gen.ts index 2870da539d..c6a180512c 100644 --- a/ui/desktop/src/api/sdk.gen.ts +++ b/ui/desktop/src/api/sdk.gen.ts @@ -2,7 +2,7 @@ import type { Client, Options as Options2, TDataShape } from './client'; import { client } from './client.gen'; -import type { AddExtensionData, AddExtensionErrors, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponses, CallToolData, CallToolErrors, CallToolResponses, CancelDownloadData, CancelDownloadErrors, CancelDownloadResponses, CancelLocalModelDownloadData, CancelLocalModelDownloadErrors, CancelLocalModelDownloadResponses, CheckProviderData, CleanupProviderCacheData, CleanupProviderCacheErrors, CleanupProviderCacheResponses, ConfigureProviderOauthData, ConfigureProviderOauthErrors, ConfigureProviderOauthResponses, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteLocalModelData, DeleteLocalModelErrors, DeleteLocalModelResponses, DeleteModelData, DeleteModelErrors, DeleteModelResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, DownloadHfModelData, DownloadHfModelErrors, DownloadHfModelResponses, DownloadModelData, DownloadModelErrors, DownloadModelResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportAppData, ExportAppErrors, ExportAppResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, ForkSessionData, ForkSessionErrors, ForkSessionResponses, GetCanonicalModelInfoData, GetCanonicalModelInfoResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetDictationConfigData, GetDictationConfigResponses, GetDownloadProgressData, GetDownloadProgressErrors, GetDownloadProgressResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetFeaturesData, GetFeaturesResponses, GetLocalModelDownloadProgressData, GetLocalModelDownloadProgressErrors, GetLocalModelDownloadProgressResponses, GetModelSettingsData, GetModelSettingsErrors, GetModelSettingsResponses, GetPromptData, GetPromptErrors, GetPromptResponses, GetPromptsData, GetPromptsResponses, GetProviderCatalogData, GetProviderCatalogErrors, GetProviderCatalogResponses, GetProviderCatalogTemplateData, GetProviderCatalogTemplateErrors, GetProviderCatalogTemplateResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetRepoFilesData, GetRepoFilesResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponses, ImportAppData, ImportAppErrors, ImportAppResponses, ImportSessionData, ImportSessionErrors, ImportSessionNostrData, ImportSessionNostrErrors, ImportSessionNostrResponses, ImportSessionResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsErrors, ListAppsResponses, ListLocalModelsData, ListLocalModelsResponses, ListModelsData, ListModelsResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceResponses, RecipeToYamlData, RecipeToYamlErrors, RecipeToYamlResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, ResetPromptData, ResetPromptErrors, ResetPromptResponses, RestartAgentData, RestartAgentErrors, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SavePromptData, SavePromptErrors, SavePromptResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeResponses, SearchHfModelsData, SearchHfModelsErrors, SearchHfModelsResponses, SearchSessionsData, SearchSessionsErrors, SearchSessionsResponses, SendTelemetryEventData, SendTelemetryEventResponses, SessionCancelData, SessionCancelResponses, SessionEventsData, SessionEventsErrors, SessionEventsResponses, SessionReplyData, SessionReplyErrors, SessionReplyResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, SetConfigProviderData, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, ShareSessionNostrData, ShareSessionNostrErrors, ShareSessionNostrResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartNanogptSetupData, StartNanogptSetupResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StartTunnelData, StartTunnelErrors, StartTunnelResponses, StatusData, StatusResponses, StopAgentData, StopAgentErrors, StopAgentResponses, StopTunnelData, StopTunnelErrors, StopTunnelResponses, SyncFeaturedModelsData, SyncFeaturedModelsResponses, SystemInfoData, SystemInfoResponses, TranscribeDictationData, TranscribeDictationErrors, TranscribeDictationResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateModelSettingsData, UpdateModelSettingsErrors, UpdateModelSettingsResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionData, UpdateSessionErrors, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen'; +import type { AddExtensionData, AddExtensionErrors, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponses, CallToolData, CallToolErrors, CallToolResponses, CancelDownloadData, CancelDownloadErrors, CancelDownloadResponses, CancelLocalModelDownloadData, CancelLocalModelDownloadErrors, CancelLocalModelDownloadResponses, CheckProviderData, CleanupProviderCacheData, CleanupProviderCacheErrors, CleanupProviderCacheResponses, ConfigureProviderOauthData, ConfigureProviderOauthErrors, ConfigureProviderOauthResponses, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteLocalModelData, DeleteLocalModelErrors, DeleteLocalModelResponses, DeleteModelData, DeleteModelErrors, DeleteModelResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, DownloadHfModelData, DownloadHfModelErrors, DownloadHfModelResponses, DownloadModelData, DownloadModelErrors, DownloadModelResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportAppData, ExportAppErrors, ExportAppResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, ForkSessionData, ForkSessionErrors, ForkSessionResponses, GetCanonicalModelInfoData, GetCanonicalModelInfoResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetDictationConfigData, GetDictationConfigResponses, GetDownloadProgressData, GetDownloadProgressErrors, GetDownloadProgressResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetFeaturesData, GetFeaturesResponses, GetLocalModelDownloadProgressData, GetLocalModelDownloadProgressErrors, GetLocalModelDownloadProgressResponses, GetModelSettingsData, GetModelSettingsErrors, GetModelSettingsResponses, GetPromptData, GetPromptErrors, GetPromptResponses, GetPromptsData, GetPromptsResponses, GetProviderCatalogData, GetProviderCatalogErrors, GetProviderCatalogResponses, GetProviderCatalogTemplateData, GetProviderCatalogTemplateErrors, GetProviderCatalogTemplateResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetRepoFilesData, GetRepoFilesResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponses, ImportAppData, ImportAppErrors, ImportAppResponses, ImportSessionData, ImportSessionErrors, ImportSessionNostrData, ImportSessionNostrErrors, ImportSessionNostrResponses, ImportSessionResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsErrors, ListAppsResponses, ListLocalModelsData, ListLocalModelsResponses, ListModelsData, ListModelsResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PatchTypedConfigData, PatchTypedConfigErrors, PatchTypedConfigResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceResponses, ReadTypedConfigData, ReadTypedConfigErrors, ReadTypedConfigResponses, RecipeToYamlData, RecipeToYamlErrors, RecipeToYamlResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, ResetPromptData, ResetPromptErrors, ResetPromptResponses, RestartAgentData, RestartAgentErrors, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SavePromptData, SavePromptErrors, SavePromptResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeResponses, SearchHfModelsData, SearchHfModelsErrors, SearchHfModelsResponses, SearchSessionsData, SearchSessionsErrors, SearchSessionsResponses, SendTelemetryEventData, SendTelemetryEventResponses, SessionCancelData, SessionCancelResponses, SessionEventsData, SessionEventsErrors, SessionEventsResponses, SessionReplyData, SessionReplyErrors, SessionReplyResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, SetConfigProviderData, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, ShareSessionNostrData, ShareSessionNostrErrors, ShareSessionNostrResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartNanogptSetupData, StartNanogptSetupResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StartTunnelData, StartTunnelErrors, StartTunnelResponses, StatusData, StatusResponses, StopAgentData, StopAgentErrors, StopAgentResponses, StopTunnelData, StopTunnelErrors, StopTunnelResponses, SyncFeaturedModelsData, SyncFeaturedModelsResponses, SystemInfoData, SystemInfoResponses, TranscribeDictationData, TranscribeDictationErrors, TranscribeDictationResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateModelSettingsData, UpdateModelSettingsErrors, UpdateModelSettingsResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionData, UpdateSessionErrors, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen'; export type Options = Options2 & { /** @@ -270,6 +270,32 @@ export const setConfigProvider = (options: export const getSlashCommands = (options?: Options) => (options?.client ?? client).get({ url: '/config/slash_commands', ...options }); +export const readTypedConfig = (options?: Options) => (options?.client ?? client).get({ url: '/config/typed', ...options }); + +/** + * Update configuration values via sparse patch. Only send the fields you want + * + * to change — omitted and null fields are both left unchanged (serde cannot + * distinguish the two). To delete a key, use `POST /config/remove`. + * + * Nested objects (`extensions`, `slash_commands`, `experiments`) use whole-value + * replacement, not deep merge. Secret fields (API keys) are stored in the system + * keyring, not the config file. + * + * **Caution:** `GET /config/typed` returns values merged from env vars, system + * config, and user config. Sending the full GET response back as a PATCH payload + * can persist inherited/env values into the user config file. Only send fields + * the user explicitly changed. + */ +export const patchTypedConfig = (options: Options) => (options.client ?? client).patch({ + url: '/config/typed', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + export const upsertConfig = (options: Options) => (options.client ?? client).post({ url: '/config/upsert', ...options, diff --git a/ui/desktop/src/api/types.gen.ts b/ui/desktop/src/api/types.gen.ts index da88661b8f..5edfeb1f9b 100644 --- a/ui/desktop/src/api/types.gen.ts +++ b/ui/desktop/src/api/types.gen.ts @@ -530,6 +530,174 @@ export type GooseApp = McpAppResource & (WindowProps | null) & { prd?: string | null; }; +/** + * JSON Schema representation of Goose's config.yaml. + * + * All fields are optional. The standalone JSON Schema (`config.schema.json`) + * sets `additionalProperties: true` so config.yaml can carry undocumented + * provider-specific keys as env-var overrides. However, the typed API + * endpoints only persist fields explicitly declared on this struct — unknown + * keys in a `PATCH /config/typed` payload are silently dropped by serde. + */ +export type GooseConfigSchema = { + ANTHROPIC_HOST?: string | null; + AVIAN_HOST?: string | null; + AWS_PROFILE?: string | null; + AWS_REGION?: string | null; + AZURE_OPENAI_API_VERSION?: string | null; + AZURE_OPENAI_DEPLOYMENT_NAME?: string | null; + AZURE_OPENAI_ENDPOINT?: string | null; + BEDROCK_BACKOFF_MULTIPLIER?: number | null; + BEDROCK_ENABLE_CACHING?: boolean | null; + BEDROCK_INITIAL_RETRY_INTERVAL_MS?: number | null; + BEDROCK_MAX_RETRIES?: number | null; + BEDROCK_MAX_RETRY_INTERVAL_MS?: number | null; + CHATGPT_CODEX_REASONING_EFFORT?: string | null; + CLAUDE_CODE_COMMAND?: string | null; + CLAUDE_THINKING_BUDGET?: number | null; + CLAUDE_THINKING_EFFORT?: string | null; + CLAUDE_THINKING_TYPE?: string | null; + CODEX_COMMAND?: string | null; + CODEX_ENABLE_SKILLS?: string | null; + CODEX_REASONING_EFFORT?: string | null; + CODEX_SKIP_GIT_CHECK?: string | null; + CODE_MODE_TOOL_DISCLOSURE?: string | null; + CONTEXT_FILE_NAMES?: Array | null; + CURSOR_AGENT_COMMAND?: string | null; + DATABRICKS_BACKOFF_MULTIPLIER?: string | null; + DATABRICKS_HOST?: string | null; + DATABRICKS_INITIAL_RETRY_INTERVAL_MS?: string | null; + DATABRICKS_MAX_RETRIES?: string | null; + DATABRICKS_MAX_RETRY_INTERVAL_MS?: string | null; + EDIT_MODE?: string | null; + GCP_BACKOFF_MULTIPLIER?: string | null; + GCP_INITIAL_RETRY_INTERVAL_MS?: string | null; + GCP_LOCATION?: string | null; + GCP_MAX_RETRIES?: string | null; + GCP_MAX_RETRY_INTERVAL_MS?: string | null; + GCP_PROJECT_ID?: string | null; + GEMINI25_THINKING_BUDGET?: number | null; + GEMINI3_THINKING_LEVEL?: string | null; + GEMINI_CLI_COMMAND?: string | null; + GITHUB_COPILOT_CLIENT_ID?: string | null; + GITHUB_COPILOT_HOST?: string | null; + GITHUB_COPILOT_TOKEN_URL?: string | null; + GOOGLE_HOST?: string | null; + GOOSE_ALLOWLIST?: string | null; + GOOSE_AUTO_COMPACT_THRESHOLD?: number | null; + GOOSE_CA_CERT_PATH?: string | null; + GOOSE_CLIENT_CERT_PATH?: string | null; + GOOSE_CLIENT_KEY_PATH?: string | null; + GOOSE_CLI_DARK_THEME?: string | null; + GOOSE_CLI_LIGHT_THEME?: string | null; + GOOSE_CLI_MIN_PRIORITY?: number | null; + GOOSE_CLI_NEWLINE_KEY?: string | null; + GOOSE_CLI_SHOW_COST?: boolean | null; + GOOSE_CLI_SHOW_THINKING?: boolean | null; + GOOSE_CLI_THEME?: string | null; + GOOSE_CONTEXT_LIMIT?: number | null; + GOOSE_DATABRICKS_CLIENT_REQUEST_ID?: boolean | null; + GOOSE_DEBUG?: boolean | null; + GOOSE_DEFAULT_EXTENSION_TIMEOUT?: number | null; + GOOSE_DISABLE_KEYRING?: boolean | null; + GOOSE_DISABLE_SESSION_NAMING?: boolean | null; + GOOSE_DISABLE_TOOL_CALL_SUMMARY?: boolean | null; + GOOSE_INPUT_LIMIT?: number | null; + GOOSE_LOCAL_ENABLE_THINKING?: boolean | null; + GOOSE_MAX_ACTIVE_AGENTS?: number | null; + GOOSE_MAX_BACKGROUND_TASKS?: number | null; + GOOSE_MAX_TOKENS?: number | null; + GOOSE_MAX_TURNS?: number | null; + GOOSE_MODE?: GooseMode | null; + GOOSE_MODEL?: string | null; + GOOSE_PLANNER_MODEL?: string | null; + GOOSE_PLANNER_PROVIDER?: string | null; + GOOSE_PROMPT_EDITOR?: string | null; + GOOSE_PROMPT_EDITOR_ALWAYS?: boolean | null; + GOOSE_PROVIDER?: string | null; + GOOSE_RECIPE_GITHUB_REPO?: string | null; + GOOSE_RECIPE_ON_FAILURE_TIMEOUT_SECONDS?: number | null; + GOOSE_RECIPE_RETRY_TIMEOUT_SECONDS?: number | null; + GOOSE_SEARCH_PATHS?: Array | null; + GOOSE_SHOW_FULL_OUTPUT?: boolean | null; + GOOSE_STATUS_HOOK?: string | null; + GOOSE_STREAM_TIMEOUT?: number | null; + GOOSE_SUBAGENT_MAX_TURNS?: number | null; + GOOSE_SUBAGENT_MODEL?: string | null; + GOOSE_SUBAGENT_PROVIDER?: string | null; + GOOSE_SYSTEM_PROMPT_FILE_PATH?: string | null; + GOOSE_TELEMETRY_ENABLED?: boolean | null; + GOOSE_TOOL_CALL_CUTOFF?: number | null; + GOOSE_TOOL_PAIR_SUMMARIZATION?: boolean | null; + LITELLM_BASE_PATH?: string | null; + LITELLM_HOST?: string | null; + LITELLM_TIMEOUT?: number | null; + OLLAMA_HOST?: string | null; + OLLAMA_STREAM_TIMEOUT?: number | null; + OLLAMA_STREAM_USAGE?: boolean | null; + OLLAMA_TIMEOUT?: number | null; + OPENAI_BASE_PATH?: string | null; + OPENAI_BASE_URL?: string | null; + OPENAI_HOST?: string | null; + OPENAI_ORGANIZATION?: string | null; + OPENAI_PROJECT?: string | null; + OPENAI_TIMEOUT?: number | null; + OPENROUTER_HOST?: string | null; + RANDOM_THINKING_MESSAGES?: boolean | null; + SAGEMAKER_ENDPOINT_NAME?: string | null; + SECURITY_COMMAND_CLASSIFIER_ENABLED?: boolean | null; + SECURITY_PROMPT_CLASSIFIER_ENABLED?: boolean | null; + SECURITY_PROMPT_CLASSIFIER_ENDPOINT?: string | null; + SECURITY_PROMPT_CLASSIFIER_MODEL?: string | null; + SECURITY_PROMPT_ENABLED?: boolean | null; + SECURITY_PROMPT_THRESHOLD?: number | null; + SNOWFLAKE_HOST?: string | null; + TETRATE_HOST?: string | null; + VENICE_BASE_PATH?: string | null; + VENICE_HOST?: string | null; + VENICE_MODELS_PATH?: string | null; + XAI_HOST?: string | null; + experiments?: { + [key: string]: boolean; + } | null; + extensions?: { + [key: string]: ExtensionEntry; + } | null; + otel_exporter_otlp_endpoint?: string | null; + otel_exporter_otlp_timeout?: number | null; + slash_commands?: Array | null; + tunnel_auto_start?: boolean | null; +}; + +/** + * Config update payload for `PATCH /config/typed`. + * + * Embeds all non-secret fields from [`GooseConfigSchema`] via `#[serde(flatten)]`, + * plus provider API key fields that route to the system keyring. + * + * **Sparse patch semantics:** Only send fields you want to change. Fields set to + * `null` (or omitted) are left unchanged — serde cannot distinguish the two cases. + * To delete a key, use `POST /config/remove`. Nested objects (`extensions`, + * `slash_commands`, `experiments`) use whole-value replacement, not deep merge. + */ +export type GooseConfigUpdate = GooseConfigSchema & { + ANTHROPIC_API_KEY?: string | null; + AVIAN_API_KEY?: string | null; + AZURE_OPENAI_API_KEY?: string | null; + DATABRICKS_TOKEN?: string | null; + GOOGLE_API_KEY?: string | null; + GROQ_API_KEY?: string | null; + LITELLM_API_KEY?: string | null; + LITELLM_CUSTOM_HEADERS?: string | null; + NANOGPT_API_KEY?: string | null; + OPENAI_API_KEY?: string | null; + OPENROUTER_API_KEY?: string | null; + SNOWFLAKE_TOKEN?: string | null; + TETRATE_API_KEY?: string | null; + VENICE_API_KEY?: string | null; + XAI_API_KEY?: string | null; +}; + export type GooseMode = 'auto' | 'approve' | 'smart_approve' | 'chat'; /** @@ -1391,6 +1559,11 @@ export type SlashCommand = { help: string; }; +export type SlashCommandMapping = { + command: string; + recipe_path: string; +}; + export type SlashCommandsResponse = { commands: Array; }; @@ -2865,6 +3038,52 @@ export type GetSlashCommandsResponses = { export type GetSlashCommandsResponse = GetSlashCommandsResponses[keyof GetSlashCommandsResponses]; +export type ReadTypedConfigData = { + body?: never; + path?: never; + query?: never; + url: '/config/typed'; +}; + +export type ReadTypedConfigErrors = { + /** + * Internal server error + */ + 500: unknown; +}; + +export type ReadTypedConfigResponses = { + /** + * All configuration values (typed) + */ + 200: GooseConfigSchema; +}; + +export type ReadTypedConfigResponse = ReadTypedConfigResponses[keyof ReadTypedConfigResponses]; + +export type PatchTypedConfigData = { + body: GooseConfigUpdate; + path?: never; + query?: never; + url: '/config/typed'; +}; + +export type PatchTypedConfigErrors = { + /** + * Internal server error + */ + 500: unknown; +}; + +export type PatchTypedConfigResponses = { + /** + * Configuration updated + */ + 200: GooseConfigSchema; +}; + +export type PatchTypedConfigResponse = PatchTypedConfigResponses[keyof PatchTypedConfigResponses]; + export type UpsertConfigData = { body: UpsertConfigQuery; path?: never; diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index ca34af6559..c773c6a517 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -224,7 +224,7 @@ importers: specifier: ^6.14.0 version: 6.14.0 '@hey-api/openapi-ts': - specifier: ^0.93.0 + specifier: 0.93.1 version: 0.93.1(magicast@0.5.2)(typescript@5.9.3) '@modelcontextprotocol/sdk': specifier: ^1.27.0