mirror of
https://github.com/openai/codex.git
synced 2026-11-13 22:47:20 +00:00
Make runtime workspace roots absolute in app-server API (#26552)
Stacked on #26532. ## Why #26532 moves cwd normalization to the app-server/core boundary. `runtimeWorkspaceRoots` still accepted raw paths in v2 requests and in `ConfigOverrides`, which left core responsible for interpreting those roots later. This makes runtime workspace roots follow the same absolute-path boundary as cwd. ## What - Change v2 `runtimeWorkspaceRoots` request fields for `thread/start`, `thread/resume`, `thread/fork`, and `turn/start` to `AbsolutePathBuf`. - Deduplicate already-absolute runtime roots in app-server handlers and pass them through `ConfigOverrides.workspace_roots` as `AbsolutePathBuf`. - Update TUI and exec client request builders to pass absolute runtime roots directly. - Update app-server docs, schema fixtures, and focused tests for absolute runtime roots. ## Testing - `just test -p codex-app-server-protocol` - `just test -p codex-app-server runtime_workspace_roots` - `just test -p codex-core session_permission_profile_rebinds_runtime_workspace_roots` - `just test -p codex-tui app_server_session` - `just test -p codex-exec`
This commit is contained in:
1 parent
8d72fb6de9
commit
76c0a5379c
14 files changed
+73
-148
No files matched your search
@@ -56,17 +56,7 @@ fn collect_resume_override_mismatches(
|
||||
}
|
||||
}
|
||||
if let Some(requested_runtime_workspace_roots) = request.runtime_workspace_roots.as_ref() {
|
||||
let base_cwd = request
|
||||
.cwd
|
||||
.as_deref()
|
||||
.map(|cwd| {
|
||||
AbsolutePathBuf::resolve_path_against_base(cwd, config_snapshot.cwd.as_path())
|
||||
})
|
||||
.unwrap_or_else(|| config_snapshot.cwd.clone());
|
||||
let requested_runtime_workspace_roots = requested_runtime_workspace_roots
|
||||
.iter()
|
||||
.map(|path| AbsolutePathBuf::resolve_path_against_base(path, base_cwd.as_path()))
|
||||
.collect::<Vec<_>>();
|
||||
let requested_runtime_workspace_roots = requested_runtime_workspace_roots.to_vec();
|
||||
if requested_runtime_workspace_roots != config_snapshot.workspace_roots {
|
||||
mismatch_details.push(format!(
|
||||
"runtime_workspace_roots requested={requested_runtime_workspace_roots:?} active={:?}",
|
||||
@@ -846,6 +836,7 @@ impl ThreadRequestProcessor {
|
||||
));
|
||||
}
|
||||
let environment_selections = self.parse_environment_selections(environments)?;
|
||||
let runtime_workspace_roots = runtime_workspace_roots.map(resolve_runtime_workspace_roots);
|
||||
let mut typesafe_overrides = self.build_thread_config_overrides(
|
||||
model,
|
||||
model_provider,
|
||||
@@ -1215,7 +1206,7 @@ impl ThreadRequestProcessor {
|
||||
model_provider: Option<String>,
|
||||
service_tier: Option<Option<String>>,
|
||||
cwd: Option<String>,
|
||||
runtime_workspace_roots: Option<Vec<PathBuf>>,
|
||||
runtime_workspace_roots: Option<Vec<AbsolutePathBuf>>,
|
||||
approval_policy: Option<codex_app_server_protocol::AskForApproval>,
|
||||
approvals_reviewer: Option<codex_app_server_protocol::ApprovalsReviewer>,
|
||||
sandbox: Option<SandboxMode>,
|
||||
@@ -2483,6 +2474,7 @@ impl ThreadRequestProcessor {
|
||||
};
|
||||
|
||||
let history_cwd = thread_history.session_cwd();
|
||||
let runtime_workspace_roots = runtime_workspace_roots.map(resolve_runtime_workspace_roots);
|
||||
let mut typesafe_overrides = self.build_thread_config_overrides(
|
||||
model,
|
||||
model_provider,
|
||||
@@ -3201,6 +3193,7 @@ impl ThreadRequestProcessor {
|
||||
} else {
|
||||
Some(cli_overrides)
|
||||
};
|
||||
let runtime_workspace_roots = runtime_workspace_roots.map(resolve_runtime_workspace_roots);
|
||||
let mut typesafe_overrides = self.build_thread_config_overrides(
|
||||
model,
|
||||
model_provider,
|
||||
|
||||
@@ -18,28 +18,6 @@ pub(crate) struct TurnRequestProcessor {
|
||||
skills_watcher: Arc<SkillsWatcher>,
|
||||
}
|
||||
|
||||
fn resolve_runtime_workspace_roots(
|
||||
workspace_roots: Vec<PathBuf>,
|
||||
base_cwd: &AbsolutePathBuf,
|
||||
) -> Vec<AbsolutePathBuf> {
|
||||
let mut resolved_roots = Vec::new();
|
||||
for path in workspace_roots {
|
||||
let root = AbsolutePathBuf::resolve_path_against_base(path, base_cwd.as_path());
|
||||
if !resolved_roots.iter().any(|existing| existing == &root) {
|
||||
resolved_roots.push(root);
|
||||
}
|
||||
}
|
||||
resolved_roots
|
||||
}
|
||||
|
||||
fn resolve_request_cwd(cwd: Option<PathBuf>) -> Result<Option<AbsolutePathBuf>, JSONRPCErrorError> {
|
||||
cwd.map(|cwd| {
|
||||
AbsolutePathBuf::relative_to_current_dir(path_utils::normalize_for_native_workdir(cwd))
|
||||
.map_err(|err| invalid_request(format!("invalid cwd: {err}")))
|
||||
})
|
||||
.transpose()
|
||||
}
|
||||
|
||||
fn map_additional_context(
|
||||
additional_context: Option<HashMap<String, AdditionalContextEntry>>,
|
||||
) -> BTreeMap<String, CoreAdditionalContextEntry> {
|
||||
@@ -66,7 +44,7 @@ fn map_additional_context(
|
||||
struct ThreadSettingsBuildParams {
|
||||
method: &'static str,
|
||||
cwd: Option<AbsolutePathBuf>,
|
||||
runtime_workspace_roots: Option<Vec<PathBuf>>,
|
||||
runtime_workspace_roots: Option<Vec<AbsolutePathBuf>>,
|
||||
approval_policy: Option<codex_app_server_protocol::AskForApproval>,
|
||||
approvals_reviewer: Option<codex_app_server_protocol::ApprovalsReviewer>,
|
||||
sandbox_policy: Option<codex_app_server_protocol::SandboxPolicy>,
|
||||
@@ -533,7 +511,7 @@ impl TurnRequestProcessor {
|
||||
// `thread/settings/update` only acknowledges that the update was queued.
|
||||
// Clients that send dependent partial updates should wait for
|
||||
// `thread/settings/updated` or combine the fields in one request.
|
||||
let snapshot = if permissions.is_some() || runtime_workspace_roots_request.is_some() {
|
||||
let snapshot = if permissions.is_some() {
|
||||
Some(thread.config_snapshot().await)
|
||||
} else {
|
||||
None
|
||||
@@ -552,22 +530,8 @@ impl TurnRequestProcessor {
|
||||
|| collaboration_mode.is_some()
|
||||
|| personality.is_some();
|
||||
|
||||
let runtime_workspace_roots = if let Some(workspace_roots) =
|
||||
runtime_workspace_roots_request.clone()
|
||||
{
|
||||
let Some(snapshot) = snapshot.as_ref() else {
|
||||
return Err(internal_error(format!(
|
||||
"{method} runtime workspace roots missing thread snapshot"
|
||||
)));
|
||||
};
|
||||
let base_cwd = cwd
|
||||
.as_ref()
|
||||
.map(|cwd| AbsolutePathBuf::resolve_path_against_base(cwd, snapshot.cwd.as_path()))
|
||||
.unwrap_or_else(|| snapshot.cwd.clone());
|
||||
Some(resolve_runtime_workspace_roots(workspace_roots, &base_cwd))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let runtime_workspace_roots =
|
||||
runtime_workspace_roots_request.map(resolve_runtime_workspace_roots);
|
||||
let approval_policy =
|
||||
approval_policy.map(codex_app_server_protocol::AskForApproval::to_core);
|
||||
let approvals_reviewer =
|
||||
@@ -582,15 +546,11 @@ impl TurnRequestProcessor {
|
||||
};
|
||||
let overrides = ConfigOverrides {
|
||||
cwd: cwd.as_ref().map(AbsolutePathBuf::to_path_buf),
|
||||
workspace_roots: Some(runtime_workspace_roots_request.clone().unwrap_or_else(
|
||||
|| {
|
||||
snapshot
|
||||
.workspace_roots
|
||||
.iter()
|
||||
.map(AbsolutePathBuf::to_path_buf)
|
||||
.collect()
|
||||
},
|
||||
)),
|
||||
workspace_roots: Some(
|
||||
runtime_workspace_roots
|
||||
.clone()
|
||||
.unwrap_or_else(|| snapshot.workspace_roots.clone()),
|
||||
),
|
||||
default_permissions: Some(permissions),
|
||||
codex_linux_sandbox_exe: self.arg0_paths.codex_linux_sandbox_exe.clone(),
|
||||
main_execve_wrapper_exe: self.arg0_paths.main_execve_wrapper_exe.clone(),
|
||||
|
||||
Reference in new issue
Block a user