protocol: separate app and exec RPC ownership (#29714)

## Why

The app-server and exec-server expose separate JSON-RPC APIs, but
exec-server currently sources its serialized protocol and envelope types
through app-server-oriented code. Giving each API an explicit owner
makes the crate boundary legible without introducing shared generic
envelopes.

## What changed

- Added `codex-exec-server-protocol` to own exec DTOs, process IDs, and
JSON-RPC envelopes.
- Updated exec-server clients, transports, handlers, and tests to use
the new crate.
- Exposed app-server's existing JSON-RPC types through a public `rpc`
module while retaining root re-exports.
- Preserved existing wire shapes, including exec `PathUri` behavior.

## Stack

This is PR 1 of 6. Next: [PR
#29721](https://github.com/openai/codex/pull/29721), which moves auth
mode below the app wire boundary.

## Validation

- Exec-server protocol and server coverage passed in the focused
protocol test runs.
- App-server protocol schema fixtures passed.
This commit is contained in:
Adam Perry @ OpenAI
2026-06-23 15:37:31 -07:00
committed by GitHub
Unverified
parent 220f5b76b2
commit 829f5b6b59
45 changed files with 255 additions and 131 deletions
@@ -2,7 +2,7 @@ use std::io;
use base64::Engine as _;
use base64::engine::general_purpose::STANDARD;
use codex_app_server_protocol::JSONRPCErrorError;
use codex_exec_server_protocol::JSONRPCErrorError;
use crate::CopyOptions;
use crate::CreateDirectoryOptions;
+2 -2
View File
@@ -3,8 +3,8 @@ use std::sync::Mutex as StdMutex;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use codex_app_server_protocol::JSONRPCErrorError;
use codex_app_server_protocol::RequestId;
use codex_exec_server_protocol::JSONRPCErrorError;
use codex_exec_server_protocol::RequestId;
use serde_json::to_value;
use std::collections::HashSet;
use tokio::sync::Mutex;
@@ -1,4 +1,4 @@
use codex_app_server_protocol::JSONRPCErrorError;
use codex_exec_server_protocol::JSONRPCErrorError;
use crate::ExecServerRuntimePaths;
use crate::local_process::LocalProcess;
+10 -11
View File
@@ -89,7 +89,7 @@ async fn run_connection(
warn!("ignoring malformed exec-server message: {reason}");
if outgoing_tx
.send(RpcServerOutboundMessage::Error {
request_id: codex_app_server_protocol::RequestId::Integer(-1),
request_id: codex_exec_server_protocol::RequestId::Integer(-1),
error: invalid_request(reason),
})
.await
@@ -99,7 +99,7 @@ async fn run_connection(
}
}
JsonRpcConnectionEvent::Message(message) => match message {
codex_app_server_protocol::JSONRPCMessage::Request(request) => {
codex_exec_server_protocol::JSONRPCMessage::Request(request) => {
if let Some(route) = router.request_route(request.method.as_str()) {
let message = tokio::select! {
message = route(Arc::clone(&handler), request) => message,
@@ -127,7 +127,7 @@ async fn run_connection(
break;
}
}
codex_app_server_protocol::JSONRPCMessage::Notification(notification) => {
codex_exec_server_protocol::JSONRPCMessage::Notification(notification) => {
let Some(route) = router.notification_route(notification.method.as_str())
else {
warn!(
@@ -150,14 +150,14 @@ async fn run_connection(
break;
}
}
codex_app_server_protocol::JSONRPCMessage::Response(response) => {
codex_exec_server_protocol::JSONRPCMessage::Response(response) => {
warn!(
"closing exec-server connection after unexpected client response: {:?}",
response.id
);
break;
}
codex_app_server_protocol::JSONRPCMessage::Error(error) => {
codex_exec_server_protocol::JSONRPCMessage::Error(error) => {
warn!(
"closing exec-server connection after unexpected client error: {:?}",
error.id
@@ -190,11 +190,11 @@ mod tests {
use std::sync::Arc;
use std::time::Duration;
use codex_app_server_protocol::JSONRPCMessage;
use codex_app_server_protocol::JSONRPCNotification;
use codex_app_server_protocol::JSONRPCRequest;
use codex_app_server_protocol::JSONRPCResponse;
use codex_app_server_protocol::RequestId;
use codex_exec_server_protocol::JSONRPCMessage;
use codex_exec_server_protocol::JSONRPCNotification;
use codex_exec_server_protocol::JSONRPCRequest;
use codex_exec_server_protocol::JSONRPCResponse;
use codex_exec_server_protocol::RequestId;
use codex_utils_path_uri::PathUri;
use pretty_assertions::assert_eq;
use serde::Serialize;
@@ -382,7 +382,6 @@ mod tests {
id: RequestId::Integer(id),
method: method.to_string(),
params: Some(serde_json::to_value(params).expect("serialize params")),
trace: None,
}),
)
.await;
@@ -3,7 +3,7 @@ use std::sync::Arc;
use std::sync::Mutex as StdMutex;
use std::time::Duration;
use codex_app_server_protocol::JSONRPCErrorError;
use codex_exec_server_protocol::JSONRPCErrorError;
use tokio::sync::Mutex;
use uuid::Uuid;
@@ -1,11 +1,11 @@
use std::net::SocketAddr;
use std::time::Duration;
use codex_app_server_protocol::JSONRPCMessage;
use codex_app_server_protocol::JSONRPCNotification;
use codex_app_server_protocol::JSONRPCRequest;
use codex_app_server_protocol::JSONRPCResponse;
use codex_app_server_protocol::RequestId;
use codex_exec_server_protocol::JSONRPCMessage;
use codex_exec_server_protocol::JSONRPCNotification;
use codex_exec_server_protocol::JSONRPCRequest;
use codex_exec_server_protocol::JSONRPCResponse;
use codex_exec_server_protocol::RequestId;
use pretty_assertions::assert_eq;
use tokio::io::AsyncBufReadExt;
use tokio::io::AsyncWriteExt;
@@ -74,7 +74,6 @@ async fn stdio_listen_transport_serves_initialize() {
})
.expect("initialize params should serialize"),
),
trace: None,
});
write_jsonrpc_line(&mut client_writer, &initialize).await;