Preserve typed gRPC code-mode session admission errors (#60496)

## Why

Session-opening failures were returned as strings, preventing callers from inspecting the terminal gRPC admission status directly.

## What changed

- Add `GrpcCodeModeSessionProvider::open_session` returning `GrpcSessionOpenError`, distinguishing admission status codes, admission timeouts, and other setup failures.
- Keep string errors for the existing `CodeModeSessionProvider` trait API.
- Re-export `ProcessOwnedGrpcCodeModeSessionProvider` from `codex-core`.

## Testing

Extend admission tests to assert terminal status codes, cover exhausted `ResourceExhausted` retries, and verify that `DeadlineExceeded` is not retried.

GitOrigin-RevId: 9d7f2e733e12b8bb7def368fa5fc5a09ea2a374e
This commit is contained in:
Sean Huang
2026-11-10 02:09:01 +00:00
committed by copyberry
parent e0370f0b06
commit 064b2d9bca
6 changed files with 88 additions and 29 deletions
@@ -2,6 +2,7 @@
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use codex_code_mode::GrpcSessionOpenError;
use pretty_assertions::assert_eq;
use super::*;
@@ -12,6 +13,8 @@ async fn session_admission_retries_only_transient_failures_and_is_bounded() -> R
(Code::Unavailable, 1, 2, true),
(Code::ResourceExhausted, 1, 2, true),
(Code::Unavailable, usize::MAX, 5, false),
(Code::ResourceExhausted, usize::MAX, 5, false),
(Code::DeadlineExceeded, 1, 1, false),
(Code::InvalidArgument, 1, 1, false),
(Code::Unknown, 1, 1, false),
] {
@@ -43,13 +46,13 @@ async fn session_admission_retries_only_transient_failures_and_is_bounded() -> R
.expect("admission test server");
}));
let provider = GrpcCodeModeSessionProvider::new(format!("http://{address}"));
let result = timeout(TEST_TIMEOUT, provider.create_session()).await?;
assert_eq!(
result.is_ok(),
succeeds,
"status {code}: {}",
result.as_ref().err().cloned().unwrap_or_default()
);
let result = timeout(TEST_TIMEOUT, provider.open_session(Default::default())).await?;
let actual_code = match result.as_ref() {
Ok(_) => None,
Err(GrpcSessionOpenError::AdmissionStatus { code, .. }) => Some(*code),
Err(error) => anyhow::bail!("unexpected session open error: {error}"),
};
assert_eq!(actual_code, (!succeeds).then_some(code));
assert_eq!(attempts.load(Ordering::SeqCst), expected_attempts);
if let Ok(session) = result {
let response = execute(