Add in-process app server and wire up exec to use it (#14005)

This is a subset of PR #13636. See that PR for a full overview of the
architectural change.

This PR implements the in-process app server and modifies the
non-interactive "exec" entry point to use the app server.

---------

Co-authored-by: Felipe Coury <felipe.coury@gmail.com>
This commit is contained in:
Eric Traut
2026-03-08 18:43:55 -06:00
committed by GitHub
parent a684a36091
commit da3689f0ef
13 changed files with 3019 additions and 431 deletions

View File

@@ -1,6 +1,16 @@
//! Tracing helpers shared by socket and in-process app-server entry points.
//!
//! The in-process path intentionally reuses the same span shape as JSON-RPC
//! transports so request telemetry stays comparable across stdio, websocket,
//! and embedded callers. [`typed_request_span`] is the in-process counterpart
//! of [`request_span`] and stamps `rpc.transport` as `"in-process"` while
//! deriving client identity from the typed [`ClientRequest`] rather than
//! from a parsed JSON envelope.
use crate::message_processor::ConnectionSessionState;
use crate::outgoing_message::ConnectionId;
use crate::transport::AppServerTransport;
use codex_app_server_protocol::ClientRequest;
use codex_app_server_protocol::InitializeParams;
use codex_app_server_protocol::JSONRPCRequest;
use codex_otel::set_parent_from_context;
@@ -65,6 +75,51 @@ pub(crate) fn request_span(
span
}
/// Builds tracing span metadata for typed in-process requests.
///
/// This mirrors `request_span` semantics while stamping transport as
/// `in-process` and deriving client info either from initialize params or
/// from existing connection session state.
pub(crate) fn typed_request_span(
request: &ClientRequest,
connection_id: ConnectionId,
session: &ConnectionSessionState,
) -> Span {
let method = request.method();
let span = info_span!(
"app_server.request",
otel.kind = "server",
otel.name = method,
rpc.system = "jsonrpc",
rpc.method = method,
rpc.transport = "in-process",
rpc.request_id = ?request.id(),
app_server.connection_id = ?connection_id,
app_server.api_version = "v2",
app_server.client_name = field::Empty,
app_server.client_version = field::Empty,
);
if let Some((client_name, client_version)) = initialize_client_info_from_typed_request(request)
{
span.record("app_server.client_name", client_name);
span.record("app_server.client_version", client_version);
} else {
if let Some(client_name) = session.app_server_client_name.as_deref() {
span.record("app_server.client_name", client_name);
}
if let Some(client_version) = session.client_version.as_deref() {
span.record("app_server.client_version", client_version);
}
}
if let Some(context) = traceparent_context_from_env() {
set_parent_from_context(&span, context);
}
span
}
fn transport_name(transport: AppServerTransport) -> &'static str {
match transport {
AppServerTransport::Stdio => "stdio",
@@ -99,3 +154,13 @@ fn initialize_client_info(request: &JSONRPCRequest) -> Option<InitializeParams>
let params = request.params.clone()?;
serde_json::from_value(params).ok()
}
fn initialize_client_info_from_typed_request(request: &ClientRequest) -> Option<(&str, &str)> {
match request {
ClientRequest::Initialize { params, .. } => Some((
params.client_info.name.as_str(),
params.client_info.version.as_str(),
)),
_ => None,
}
}