-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[codex] resolve environment shell metadata eagerly #27709
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,8 @@ | ||
| use super::*; | ||
|
|
||
| pub(super) fn environment_selection_error_message(err: CodexErr) -> String { | ||
| pub(super) fn environment_selection_error(err: CodexErr) -> JSONRPCErrorError { | ||
| match err { | ||
| CodexErr::InvalidRequest(message) => message, | ||
| err => err.to_string(), | ||
| CodexErr::InvalidRequest(message) => invalid_request(message), | ||
| err => internal_error(format!("failed to validate environment selections: {err}")), | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ use codex_protocol::protocol::TurnEnvironmentSelection; | |
| use codex_utils_absolute_path::AbsolutePathBuf; | ||
|
|
||
| use crate::session::turn_context::TurnEnvironment; | ||
| use crate::shell::Shell; | ||
|
|
||
| pub(crate) fn default_thread_environment_selections( | ||
| environment_manager: &EnvironmentManager, | ||
|
|
@@ -60,7 +61,7 @@ impl ResolvedTurnEnvironments { | |
| } | ||
| } | ||
|
|
||
| pub(crate) fn resolve_environment_selections( | ||
| pub(crate) async fn resolve_environment_selections( | ||
| environment_manager: &EnvironmentManager, | ||
| environments: &[TurnEnvironmentSelection], | ||
| ) -> CodexResult<ResolvedTurnEnvironments> { | ||
|
|
@@ -79,19 +80,34 @@ pub(crate) fn resolve_environment_selections( | |
| .ok_or_else(|| { | ||
| CodexErr::InvalidRequest(format!("unknown turn environment id `{environment_id}`")) | ||
| })?; | ||
| let shell = match environment.info().await { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a selected remote exec-server is offline or slow to initialize, this Useful? React with 👍 / 👎.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok for now. We'll fix this more holistically later |
||
| Ok(info) => match Shell::from_environment_shell_info(info.shell) { | ||
| Ok(shell) => Some(shell), | ||
| Err(err) => { | ||
| tracing::warn!( | ||
| "failed to resolve shell for environment `{environment_id}`: {err}" | ||
| ); | ||
| None | ||
| } | ||
| }, | ||
| Err(err) => { | ||
| tracing::warn!("failed to get info for environment `{environment_id}`: {err}"); | ||
| None | ||
| } | ||
| }; | ||
| turn_environments.push(TurnEnvironment { | ||
| environment_id, | ||
| environment, | ||
| cwd: selected_environment.cwd.clone(), | ||
| shell: None, | ||
| shell, | ||
| }); | ||
| } | ||
|
|
||
| Ok(ResolvedTurnEnvironments { turn_environments }) | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use codex_exec_server::Environment; | ||
| use codex_exec_server::ExecServerRuntimePaths; | ||
| use codex_exec_server::LOCAL_ENVIRONMENT_ID; | ||
| use codex_exec_server::REMOTE_ENVIRONMENT_ID; | ||
|
|
@@ -189,6 +205,7 @@ url = "ws://127.0.0.1:8765" | |
| }, | ||
| ], | ||
| ) | ||
| .await | ||
| .expect_err("duplicate environment id should fail"); | ||
|
|
||
| assert!(err.to_string().contains("duplicate")); | ||
|
|
@@ -207,6 +224,7 @@ url = "ws://127.0.0.1:8765" | |
| cwd: selected_cwd, | ||
| }], | ||
| ) | ||
| .await | ||
| .expect("environment selections should resolve"); | ||
|
|
||
| assert_eq!( | ||
|
|
@@ -216,7 +234,21 @@ url = "ws://127.0.0.1:8765" | |
| .environment_id, | ||
| "local" | ||
| ); | ||
| assert_eq!(resolved.primary().expect("primary environment").shell, None); | ||
| assert_eq!( | ||
| resolved.primary().expect("primary environment").shell, | ||
| Some( | ||
| Shell::from_environment_shell_info( | ||
| manager | ||
| .get_environment("local") | ||
| .expect("local environment") | ||
| .info() | ||
| .await | ||
| .expect("local environment info") | ||
| .shell | ||
| ) | ||
| .expect("resolved shell") | ||
| ) | ||
| ); | ||
| } | ||
|
|
||
| #[tokio::test] | ||
|
|
@@ -230,40 +262,31 @@ url = "ws://127.0.0.1:8765" | |
| cwd: cwd.clone(), | ||
| }], | ||
| ) | ||
| .await | ||
| .expect("local environment should resolve"); | ||
| let remote_manager = EnvironmentManager::create_for_tests( | ||
| Some("ws://127.0.0.1:8765".to_string()), | ||
| Some(test_runtime_paths()), | ||
| ) | ||
| .await; | ||
| let remote = resolve_environment_selections( | ||
| &remote_manager, | ||
| &[TurnEnvironmentSelection { | ||
| let remote_environment = Arc::new( | ||
| Environment::create_for_tests(Some("ws://127.0.0.1:8765".to_string())) | ||
| .expect("remote environment"), | ||
| ); | ||
| let remote = ResolvedTurnEnvironments { | ||
| turn_environments: vec![TurnEnvironment { | ||
| environment_id: REMOTE_ENVIRONMENT_ID.to_string(), | ||
| environment: remote_environment.clone(), | ||
| cwd: cwd.clone(), | ||
| shell: None, | ||
| }], | ||
| ) | ||
| .expect("remote environment should resolve"); | ||
| local_manager | ||
| .upsert_environment( | ||
| REMOTE_ENVIRONMENT_ID.to_string(), | ||
| "ws://127.0.0.1:8765".to_string(), | ||
| ) | ||
| .expect("remote environment should register"); | ||
| let multiple = resolve_environment_selections( | ||
| &local_manager, | ||
| &[ | ||
| TurnEnvironmentSelection { | ||
| environment_id: LOCAL_ENVIRONMENT_ID.to_string(), | ||
| cwd: cwd.clone(), | ||
| }, | ||
| TurnEnvironmentSelection { | ||
| }; | ||
| let multiple = ResolvedTurnEnvironments { | ||
| turn_environments: vec![ | ||
| local.primary().expect("local environment").clone(), | ||
| TurnEnvironment { | ||
| environment_id: REMOTE_ENVIRONMENT_ID.to_string(), | ||
| environment: remote_environment, | ||
| cwd: cwd.clone(), | ||
| shell: None, | ||
| }, | ||
| ], | ||
| ) | ||
| .expect("multiple environments should resolve"); | ||
| }; | ||
|
|
||
| assert_eq!(local.single_local_environment_cwd(), Some(&cwd)); | ||
| assert_eq!(remote.single_local_environment_cwd(), None); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the remote environment shell differs from the host shell (for example the local session is zsh but the exec-server reports bash), this now advertises the remote shell in
<environment_context>, butexec_commandstill builds commands fromsession.user_shell()before sending them to the selected environment. The model will omit an explicitshellbecause it was told bash is available, yet the runtime can still invoke the host shell path on the remote environment and fail before the command runs.Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Followup./