Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9801,6 +9801,32 @@
},
"type": "object"
},
"AppConfig": {
"properties": {
"disabled_reason": {
"anyOf": [
{
"$ref": "#/definitions/v2/AppDisabledReason"
},
{
"type": "null"
}
]
},
"enabled": {
"default": true,
"type": "boolean"
}
},
"type": "object"
},
"AppDisabledReason": {
"enum": [
"unknown",
"user"
],
"type": "string"
},
"AppInfo": {
"properties": {
"description": {
Expand Down Expand Up @@ -9850,6 +9876,9 @@
],
"type": "object"
},
"AppsConfig": {
"type": "object"
},
"AppsListParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
Expand Down Expand Up @@ -10465,6 +10494,17 @@
}
]
},
"apps": {
"anyOf": [
{
"$ref": "#/definitions/v2/AppsConfig"
},
{
"type": "null"
}
],
"default": null
},
"compact_prompt": {
"type": [
"string",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,35 @@
},
"type": "object"
},
"AppConfig": {
"properties": {
"disabled_reason": {
"anyOf": [
{
"$ref": "#/definitions/AppDisabledReason"
},
{
"type": "null"
}
]
},
"enabled": {
"default": true,
"type": "boolean"
}
},
"type": "object"
},
"AppDisabledReason": {
"enum": [
"unknown",
"user"
],
"type": "string"
},
"AppsConfig": {
"type": "object"
},
"AskForApproval": {
"enum": [
"untrusted",
Expand Down Expand Up @@ -49,6 +78,17 @@
}
]
},
"apps": {
"anyOf": [
{
"$ref": "#/definitions/AppsConfig"
},
{
"type": "null"
}
],
"default": null
},
"compact_prompt": {
"type": [
"string",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!

// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.

export type AppDisabledReason = "unknown" | "user";
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!

// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { AppDisabledReason } from "./AppDisabledReason";

export type AppsConfig = { [key in string]?: { enabled: boolean, disabled_reason: AppDisabledReason | null, } };
3 changes: 2 additions & 1 deletion codex-rs/app-server-protocol/schema/typescript/v2/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import type { Verbosity } from "../Verbosity";
import type { WebSearchMode } from "../WebSearchMode";
import type { JsonValue } from "../serde_json/JsonValue";
import type { AnalyticsConfig } from "./AnalyticsConfig";
import type { AppsConfig } from "./AppsConfig";
import type { AskForApproval } from "./AskForApproval";
import type { ProfileV2 } from "./ProfileV2";
import type { SandboxMode } from "./SandboxMode";
import type { SandboxWorkspaceWrite } from "./SandboxWorkspaceWrite";
import type { ToolsV2 } from "./ToolsV2";

export type Config = { model: string | null, review_model: string | null, model_context_window: bigint | null, model_auto_compact_token_limit: bigint | null, model_provider: string | null, approval_policy: AskForApproval | null, sandbox_mode: SandboxMode | null, sandbox_workspace_write: SandboxWorkspaceWrite | null, forced_chatgpt_workspace_id: string | null, forced_login_method: ForcedLoginMethod | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, profile: string | null, profiles: { [key in string]?: ProfileV2 }, instructions: string | null, developer_instructions: string | null, compact_prompt: string | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, analytics: AnalyticsConfig | null, } & ({ [key in string]?: number | string | boolean | Array<JsonValue> | { [key in string]?: JsonValue } | null });
export type Config = { model: string | null, review_model: string | null, model_context_window: bigint | null, model_auto_compact_token_limit: bigint | null, model_provider: string | null, approval_policy: AskForApproval | null, sandbox_mode: SandboxMode | null, sandbox_workspace_write: SandboxWorkspaceWrite | null, forced_chatgpt_workspace_id: string | null, forced_login_method: ForcedLoginMethod | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, profile: string | null, profiles: { [key in string]?: ProfileV2 }, instructions: string | null, developer_instructions: string | null, compact_prompt: string | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, analytics: AnalyticsConfig | null, apps: AppsConfig | null, } & ({ [key in string]?: number | string | boolean | Array<JsonValue> | { [key in string]?: JsonValue } | null });
2 changes: 2 additions & 0 deletions codex-rs/app-server-protocol/schema/typescript/v2/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ export type { AccountRateLimitsUpdatedNotification } from "./AccountRateLimitsUp
export type { AccountUpdatedNotification } from "./AccountUpdatedNotification";
export type { AgentMessageDeltaNotification } from "./AgentMessageDeltaNotification";
export type { AnalyticsConfig } from "./AnalyticsConfig";
export type { AppDisabledReason } from "./AppDisabledReason";
export type { AppInfo } from "./AppInfo";
export type { AppsConfig } from "./AppsConfig";
export type { AppsListParams } from "./AppsListParams";
export type { AppsListResponse } from "./AppsListResponse";
export type { AskForApproval } from "./AskForApproval";
Expand Down
32 changes: 32 additions & 0 deletions codex-rs/app-server-protocol/src/protocol/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,36 @@ pub struct AnalyticsConfig {
pub additional: HashMap<String, JsonValue>,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "snake_case")]
#[ts(export_to = "v2/")]
pub enum AppDisabledReason {
Unknown,
User,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "snake_case")]
#[ts(export_to = "v2/")]
pub struct AppConfig {
#[serde(default = "default_enabled")]
pub enabled: bool,
pub disabled_reason: Option<AppDisabledReason>,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "snake_case")]
#[ts(export_to = "v2/")]
pub struct AppsConfig {
#[serde(default, flatten)]
#[schemars(with = "HashMap<String, AppConfig>")]
pub apps: HashMap<String, AppConfig>,
}

const fn default_enabled() -> bool {
true
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "snake_case")]
#[ts(export_to = "v2/")]
Expand All @@ -400,6 +430,8 @@ pub struct Config {
pub model_reasoning_summary: Option<ReasoningSummary>,
pub model_verbosity: Option<Verbosity>,
pub analytics: Option<AnalyticsConfig>,
#[serde(default)]
pub apps: Option<AppsConfig>,
#[serde(default, flatten)]
pub additional: HashMap<String, JsonValue>,
}
Expand Down
71 changes: 71 additions & 0 deletions codex-rs/app-server/tests/suite/v2/config_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ use app_test_support::McpProcess;
use app_test_support::test_path_buf_with_windows;
use app_test_support::test_tmp_path_buf;
use app_test_support::to_response;
use codex_app_server_protocol::AppConfig;
use codex_app_server_protocol::AppDisabledReason;
use codex_app_server_protocol::AppsConfig;
use codex_app_server_protocol::AskForApproval;
use codex_app_server_protocol::ConfigBatchWriteParams;
use codex_app_server_protocol::ConfigEdit;
Expand Down Expand Up @@ -146,6 +149,74 @@ view_image = false
Ok(())
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn config_read_includes_apps() -> Result<()> {
let codex_home = TempDir::new()?;
write_config(
&codex_home,
r#"
[apps.app1]
enabled = false
disabled_reason = "user"
"#,
)?;
let codex_home_path = codex_home.path().canonicalize()?;
let user_file = AbsolutePathBuf::try_from(codex_home_path.join("config.toml"))?;

let mut mcp = McpProcess::new(codex_home.path()).await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;

let request_id = mcp
.send_config_read_request(ConfigReadParams {
include_layers: true,
cwd: None,
})
.await?;
let resp: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
mcp.read_stream_until_response_message(RequestId::Integer(request_id)),
)
.await??;
let ConfigReadResponse {
config,
origins,
layers,
} = to_response(resp)?;

assert_eq!(
config.apps,
Some(AppsConfig {
apps: std::collections::HashMap::from([(
"app1".to_string(),
AppConfig {
enabled: false,
disabled_reason: Some(AppDisabledReason::User),
},
)]),
})
);
assert_eq!(
origins.get("apps.app1.enabled").expect("origin").name,
ConfigLayerSource::User {
file: user_file.clone(),
}
);
assert_eq!(
origins
.get("apps.app1.disabled_reason")
.expect("origin")
.name,
ConfigLayerSource::User {
file: user_file.clone(),
}
);

let layers = layers.expect("layers present");
assert_layers_user_then_optional_system(&layers, user_file)?;

Ok(())
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn config_read_includes_project_layers_for_cwd() -> Result<()> {
let codex_home = TempDir::new()?;
Expand Down
43 changes: 43 additions & 0 deletions codex-rs/core/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,40 @@
},
"type": "object"
},
"AppConfig": {
"additionalProperties": false,
"description": "Config values for a single app/connector.",
"properties": {
"disabled_reason": {
"allOf": [
{
"$ref": "#/definitions/AppDisabledReason"
}
],
"description": "Reason this app was disabled."
},
"enabled": {
"default": true,
"description": "When `false`, Codex does not surface this app.",
"type": "boolean"
}
},
"type": "object"
},
"AppDisabledReason": {
"enum": [
"unknown",
"user"
],
"type": "string"
},
"AppsConfigToml": {
"additionalProperties": {
"$ref": "#/definitions/AppConfig"
},
"description": "App/connector settings loaded from `config.toml`.",
"type": "object"
},
"AskForApproval": {
"description": "Determines the conditions under which the user is consulted to approve running the command proposed by Codex.",
"oneOf": [
Expand Down Expand Up @@ -1137,6 +1171,15 @@
],
"description": "Default approval policy for executing commands."
},
"apps": {
"allOf": [
{
"$ref": "#/definitions/AppsConfigToml"
}
],
"default": null,
"description": "Settings for app-specific controls."
},
"chatgpt_base_url": {
"description": "Base URL for requests to ChatGPT (as opposed to the OpenAI API).",
"type": "string"
Expand Down
5 changes: 5 additions & 0 deletions codex-rs/core/src/config/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::auth::AuthCredentialsStoreMode;
use crate::config::edit::ConfigEdit;
use crate::config::edit::ConfigEditsBuilder;
use crate::config::types::AppsConfigToml;
use crate::config::types::DEFAULT_OTEL_ENVIRONMENT;
use crate::config::types::History;
use crate::config::types::McpServerConfig;
Expand Down Expand Up @@ -985,6 +986,10 @@ pub struct ConfigToml {
/// Defaults to `true`.
pub feedback: Option<crate::config::types::FeedbackConfigToml>,

/// Settings for app-specific controls.
#[serde(default)]
pub apps: Option<AppsConfigToml>,

/// OTEL configuration.
pub otel: Option<crate::config::types::OtelConfigToml>,

Expand Down
Loading
Loading