Skip to content

Commit 4c088dd

Browse files
Merge branch 'main' into drifkin/ollama-oss-responses
2 parents e643e36 + 7157421 commit 4c088dd

File tree

94 files changed

+3476
-1952
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+3476
-1952
lines changed

codex-rs/Cargo.lock

Lines changed: 23 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

codex-rs/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ icu_locale_core = "2.1"
141141
icu_provider = { version = "2.1", features = ["sync"] }
142142
ignore = "0.4.23"
143143
image = { version = "^0.25.9", default-features = false }
144+
include_dir = "0.7.4"
144145
indexmap = "2.12.0"
145146
insta = "1.44.3"
146147
itertools = "0.14.0"

codex-rs/app-server-protocol/src/protocol/v2.rs

Lines changed: 85 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ v2_enum_from_core!(
208208
}
209209
);
210210

211+
// TODO(mbolin): Support in-repo layer.
211212
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
212213
#[serde(tag = "type", rename_all = "camelCase")]
213214
#[ts(tag = "type")]
@@ -216,17 +217,63 @@ pub enum ConfigLayerSource {
216217
/// Managed preferences layer delivered by MDM (macOS only).
217218
#[serde(rename_all = "camelCase")]
218219
#[ts(rename_all = "camelCase")]
219-
Mdm { domain: String, key: String },
220+
Mdm {
221+
domain: String,
222+
key: String,
223+
},
224+
220225
/// Managed config layer from a file (usually `managed_config.toml`).
221226
#[serde(rename_all = "camelCase")]
222227
#[ts(rename_all = "camelCase")]
223-
System { file: AbsolutePathBuf },
224-
/// Session-layer overrides supplied via `-c`/`--config`.
225-
SessionFlags,
226-
/// User config layer from a file (usually `config.toml`).
228+
System {
229+
file: AbsolutePathBuf,
230+
},
231+
232+
/// User config layer from $CODEX_HOME/config.toml. This layer is special
233+
/// in that it is expected to be:
234+
/// - writable by the user
235+
/// - generally outside the workspace directory
227236
#[serde(rename_all = "camelCase")]
228237
#[ts(rename_all = "camelCase")]
229-
User { file: AbsolutePathBuf },
238+
User {
239+
file: AbsolutePathBuf,
240+
},
241+
242+
/// Session-layer overrides supplied via `-c`/`--config`.
243+
SessionFlags,
244+
245+
/// `managed_config.toml` was designed to be a config that was loaded
246+
/// as the last layer on top of everything else. This scheme did not quite
247+
/// work out as intended, but we keep this variant as a "best effort" while
248+
/// we phase out `managed_config.toml` in favor of `requirements.toml`.
249+
LegacyManagedConfigTomlFromFile {
250+
file: AbsolutePathBuf,
251+
},
252+
253+
LegacyManagedConfigTomlFromMdm,
254+
}
255+
256+
impl ConfigLayerSource {
257+
/// A settings from a layer with a higher precedence will override a setting
258+
/// from a layer with a lower precedence.
259+
pub fn precedence(&self) -> i16 {
260+
match self {
261+
ConfigLayerSource::Mdm { .. } => 0,
262+
ConfigLayerSource::System { .. } => 10,
263+
ConfigLayerSource::User { .. } => 20,
264+
ConfigLayerSource::SessionFlags => 30,
265+
ConfigLayerSource::LegacyManagedConfigTomlFromFile { .. } => 40,
266+
ConfigLayerSource::LegacyManagedConfigTomlFromMdm => 50,
267+
}
268+
}
269+
}
270+
271+
/// Compares [ConfigLayerSource] by precedence, so `A < B` means settings from
272+
/// layer `A` will be overridden by settings from layer `B`.
273+
impl PartialOrd for ConfigLayerSource {
274+
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
275+
Some(self.precedence().cmp(&other.precedence()))
276+
}
230277
}
231278

232279
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]
@@ -344,7 +391,7 @@ pub struct ConfigWriteResponse {
344391
pub status: WriteStatus,
345392
pub version: String,
346393
/// Canonical path to the config file that was written.
347-
pub file_path: String,
394+
pub file_path: AbsolutePathBuf,
348395
pub overridden_metadata: Option<OverriddenMetadata>,
349396
}
350397

@@ -357,6 +404,7 @@ pub enum ConfigWriteErrorCode {
357404
ConfigValidationError,
358405
ConfigPathNotFound,
359406
ConfigSchemaUnknownKey,
407+
UserLayerNotFound,
360408
}
361409

362410
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
@@ -980,6 +1028,10 @@ pub struct SkillsListParams {
9801028
/// When empty, defaults to the current session working directory.
9811029
#[serde(default, skip_serializing_if = "Vec::is_empty")]
9821030
pub cwds: Vec<PathBuf>,
1031+
1032+
/// When true, bypass the skills cache and re-scan skills from disk.
1033+
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
1034+
pub force_reload: bool,
9831035
}
9841036

9851037
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
@@ -996,7 +1048,7 @@ pub struct SkillsListResponse {
9961048
pub enum SkillScope {
9971049
User,
9981050
Repo,
999-
Public,
1051+
System,
10001052
}
10011053

10021054
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
@@ -1042,7 +1094,7 @@ impl From<CoreSkillScope> for SkillScope {
10421094
match value {
10431095
CoreSkillScope::User => Self::User,
10441096
CoreSkillScope::Repo => Self::Repo,
1045-
CoreSkillScope::Public => Self::Public,
1097+
CoreSkillScope::System => Self::System,
10461098
}
10471099
}
10481100
}
@@ -1939,6 +1991,30 @@ mod tests {
19391991
);
19401992
}
19411993

1994+
#[test]
1995+
fn skills_list_params_serialization_uses_force_reload() {
1996+
assert_eq!(
1997+
serde_json::to_value(SkillsListParams {
1998+
cwds: Vec::new(),
1999+
force_reload: false,
2000+
})
2001+
.unwrap(),
2002+
json!({}),
2003+
);
2004+
2005+
assert_eq!(
2006+
serde_json::to_value(SkillsListParams {
2007+
cwds: vec![PathBuf::from("/repo")],
2008+
force_reload: true,
2009+
})
2010+
.unwrap(),
2011+
json!({
2012+
"cwds": ["/repo"],
2013+
"forceReload": true,
2014+
}),
2015+
);
2016+
}
2017+
19422018
#[test]
19432019
fn codex_error_info_serializes_http_status_code_in_camel_case() {
19442020
let value = CodexErrorInfo::ResponseTooManyFailedAttempts {

codex-rs/app-server/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ Example (from OpenAI's official VSCode extension):
7777
- `review/start` — kick off Codex’s automated reviewer for a thread; responds like `turn/start` and emits `item/started`/`item/completed` notifications with `enteredReviewMode` and `exitedReviewMode` items, plus a final assistant `agentMessage` containing the review.
7878
- `command/exec` — run a single command under the server sandbox without starting a thread/turn (handy for utilities and validation).
7979
- `model/list` — list available models (with reasoning effort options).
80-
- `skills/list` — list skills for one or more `cwd` values.
80+
- `skills/list` — list skills for one or more `cwd` values (optional `forceReload`).
8181
- `mcpServer/oauth/login` — start an OAuth login for a configured MCP server; returns an `authorization_url` and later emits `mcpServer/oauthLogin/completed` once the browser flow finishes.
8282
- `mcpServerStatus/list` — enumerate configured MCP servers with their tools, resources, resource templates, and auth status; supports cursor+limit pagination.
8383
- `feedback/upload` — submit a feedback report (classification + optional reason/logs and conversation_id); returns the tracking thread id.

codex-rs/app-server/src/codex_message_processor.rs

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ impl CodexMessageProcessor {
282282
}
283283

284284
async fn load_latest_config(&self) -> Result<Config, JSONRPCErrorError> {
285-
Config::load_with_cli_overrides(self.cli_overrides.clone(), ConfigOverrides::default())
285+
Config::load_with_cli_overrides(self.cli_overrides.clone())
286286
.await
287287
.map_err(|err| JSONRPCErrorError {
288288
code: INTERNAL_ERROR_CODE,
@@ -2640,36 +2640,27 @@ impl CodexMessageProcessor {
26402640
}
26412641

26422642
async fn skills_list(&self, request_id: RequestId, params: SkillsListParams) {
2643-
let SkillsListParams { cwds } = params;
2643+
let SkillsListParams { cwds, force_reload } = params;
26442644
let cwds = if cwds.is_empty() {
26452645
vec![self.config.cwd.clone()]
26462646
} else {
26472647
cwds
26482648
};
26492649

2650-
let data = if self.config.features.enabled(Feature::Skills) {
2651-
let skills_manager = self.conversation_manager.skills_manager();
2652-
cwds.into_iter()
2653-
.map(|cwd| {
2654-
let outcome = skills_manager.skills_for_cwd(&cwd);
2655-
let errors = errors_to_info(&outcome.errors);
2656-
let skills = skills_to_info(&outcome.skills);
2657-
codex_app_server_protocol::SkillsListEntry {
2658-
cwd,
2659-
skills,
2660-
errors,
2661-
}
2662-
})
2663-
.collect()
2664-
} else {
2665-
cwds.into_iter()
2666-
.map(|cwd| codex_app_server_protocol::SkillsListEntry {
2650+
let skills_manager = self.conversation_manager.skills_manager();
2651+
let data = cwds
2652+
.into_iter()
2653+
.map(|cwd| {
2654+
let outcome = skills_manager.skills_for_cwd_with_options(&cwd, force_reload);
2655+
let errors = errors_to_info(&outcome.errors);
2656+
let skills = skills_to_info(&outcome.skills);
2657+
codex_app_server_protocol::SkillsListEntry {
26672658
cwd,
2668-
skills: Vec::new(),
2669-
errors: Vec::new(),
2670-
})
2671-
.collect()
2672-
};
2659+
skills,
2660+
errors,
2661+
}
2662+
})
2663+
.collect();
26732664
self.outgoing
26742665
.send_response(request_id, SkillsListResponse { data })
26752666
.await;
@@ -3348,15 +3339,15 @@ fn errors_to_info(
33483339

33493340
async fn derive_config_from_params(
33503341
overrides: ConfigOverrides,
3351-
cli_overrides: Option<std::collections::HashMap<String, serde_json::Value>>,
3342+
cli_overrides: Option<HashMap<String, serde_json::Value>>,
33523343
) -> std::io::Result<Config> {
33533344
let cli_overrides = cli_overrides
33543345
.unwrap_or_default()
33553346
.into_iter()
33563347
.map(|(k, v)| (k, json_to_toml(v)))
33573348
.collect();
33583349

3359-
Config::load_with_cli_overrides(cli_overrides, overrides).await
3350+
Config::load_with_cli_overrides_and_harness_overrides(cli_overrides, overrides).await
33603351
}
33613352

33623353
async fn read_summary_from_rollout(

codex-rs/app-server/src/lib.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
use codex_common::CliConfigOverrides;
44
use codex_core::config::Config;
5-
use codex_core::config::ConfigOverrides;
65
use std::io::ErrorKind;
76
use std::io::Result as IoResult;
87
use std::path::PathBuf;
@@ -81,12 +80,11 @@ pub async fn run_main(
8180
format!("error parsing -c overrides: {e}"),
8281
)
8382
})?;
84-
let config =
85-
Config::load_with_cli_overrides(cli_kv_overrides.clone(), ConfigOverrides::default())
86-
.await
87-
.map_err(|e| {
88-
std::io::Error::new(ErrorKind::InvalidData, format!("error loading config: {e}"))
89-
})?;
83+
let config = Config::load_with_cli_overrides(cli_kv_overrides.clone())
84+
.await
85+
.map_err(|e| {
86+
std::io::Error::new(ErrorKind::InvalidData, format!("error loading config: {e}"))
87+
})?;
9088

9189
let feedback = CodexFeedback::new();
9290

codex-rs/app-server/tests/common/models_cache.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ fn preset_to_info(preset: &ModelPreset, priority: i32) -> ModelInfo {
4242
}
4343
}
4444

45+
// todo(aibrahim): fix the priorities to be the opposite here.
4546
/// Write a models_cache.json file to the codex home directory.
4647
/// This prevents ModelsManager from making network requests to refresh models.
4748
/// The cache will be treated as fresh (within TTL) and used instead of fetching from the network.

0 commit comments

Comments
 (0)