Skip to content
Draft
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
7 changes: 3 additions & 4 deletions codex-rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion codex-rs/app-server-protocol/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ workspace = true
anyhow = { workspace = true }
clap = { workspace = true, features = ["derive"] }
codex-experimental-api-macros = { workspace = true }
codex-git-utils = { workspace = true }
codex-protocol = { workspace = true }
codex-shell-command = { workspace = true }
codex-utils-absolute-path = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion codex-rs/app-server-protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ mod jsonrpc_lite;
mod protocol;
mod schema_fixtures;

pub use codex_git_utils::GitSha;
pub use experimental_api::*;
pub use export::GenerateTsOptions;
pub use export::generate_internal_json_schema;
Expand All @@ -30,6 +29,7 @@ pub use protocol::v1::GetConversationSummaryParams;
pub use protocol::v1::GetConversationSummaryResponse;
pub use protocol::v1::GitDiffToRemoteParams;
pub use protocol::v1::GitDiffToRemoteResponse;
pub use protocol::v1::GitSha;
pub use protocol::v1::InitializeCapabilities;
pub use protocol::v1::InitializeParams;
pub use protocol::v1::InitializeResponse;
Expand Down
2 changes: 1 addition & 1 deletion codex-rs/app-server-protocol/src/protocol/v1.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::collections::HashMap;
use std::path::PathBuf;

use codex_git_utils::GitSha;
use codex_protocol::ThreadId;
use codex_protocol::config_types::ForcedLoginMethod;
use codex_protocol::config_types::ReasoningSummary;
Expand All @@ -11,6 +10,7 @@ use codex_protocol::openai_models::ReasoningEffort;
use codex_protocol::parse_command::ParsedCommand;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::FileChange;
pub use codex_protocol::protocol::GitSha;
use codex_protocol::protocol::ReviewDecision;
use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::protocol::SessionSource;
Expand Down
97 changes: 67 additions & 30 deletions codex-rs/app-server/src/codex_message_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,9 @@ use codex_core::config::edit::ConfigEditsBuilder;
use codex_core::config_loader::CloudRequirementsLoadError;
use codex_core::config_loader::CloudRequirementsLoadErrorCode;
use codex_core::config_loader::CloudRequirementsLoader;
use codex_core::config_loader::ConfigLoadFileSystems;
use codex_core::config_loader::LoaderOverrides;
use codex_core::config_loader::load_config_layers_state;
use codex_core::config_loader::load_config_layers_state_with_file_systems;
use codex_core::config_loader::project_trust_key;
use codex_core::exec::ExecCapturePolicy;
use codex_core::exec::ExecExpiration;
Expand Down Expand Up @@ -251,6 +252,7 @@ use codex_core_plugins::loader::load_plugin_mcp_servers;
use codex_core_plugins::manifest::PluginManifestInterface;
use codex_core_plugins::marketplace::MarketplaceError;
use codex_core_plugins::marketplace::MarketplacePluginSource;
use codex_exec_server::ExecutorFileSystem;
use codex_exec_server::LOCAL_FS;
use codex_features::FEATURES;
use codex_features::Feature;
Expand Down Expand Up @@ -621,9 +623,25 @@ pub(crate) struct CodexMessageProcessorArgs {
}

impl CodexMessageProcessor {
async fn instruction_sources_from_config(config: &Config) -> Vec<AbsolutePathBuf> {
async fn thread_filesystem_or_local(
thread_manager: &ThreadManager,
) -> Arc<dyn ExecutorFileSystem> {
match thread_manager.environment_manager().current().await {
Ok(Some(environment)) => environment.get_filesystem(),
Ok(None) => Arc::clone(&LOCAL_FS),
Err(err) => {
warn!("failed to get current environment filesystem: {err}");
Arc::clone(&LOCAL_FS)
}
}
}

async fn instruction_sources_from_config(
config: &Config,
fs: Arc<dyn ExecutorFileSystem>,
) -> Vec<AbsolutePathBuf> {
codex_core::AgentsMdManager::new(config)
.instruction_sources(LOCAL_FS.as_ref())
.instruction_sources(fs.as_ref())
.await
}

Expand Down Expand Up @@ -2417,6 +2435,8 @@ impl CodexMessageProcessor {
return;
}
};
let thread_fs =
Self::thread_filesystem_or_local(&listener_task_context.thread_manager).await;

// The user may have requested WorkspaceWrite or DangerFullAccess via
// the command line, though in the process of deriving the Config, it
Expand All @@ -2441,9 +2461,9 @@ impl CodexMessageProcessor {
| codex_protocol::protocol::SandboxPolicy::ExternalSandbox { .. }
))
{
let trust_target = resolve_root_git_project_for_trust(config.cwd.as_path())
let trust_target = resolve_root_git_project_for_trust(thread_fs.as_ref(), &config.cwd)
.await
.unwrap_or_else(|| config.cwd.to_path_buf());
.unwrap_or_else(|| config.cwd.clone());
let cli_overrides_with_trust;
let cli_overrides_for_reload = if let Err(err) =
codex_core::config::set_project_trust_level(
Expand Down Expand Up @@ -2500,7 +2520,8 @@ impl CodexMessageProcessor {
};
}

let instruction_sources = Self::instruction_sources_from_config(&config).await;
let instruction_sources =
Self::instruction_sources_from_config(&config, thread_fs.clone()).await;
let dynamic_tools = dynamic_tools.unwrap_or_default();
let core_dynamic_tools = if dynamic_tools.is_empty() {
Vec::new()
Expand Down Expand Up @@ -4092,7 +4113,9 @@ impl CodexMessageProcessor {
};

let fallback_model_provider = config.model_provider_id.clone();
let instruction_sources = Self::instruction_sources_from_config(&config).await;
let instruction_sources_fs = Self::thread_filesystem_or_local(&self.thread_manager).await;
let instruction_sources =
Self::instruction_sources_from_config(&config, instruction_sources_fs).await;
let response_history = thread_history.clone();

match self
Expand Down Expand Up @@ -4357,8 +4380,13 @@ impl CodexMessageProcessor {
}
let mut config_for_instruction_sources = self.config.as_ref().clone();
config_for_instruction_sources.cwd = config_snapshot.cwd.clone();
let instruction_sources =
Self::instruction_sources_from_config(&config_for_instruction_sources).await;
let instruction_sources_fs =
Self::thread_filesystem_or_local(&self.thread_manager).await;
let instruction_sources = Self::instruction_sources_from_config(
&config_for_instruction_sources,
instruction_sources_fs,
)
.await;
let thread_summary = match load_thread_summary_for_rollout(
&self.config,
existing_thread_id,
Expand Down Expand Up @@ -4672,7 +4700,9 @@ impl CodexMessageProcessor {
};

let fallback_model_provider = config.model_provider_id.clone();
let instruction_sources = Self::instruction_sources_from_config(&config).await;
let instruction_sources_fs = Self::thread_filesystem_or_local(&self.thread_manager).await;
let instruction_sources =
Self::instruction_sources_from_config(&config, instruction_sources_fs).await;

let NewThread {
thread_id,
Expand Down Expand Up @@ -6116,30 +6146,36 @@ impl CodexMessageProcessor {
};
let skills_manager = self.thread_manager.skills_manager();
let plugins_manager = self.thread_manager.plugins_manager();
let fs = match self.thread_manager.environment_manager().current().await {
Ok(Some(environment)) => Some(environment.get_filesystem()),
Ok(None) => None,
Err(err) => {
self.outgoing
.send_error(
request_id,
JSONRPCErrorError {
code: INTERNAL_ERROR_CODE,
message: format!("failed to create environment: {err}"),
data: None,
},
)
.await;
return;
}
};
let (fs, remote_project_fs) =
match self.thread_manager.environment_manager().current().await {
Ok(Some(environment)) => (environment.get_filesystem(), environment.is_remote()),
Ok(None) => (Arc::clone(&LOCAL_FS), false),
Err(err) => {
self.outgoing
.send_error(
request_id,
JSONRPCErrorError {
code: INTERNAL_ERROR_CODE,
message: format!("failed to create environment: {err}"),
data: None,
},
)
.await;
return;
}
};
let cli_overrides = self.current_cli_overrides();
let mut data = Vec::new();
for cwd in cwds {
let extra_roots = extra_roots_by_cwd
.get(&cwd)
.map_or(&[][..], std::vec::Vec::as_slice);
let cwd_abs = match AbsolutePathBuf::relative_to_current_dir(cwd.as_path()) {
let cwd_abs = if remote_project_fs {
AbsolutePathBuf::from_absolute_path_checked(cwd.as_path())
} else {
AbsolutePathBuf::relative_to_current_dir(cwd.as_path())
};
let cwd_abs = match cwd_abs {
Ok(path) => path,
Err(err) => {
let error_path = cwd.clone();
Expand All @@ -6154,7 +6190,8 @@ impl CodexMessageProcessor {
continue;
}
};
let config_layer_stack = match load_config_layers_state(
let config_layer_stack = match load_config_layers_state_with_file_systems(
ConfigLoadFileSystems::same(fs.as_ref()),
&self.config.codex_home,
Some(cwd_abs.clone()),
&cli_overrides,
Expand Down Expand Up @@ -6194,7 +6231,7 @@ impl CodexMessageProcessor {
&skills_input,
force_reload,
extra_roots,
fs.clone(),
Some(Arc::clone(&fs)),
)
.await;
let errors = errors_to_info(&outcome.errors);
Expand Down
14 changes: 9 additions & 5 deletions codex-rs/app-server/tests/suite/v2/thread_start.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use anyhow::Result;
use app_test_support::ChatGptAuthFixture;
use app_test_support::McpProcess;
use app_test_support::PathBufExt;
use app_test_support::create_mock_responses_server_repeating_assistant;
use app_test_support::to_response;
use app_test_support::write_chatgpt_auth;
Expand All @@ -20,6 +21,7 @@ use codex_app_server_protocol::ThreadStatus;
use codex_app_server_protocol::ThreadStatusChangedNotification;
use codex_config::types::AuthCredentialsStoreMode;
use codex_core::config::set_project_trust_level;
use codex_exec_server::LOCAL_FS;
use codex_git_utils::resolve_root_git_project_for_trust;
use codex_login::REFRESH_TOKEN_URL_OVERRIDE_ENV_VAR;
use codex_protocol::config_types::ServiceTier;
Expand Down Expand Up @@ -716,10 +718,11 @@ model_reasoning_effort = "high"
assert_eq!(reasoning_effort, Some(ReasoningEffort::High));

let config_toml = std::fs::read_to_string(codex_home.path().join("config.toml"))?;
let trusted_root = resolve_root_git_project_for_trust(workspace.path())
let workspace_abs = workspace.path().to_path_buf().abs();
let trusted_root = resolve_root_git_project_for_trust(LOCAL_FS.as_ref(), &workspace_abs)
.await
.unwrap_or_else(|| workspace.path().to_path_buf());
assert!(config_toml.contains(&persisted_trust_path(&trusted_root)));
.unwrap_or(workspace_abs);
assert!(config_toml.contains(&persisted_trust_path(trusted_root.as_path())));
assert!(config_toml.contains("trust_level = \"trusted\""));

Ok(())
Expand Down Expand Up @@ -754,10 +757,11 @@ async fn thread_start_with_nested_git_cwd_trusts_repo_root() -> Result<()> {
.await??;

let config_toml = std::fs::read_to_string(codex_home.path().join("config.toml"))?;
let trusted_root = resolve_root_git_project_for_trust(&nested)
let nested_abs = nested.abs();
let trusted_root = resolve_root_git_project_for_trust(LOCAL_FS.as_ref(), &nested_abs)
.await
.expect("git root should resolve");
assert!(config_toml.contains(&persisted_trust_path(&trusted_root)));
assert!(config_toml.contains(&persisted_trust_path(trusted_root.as_path())));
assert!(!config_toml.contains(&persisted_trust_path(&nested)));

Ok(())
Expand Down
19 changes: 9 additions & 10 deletions codex-rs/apply-patch/src/invocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::path::Path;
use std::sync::LazyLock;

use codex_exec_server::ExecutorFileSystem;
use codex_exec_server::ExecutorPathRef;
use codex_utils_absolute_path::AbsolutePathBuf;
use tree_sitter::Parser;
use tree_sitter::Query;
Expand All @@ -20,7 +21,7 @@ use crate::MaybeApplyPatchVerified;
use crate::parser::Hunk;
use crate::parser::ParseError;
use crate::parser::parse_patch;
use crate::unified_diff_from_chunks;
use crate::unified_diff_from_chunks_at;
use std::str::Utf8Error;
use tree_sitter::LanguageError;

Expand Down Expand Up @@ -162,16 +163,16 @@ pub async fn maybe_parse_apply_patch_verified(
.unwrap_or_else(|| cwd.clone());
let mut changes = HashMap::new();
for hunk in hunks {
let path = hunk.resolve_path(&effective_cwd);
let path = ExecutorPathRef::new(fs, hunk.resolve_path(&effective_cwd));
match hunk {
Hunk::AddFile { contents, .. } => {
changes.insert(
path.into_path_buf(),
path.to_path_buf(),
ApplyPatchFileChange::Add { content: contents },
);
}
Hunk::DeleteFile { .. } => {
let content = match fs.read_file_text(&path, sandbox).await {
let content = match path.with_sandbox(sandbox).read_file_text().await {
Ok(content) => content,
Err(e) => {
return MaybeApplyPatchVerified::CorrectnessError(
Expand All @@ -182,25 +183,23 @@ pub async fn maybe_parse_apply_patch_verified(
);
}
};
changes.insert(
path.into_path_buf(),
ApplyPatchFileChange::Delete { content },
);
changes
.insert(path.to_path_buf(), ApplyPatchFileChange::Delete { content });
}
Hunk::UpdateFile {
move_path, chunks, ..
} => {
let ApplyPatchFileUpdate {
unified_diff,
content: contents,
} = match unified_diff_from_chunks(&path, &chunks, fs, sandbox).await {
} = match unified_diff_from_chunks_at(&path, &chunks, sandbox).await {
Ok(diff) => diff,
Err(e) => {
return MaybeApplyPatchVerified::CorrectnessError(e);
}
};
changes.insert(
path.into_path_buf(),
path.to_path_buf(),
ApplyPatchFileChange::Update {
unified_diff,
move_path: move_path.map(|p| effective_cwd.join(p).into_path_buf()),
Expand Down
Loading
Loading