mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[codex] define code mode host handshake protocol (#29515)
## Summary - add validated protocol-version, capability, and session identifier types - define explicit `ClientToHost` and `HostToClient` JSON envelopes for connection negotiation and session open/close acknowledgements - reject invalid states and unknown fields during decoding, with explicit wire-format and round-trip coverage ## Why This establishes the transport-neutral encoding shape needed to build and test the new code-mode host incrementally. Cell, tool callback, and failure-domain messages are intentionally deferred until their actors and behavior tests establish the required semantics. This is additive protocol scaffolding and does not change the current production code-mode implementation. ## Validation
This commit is contained in:
committed by
GitHub
Unverified
parent
2e69966cd8
commit
be0dfcfbea
@@ -0,0 +1,142 @@
|
||||
use std::fmt;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use super::Capability;
|
||||
use super::CapabilitySet;
|
||||
use super::HandshakeRejectReason;
|
||||
use super::ProtocolVersion;
|
||||
use super::SessionId;
|
||||
use super::SupportedProtocolVersions;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ClientHello {
|
||||
supported_versions: SupportedProtocolVersions,
|
||||
required_capabilities: CapabilitySet,
|
||||
optional_capabilities: CapabilitySet,
|
||||
}
|
||||
|
||||
impl ClientHello {
|
||||
pub fn new(
|
||||
supported_versions: SupportedProtocolVersions,
|
||||
required_capabilities: CapabilitySet,
|
||||
optional_capabilities: CapabilitySet,
|
||||
) -> Result<Self, ClientHelloError> {
|
||||
if let Some(capability) = required_capabilities
|
||||
.iter()
|
||||
.find(|capability| optional_capabilities.contains(capability))
|
||||
{
|
||||
return Err(ClientHelloError::OverlappingCapability(capability.clone()));
|
||||
}
|
||||
Ok(Self {
|
||||
supported_versions,
|
||||
required_capabilities,
|
||||
optional_capabilities,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn supported_versions(&self) -> &SupportedProtocolVersions {
|
||||
&self.supported_versions
|
||||
}
|
||||
|
||||
pub fn required_capabilities(&self) -> &CapabilitySet {
|
||||
&self.required_capabilities
|
||||
}
|
||||
|
||||
pub fn optional_capabilities(&self) -> &CapabilitySet {
|
||||
&self.optional_capabilities
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(deny_unknown_fields, rename_all = "camelCase")]
|
||||
struct ClientHelloWire {
|
||||
supported_versions: SupportedProtocolVersions,
|
||||
required_capabilities: CapabilitySet,
|
||||
optional_capabilities: CapabilitySet,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ClientHello {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let wire = ClientHelloWire::deserialize(deserializer)?;
|
||||
Self::new(
|
||||
wire.supported_versions,
|
||||
wire.required_capabilities,
|
||||
wire.optional_capabilities,
|
||||
)
|
||||
.map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum ClientHelloError {
|
||||
OverlappingCapability(Capability),
|
||||
}
|
||||
|
||||
impl fmt::Display for ClientHelloError {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::OverlappingCapability(capability) => write!(
|
||||
formatter,
|
||||
"capability `{capability}` cannot be both required and optional"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ClientHelloError {}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields, rename_all = "camelCase")]
|
||||
pub struct HostHello {
|
||||
selected_version: ProtocolVersion,
|
||||
capabilities: CapabilitySet,
|
||||
}
|
||||
|
||||
impl HostHello {
|
||||
pub fn new(selected_version: ProtocolVersion, capabilities: CapabilitySet) -> Self {
|
||||
Self {
|
||||
selected_version,
|
||||
capabilities,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn selected_version(&self) -> ProtocolVersion {
|
||||
self.selected_version
|
||||
}
|
||||
|
||||
pub fn capabilities(&self) -> &CapabilitySet {
|
||||
&self.capabilities
|
||||
}
|
||||
}
|
||||
|
||||
/// Messages sent from a client to the code-mode host.
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields, tag = "type", rename_all_fields = "camelCase")]
|
||||
pub enum ClientToHost {
|
||||
#[serde(rename = "connection/hello")]
|
||||
ClientHello(ClientHello),
|
||||
#[serde(rename = "session/open")]
|
||||
OpenSession { session_id: SessionId },
|
||||
#[serde(rename = "session/close")]
|
||||
CloseSession { session_id: SessionId },
|
||||
}
|
||||
|
||||
/// Messages sent from the code-mode host to a client.
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields, tag = "type", rename_all_fields = "camelCase")]
|
||||
pub enum HostToClient {
|
||||
#[serde(rename = "connection/ready")]
|
||||
HostHello(HostHello),
|
||||
#[serde(rename = "connection/rejected")]
|
||||
HandshakeRejected { reason: HandshakeRejectReason },
|
||||
#[serde(rename = "session/ready")]
|
||||
SessionReady { session_id: SessionId },
|
||||
#[serde(rename = "session/closed")]
|
||||
SessionClosed { session_id: SessionId },
|
||||
}
|
||||
Reference in New Issue
Block a user