This commit is contained in:
Will Pfleger 2026-05-16 11:35:00 -04:00 committed by GitHub
commit 661cd1a579
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 3662 additions and 10 deletions

View file

@ -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

View file

@ -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

View file

@ -1490,7 +1490,11 @@ pub fn configure_keyring_dialog() -> anyhow::Result<()> {
);
}
let currently_disabled = config.get_param::<String>("GOOSE_DISABLE_KEYRING").is_ok();
let currently_disabled = config
.get_param::<serde_yaml::Value>("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)"

View file

@ -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,

View file

@ -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<Json<GooseConfigSchema>, 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<GooseConfigUpdate>,
) -> Result<Json<GooseConfigSchema>, ErrorResponse> {
let config = Config::global();
update.apply_to_config(config)?;
let typed = GooseConfigSchema::from_config(config);
Ok(Json(typed))
}
pub fn routes(state: Arc<AppState>) -> 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))

View file

@ -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 = [

File diff suppressed because it is too large Load diff

View file

@ -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<T> = Result<T, ExtensionError>;
#[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

View file

@ -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());
}
}

View file

@ -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)]

View file

@ -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")]

View file

@ -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;

File diff suppressed because it is too large Load diff

View file

@ -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,

View file

@ -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": [

View file

@ -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",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -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<string> | 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<string> | 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<SlashCommandMapping> | 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<SlashCommand>;
};
@ -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;

2
ui/pnpm-lock.yaml generated
View file

@ -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