mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08: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:
committed by
GitHub
Unverified
parent
8d72fb6de9
commit
76c0a5379c
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"definitions": {
|
||||
"AbsolutePathBuf": {
|
||||
"description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
|
||||
"type": "string"
|
||||
},
|
||||
"ApprovalsReviewer": {
|
||||
"description": "Configures who approval requests are routed to for review. Examples include sandbox escapes, blocked network access, MCP approval prompts, and ARC escalations. Defaults to `user`. `auto_review` uses a carefully prompted subagent to gather relevant context and apply a risk-based decision framework before approving or denying the request. The legacy value `guardian_subagent` is accepted for compatibility.",
|
||||
"enum": [
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"definitions": {
|
||||
"AbsolutePathBuf": {
|
||||
"description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
|
||||
"type": "string"
|
||||
},
|
||||
"AgentMessageInputContent": {
|
||||
"oneOf": [
|
||||
{
|
||||
|
||||
@@ -107,11 +107,10 @@ pub struct ThreadStartParams {
|
||||
pub service_tier: Option<Option<String>>,
|
||||
#[ts(optional = nullable)]
|
||||
pub cwd: Option<String>,
|
||||
/// Replace the thread's runtime workspace roots. Relative paths are
|
||||
/// resolved against the effective cwd for the thread.
|
||||
/// Replace the thread's runtime workspace roots. Paths must be absolute.
|
||||
#[experimental("thread/start.runtimeWorkspaceRoots")]
|
||||
#[ts(optional = nullable)]
|
||||
pub runtime_workspace_roots: Option<Vec<PathBuf>>,
|
||||
pub runtime_workspace_roots: Option<Vec<AbsolutePathBuf>>,
|
||||
#[experimental(nested)]
|
||||
#[ts(optional = nullable)]
|
||||
pub approval_policy: Option<AskForApproval>,
|
||||
@@ -358,11 +357,10 @@ pub struct ThreadResumeParams {
|
||||
pub service_tier: Option<Option<String>>,
|
||||
#[ts(optional = nullable)]
|
||||
pub cwd: Option<String>,
|
||||
/// Replace the thread's runtime workspace roots. Relative paths are
|
||||
/// resolved against the effective cwd for the thread.
|
||||
/// Replace the thread's runtime workspace roots. Paths must be absolute.
|
||||
#[experimental("thread/resume.runtimeWorkspaceRoots")]
|
||||
#[ts(optional = nullable)]
|
||||
pub runtime_workspace_roots: Option<Vec<PathBuf>>,
|
||||
pub runtime_workspace_roots: Option<Vec<AbsolutePathBuf>>,
|
||||
#[experimental(nested)]
|
||||
#[ts(optional = nullable)]
|
||||
pub approval_policy: Option<AskForApproval>,
|
||||
@@ -509,11 +507,10 @@ pub struct ThreadForkParams {
|
||||
pub service_tier: Option<Option<String>>,
|
||||
#[ts(optional = nullable)]
|
||||
pub cwd: Option<String>,
|
||||
/// Replace the thread's runtime workspace roots. Relative paths are
|
||||
/// resolved against the effective cwd for the thread.
|
||||
/// Replace the thread's runtime workspace roots. Paths must be absolute.
|
||||
#[experimental("thread/fork.runtimeWorkspaceRoots")]
|
||||
#[ts(optional = nullable)]
|
||||
pub runtime_workspace_roots: Option<Vec<PathBuf>>,
|
||||
pub runtime_workspace_roots: Option<Vec<AbsolutePathBuf>>,
|
||||
#[experimental(nested)]
|
||||
#[ts(optional = nullable)]
|
||||
pub approval_policy: Option<AskForApproval>,
|
||||
|
||||
@@ -88,11 +88,10 @@ pub struct TurnStartParams {
|
||||
#[ts(optional = nullable)]
|
||||
pub cwd: Option<PathBuf>,
|
||||
/// Replace the thread's runtime workspace roots for this turn and
|
||||
/// subsequent turns. Relative paths are resolved against the effective
|
||||
/// cwd for the turn.
|
||||
/// subsequent turns. Paths must be absolute.
|
||||
#[experimental("turn/start.runtimeWorkspaceRoots")]
|
||||
#[ts(optional = nullable)]
|
||||
pub runtime_workspace_roots: Option<Vec<PathBuf>>,
|
||||
pub runtime_workspace_roots: Option<Vec<AbsolutePathBuf>>,
|
||||
/// Override the approval policy for this turn and subsequent turns.
|
||||
#[experimental(nested)]
|
||||
#[ts(optional = nullable)]
|
||||
|
||||
@@ -130,7 +130,7 @@ Example with notification opt-out:
|
||||
|
||||
## API Overview
|
||||
|
||||
- `thread/start` — create a new thread; emits `thread/started` (including the current `thread.status`) and auto-subscribes you to turn/item events for that thread. When the request includes a `cwd` and the resolved sandbox is `workspace-write` or full access, app-server also marks that project as trusted in the user `config.toml`. Pass `sessionStartSource: "clear"` when starting a replacement thread after clearing the current session so `SessionStart` hooks receive `source: "clear"` instead of the default `"startup"`. Experimental `runtimeWorkspaceRoots` replaces the thread-scoped runtime workspace roots used to materialize `:workspace_roots`; relative paths resolve against the effective thread cwd. For permissions, prefer experimental `permissions` profile selection by id; the legacy `sandbox` shorthand is still accepted but cannot be combined with `permissions`. Experimental `environments` selects the sticky execution environments for turns on the thread; omit it to use the server default, pass `[]` to disable environments, or pass explicit environment ids with per-environment `cwd`.
|
||||
- `thread/start` — create a new thread; emits `thread/started` (including the current `thread.status`) and auto-subscribes you to turn/item events for that thread. When the request includes a `cwd` and the resolved sandbox is `workspace-write` or full access, app-server also marks that project as trusted in the user `config.toml`. Pass `sessionStartSource: "clear"` when starting a replacement thread after clearing the current session so `SessionStart` hooks receive `source: "clear"` instead of the default `"startup"`. Experimental `runtimeWorkspaceRoots` replaces the thread-scoped runtime workspace roots used to materialize `:workspace_roots`; paths must be absolute. For permissions, prefer experimental `permissions` profile selection by id; the legacy `sandbox` shorthand is still accepted but cannot be combined with `permissions`. Experimental `environments` selects the sticky execution environments for turns on the thread; omit it to use the server default, pass `[]` to disable environments, or pass explicit environment ids with per-environment `cwd`.
|
||||
- `thread/resume` — reopen an existing thread by id so subsequent `turn/start` calls append to it. Accepts the same permission override rules as `thread/start`.
|
||||
- `thread/fork` — fork an existing thread into a new thread id by copying the stored history; if the source thread is currently mid-turn, the fork records the same interruption marker as `turn/interrupt` instead of inheriting an unmarked partial turn suffix. The returned `thread.forkedFromId` points at the source thread when known. Accepts `ephemeral: true` for an in-memory temporary fork, emits `thread/started` (including the current `thread.status`), and auto-subscribes you to turn/item events for the new thread. Experimental clients can pass `excludeTurns: true` when they plan to page fork history via `thread/turns/list` instead of receiving the full turn array immediately. Accepts the same permission override rules as `thread/start`.
|
||||
- `thread/start`, `thread/resume`, and `thread/fork` responses include the legacy `sandbox` compatibility projection. Experimental clients can read `runtimeWorkspaceRoots` for the thread-scoped runtime roots and `activePermissionProfile` for the named or implicit built-in profile identity/provenance when known.
|
||||
@@ -158,7 +158,7 @@ Example with notification opt-out:
|
||||
- `thread/shellCommand` — run a user-initiated `!` shell command against a thread; this runs unsandboxed with full access rather than inheriting the thread sandbox policy. Returns `{}` immediately while progress streams through standard turn/item notifications and any active turn receives the formatted output in its message stream.
|
||||
- `thread/backgroundTerminals/clean` — terminate all running background terminals for a thread (experimental; requires `capabilities.experimentalApi`); returns `{}` when the cleanup request is accepted.
|
||||
- `thread/rollback` — drop the last N turns from the agent’s in-memory context and persist a rollback marker in the rollout so future resumes see the pruned history; returns the updated `thread` (with `turns` populated) on success.
|
||||
- `turn/start` — add user input to a thread and begin Codex generation; responds with the initial `turn` object and streams `turn/started`, `item/*`, and `turn/completed` notifications. `clientUserMessageId` is optional; when supplied, the corresponding `userMessage` item echoes it as `clientId`. Experimental `runtimeWorkspaceRoots` replaces the thread-scoped runtime workspace roots used to materialize `:workspace_roots`; relative paths resolve against the effective turn cwd. Prefer experimental `permissions` profile selection by id for permission overrides; the legacy `sandboxPolicy` field is still accepted but cannot be combined with `permissions`. For `collaborationMode`, `settings.developer_instructions: null` means "use built-in instructions for the selected mode".
|
||||
- `turn/start` — add user input to a thread and begin Codex generation; responds with the initial `turn` object and streams `turn/started`, `item/*`, and `turn/completed` notifications. `clientUserMessageId` is optional; when supplied, the corresponding `userMessage` item echoes it as `clientId`. Experimental `runtimeWorkspaceRoots` replaces the thread-scoped runtime workspace roots used to materialize `:workspace_roots`; paths must be absolute. Prefer experimental `permissions` profile selection by id for permission overrides; the legacy `sandboxPolicy` field is still accepted but cannot be combined with `permissions`. For `collaborationMode`, `settings.developer_instructions: null` means "use built-in instructions for the selected mode".
|
||||
- `thread/inject_items` — append raw Responses API items to a loaded thread’s model-visible history without starting a user turn; returns `{}` on success.
|
||||
- `turn/steer` — add user input to an already in-flight regular turn without starting a new turn; returns the active `turnId` that accepted the input. `clientUserMessageId` is optional; when supplied, the corresponding `userMessage` item echoes it as `clientId`. Review and manual compaction turns reject `turn/steer`.
|
||||
- `turn/interrupt` — request cancellation of an in-flight turn by `(thread_id, turn_id)`; success is an empty `{}` response and the turn finishes with `status: "interrupted"`.
|
||||
|
||||
@@ -513,6 +513,24 @@ use crate::thread_state::ThreadStateManager;
|
||||
use token_usage_replay::latest_token_usage_turn_id_from_rollout_items;
|
||||
use token_usage_replay::send_thread_token_usage_update_to_connection;
|
||||
|
||||
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 resolve_runtime_workspace_roots(workspace_roots: Vec<AbsolutePathBuf>) -> Vec<AbsolutePathBuf> {
|
||||
let mut resolved_roots = Vec::new();
|
||||
for root in workspace_roots {
|
||||
if !resolved_roots.iter().any(|existing| existing == &root) {
|
||||
resolved_roots.push(root);
|
||||
}
|
||||
}
|
||||
resolved_roots
|
||||
}
|
||||
|
||||
mod config_errors;
|
||||
mod request_errors;
|
||||
mod thread_goal_processor;
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -366,7 +366,10 @@ async fn turn_start_updates_runtime_workspace_roots_for_loaded_thread() -> Resul
|
||||
text: "Hello".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
runtime_workspace_roots: Some(vec![extra_root.clone(), extra_root.join(".")]),
|
||||
runtime_workspace_roots: Some(vec![
|
||||
AbsolutePathBuf::from_absolute_path(&extra_root)?,
|
||||
AbsolutePathBuf::from_absolute_path(extra_root.join("."))?,
|
||||
]),
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -197,15 +197,15 @@ async fn thread_start_creates_thread_and_emits_started() -> Result<()> {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn thread_start_resolves_runtime_workspace_roots_against_cwd() -> Result<()> {
|
||||
async fn thread_start_accepts_absolute_runtime_workspace_roots() -> Result<()> {
|
||||
let server = create_mock_responses_server_repeating_assistant("Done").await;
|
||||
let codex_home = TempDir::new()?;
|
||||
create_config_toml_without_approval_policy(codex_home.path(), &server.uri())?;
|
||||
|
||||
let cwd_tmp = TempDir::new()?;
|
||||
let cwd = cwd_tmp.path().to_path_buf();
|
||||
let relative_root = PathBuf::from("extra-root");
|
||||
std::fs::create_dir_all(cwd.join(&relative_root))?;
|
||||
let extra_root = cwd.join("extra-root");
|
||||
std::fs::create_dir_all(&extra_root)?;
|
||||
|
||||
let mut mcp = TestAppServer::new(codex_home.path()).await?;
|
||||
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
|
||||
@@ -213,7 +213,7 @@ async fn thread_start_resolves_runtime_workspace_roots_against_cwd() -> Result<(
|
||||
let req_id = mcp
|
||||
.send_thread_start_request(ThreadStartParams {
|
||||
cwd: Some(cwd.to_string_lossy().to_string()),
|
||||
runtime_workspace_roots: Some(vec![relative_root.clone()]),
|
||||
runtime_workspace_roots: Some(vec![extra_root.abs()]),
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
@@ -230,10 +230,7 @@ async fn thread_start_resolves_runtime_workspace_roots_against_cwd() -> Result<(
|
||||
} = to_response::<ThreadStartResponse>(resp)?;
|
||||
|
||||
assert_eq!(response_cwd, cwd.abs());
|
||||
assert_eq!(
|
||||
runtime_workspace_roots,
|
||||
vec![cwd_tmp.path().join(relative_root).abs()]
|
||||
);
|
||||
assert_eq!(runtime_workspace_roots, vec![extra_root.abs()]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2449,6 +2449,8 @@ async fn turn_start_permission_profile_rebinds_runtime_workspace_roots_between_t
|
||||
std::fs::create_dir(&new_root)?;
|
||||
let old_root_text = old_root.to_string_lossy().into_owned();
|
||||
let new_root_text = new_root.to_string_lossy().into_owned();
|
||||
let old_root = codex_utils_absolute_path::AbsolutePathBuf::from_absolute_path(old_root)?;
|
||||
let new_root = codex_utils_absolute_path::AbsolutePathBuf::from_absolute_path(new_root)?;
|
||||
|
||||
let server = responses::start_mock_server().await;
|
||||
let response_mock = responses::mount_sse_sequence(
|
||||
|
||||
@@ -2245,9 +2245,9 @@ pub struct ConfigOverrides {
|
||||
pub bypass_hook_trust: Option<bool>,
|
||||
/// Additional directories that should be treated as writable roots for this session.
|
||||
pub additional_writable_roots: Vec<PathBuf>,
|
||||
/// Explicit runtime workspace roots for this session. When set, this is
|
||||
/// the full runtime root list rather than an additive override.
|
||||
pub workspace_roots: Option<Vec<PathBuf>>,
|
||||
/// Explicit absolute runtime workspace roots for this session. When set,
|
||||
/// this is the full runtime root list rather than an additive override.
|
||||
pub workspace_roots: Option<Vec<AbsolutePathBuf>>,
|
||||
}
|
||||
|
||||
fn dedupe_absolute_paths(paths: &mut Vec<AbsolutePathBuf>) {
|
||||
@@ -2821,12 +2821,7 @@ impl Config {
|
||||
|| !requested_additional_writable_roots.is_empty()
|
||||
|| legacy_workspace_roots_explicit;
|
||||
let mut workspace_roots = match workspace_roots_override {
|
||||
Some(workspace_roots) => workspace_roots
|
||||
.into_iter()
|
||||
.map(|path| {
|
||||
AbsolutePathBuf::resolve_path_against_base(path, resolved_cwd.as_path())
|
||||
})
|
||||
.collect(),
|
||||
Some(workspace_roots) => workspace_roots,
|
||||
None => {
|
||||
let mut workspace_roots = vec![resolved_cwd.clone()];
|
||||
workspace_roots.extend(requested_additional_writable_roots.clone());
|
||||
|
||||
@@ -1044,13 +1044,7 @@ fn thread_start_params_from_config(config: &Config) -> ThreadStartParams {
|
||||
model: config.model.clone(),
|
||||
model_provider: Some(config.model_provider_id.clone()),
|
||||
cwd: Some(config.cwd.to_string_lossy().to_string()),
|
||||
runtime_workspace_roots: Some(
|
||||
config
|
||||
.workspace_roots
|
||||
.iter()
|
||||
.map(AbsolutePathBuf::to_path_buf)
|
||||
.collect(),
|
||||
),
|
||||
runtime_workspace_roots: Some(config.workspace_roots.clone()),
|
||||
approval_policy: Some(config.permissions.approval_policy.value().into()),
|
||||
approvals_reviewer: approvals_reviewer_override_from_config(config),
|
||||
sandbox: sandbox.flatten(),
|
||||
@@ -1075,13 +1069,7 @@ fn thread_resume_params_from_config(config: &Config, thread_id: String) -> Threa
|
||||
model: config.model.clone(),
|
||||
model_provider: Some(config.model_provider_id.clone()),
|
||||
cwd: Some(config.cwd.to_string_lossy().to_string()),
|
||||
runtime_workspace_roots: Some(
|
||||
config
|
||||
.workspace_roots
|
||||
.iter()
|
||||
.map(AbsolutePathBuf::to_path_buf)
|
||||
.collect(),
|
||||
),
|
||||
runtime_workspace_roots: Some(config.workspace_roots.clone()),
|
||||
approval_policy: Some(config.permissions.approval_policy.value().into()),
|
||||
approvals_reviewer: approvals_reviewer_override_from_config(config),
|
||||
sandbox: sandbox.flatten(),
|
||||
|
||||
@@ -708,12 +708,7 @@ impl AppServerSession {
|
||||
additional_context: None,
|
||||
environments: None,
|
||||
cwd: Some(cwd),
|
||||
runtime_workspace_roots: Some(
|
||||
workspace_roots
|
||||
.iter()
|
||||
.map(AbsolutePathBuf::to_path_buf)
|
||||
.collect(),
|
||||
),
|
||||
runtime_workspace_roots: Some(workspace_roots.to_vec()),
|
||||
approval_policy: Some(approval_policy),
|
||||
approvals_reviewer: Some(approvals_reviewer.into()),
|
||||
sandbox_policy,
|
||||
@@ -1403,13 +1398,7 @@ fn thread_start_params_from_config(
|
||||
model_provider: thread_params_mode.model_provider_from_config(config),
|
||||
service_tier: service_tier_override_from_config(config),
|
||||
cwd: thread_cwd_from_config(config, thread_params_mode, remote_cwd_override),
|
||||
runtime_workspace_roots: Some(
|
||||
config
|
||||
.workspace_roots
|
||||
.iter()
|
||||
.map(AbsolutePathBuf::to_path_buf)
|
||||
.collect(),
|
||||
),
|
||||
runtime_workspace_roots: Some(config.workspace_roots.clone()),
|
||||
approval_policy: Some(config.permissions.approval_policy.value().into()),
|
||||
approvals_reviewer: approvals_reviewer_override_from_config(config),
|
||||
sandbox,
|
||||
@@ -1444,13 +1433,7 @@ fn thread_resume_params_from_config(
|
||||
model_provider: thread_params_mode.model_provider_from_config(&config),
|
||||
service_tier: service_tier_override_from_config(&config),
|
||||
cwd: thread_cwd_from_config(&config, thread_params_mode, remote_cwd_override),
|
||||
runtime_workspace_roots: Some(
|
||||
config
|
||||
.workspace_roots
|
||||
.iter()
|
||||
.map(AbsolutePathBuf::to_path_buf)
|
||||
.collect(),
|
||||
),
|
||||
runtime_workspace_roots: Some(config.workspace_roots.clone()),
|
||||
approval_policy: Some(config.permissions.approval_policy.value().into()),
|
||||
approvals_reviewer: approvals_reviewer_override_from_config(&config),
|
||||
sandbox,
|
||||
@@ -1482,13 +1465,7 @@ fn thread_fork_params_from_config(
|
||||
model_provider: thread_params_mode.model_provider_from_config(&config),
|
||||
service_tier: service_tier_override_from_config(&config),
|
||||
cwd: thread_cwd_from_config(&config, thread_params_mode, remote_cwd_override),
|
||||
runtime_workspace_roots: Some(
|
||||
config
|
||||
.workspace_roots
|
||||
.iter()
|
||||
.map(AbsolutePathBuf::to_path_buf)
|
||||
.collect(),
|
||||
),
|
||||
runtime_workspace_roots: Some(config.workspace_roots.clone()),
|
||||
approval_policy: Some(config.permissions.approval_policy.value().into()),
|
||||
approvals_reviewer: approvals_reviewer_override_from_config(&config),
|
||||
sandbox,
|
||||
@@ -1885,13 +1862,7 @@ mod tests {
|
||||
assert_eq!(params.cwd, Some(config.cwd.to_string_lossy().to_string()));
|
||||
assert_eq!(
|
||||
params.runtime_workspace_roots,
|
||||
Some(
|
||||
config
|
||||
.workspace_roots
|
||||
.iter()
|
||||
.map(AbsolutePathBuf::to_path_buf)
|
||||
.collect()
|
||||
)
|
||||
Some(config.workspace_roots.clone())
|
||||
);
|
||||
assert_eq!(params.sandbox, None);
|
||||
assert_eq!(
|
||||
@@ -2009,13 +1980,7 @@ mod tests {
|
||||
&config.permissions.effective_permission_profile(),
|
||||
config.cwd.as_path(),
|
||||
);
|
||||
let expected_runtime_workspace_roots = Some(
|
||||
config
|
||||
.workspace_roots
|
||||
.iter()
|
||||
.map(AbsolutePathBuf::to_path_buf)
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
let expected_runtime_workspace_roots = Some(config.workspace_roots.clone());
|
||||
|
||||
let start = thread_start_params_from_config(
|
||||
&config,
|
||||
|
||||
Reference in New Issue
Block a user