fix: introduce codex-protocol crate (#2355)
This commit is contained in:
15
codex-rs/Cargo.lock
generated
15
codex-rs/Cargo.lock
generated
@@ -695,6 +695,7 @@ dependencies = [
|
||||
"codex-apply-patch",
|
||||
"codex-login",
|
||||
"codex-mcp-client",
|
||||
"codex-protocol",
|
||||
"core_test_support",
|
||||
"dirs",
|
||||
"env-flags",
|
||||
@@ -889,6 +890,19 @@ dependencies = [
|
||||
"wiremock",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codex-protocol"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"mcp-types",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"serde_json",
|
||||
"strum 0.27.2",
|
||||
"strum_macros 0.27.2",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codex-tui"
|
||||
version = "0.0.0"
|
||||
@@ -904,6 +918,7 @@ dependencies = [
|
||||
"codex-file-search",
|
||||
"codex-login",
|
||||
"codex-ollama",
|
||||
"codex-protocol",
|
||||
"color-eyre",
|
||||
"crossterm",
|
||||
"diffy",
|
||||
|
||||
@@ -15,6 +15,7 @@ members = [
|
||||
"mcp-server",
|
||||
"mcp-types",
|
||||
"ollama",
|
||||
"protocol",
|
||||
"tui",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
@@ -19,6 +19,7 @@ chrono = { version = "0.4", features = ["serde"] }
|
||||
codex-apply-patch = { path = "../apply-patch" }
|
||||
codex-login = { path = "../login" }
|
||||
codex-mcp-client = { path = "../mcp-client" }
|
||||
codex-protocol = { path = "../protocol" }
|
||||
dirs = "6"
|
||||
env-flags = "0.1.1"
|
||||
eventsource-stream = "0.2.3"
|
||||
|
||||
@@ -677,7 +677,10 @@ impl Session {
|
||||
call_id,
|
||||
command: command_for_display.clone(),
|
||||
cwd,
|
||||
parsed_cmd: parse_command(&command_for_display),
|
||||
parsed_cmd: parse_command(&command_for_display)
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
}),
|
||||
};
|
||||
let event = Event {
|
||||
@@ -1031,8 +1034,8 @@ async fn submission_loop(
|
||||
Arc::new(per_turn_config),
|
||||
None,
|
||||
provider,
|
||||
effort,
|
||||
summary,
|
||||
effort.into(),
|
||||
summary.into(),
|
||||
sess.session_id,
|
||||
);
|
||||
|
||||
@@ -1102,7 +1105,13 @@ async fn submission_loop(
|
||||
crate::protocol::GetHistoryEntryResponseEvent {
|
||||
offset,
|
||||
log_id,
|
||||
entry: entry_opt,
|
||||
entry: entry_opt.map(|e| {
|
||||
codex_protocol::message_history::HistoryEntry {
|
||||
session_id: e.session_id,
|
||||
ts: e.ts,
|
||||
text: e.text,
|
||||
}
|
||||
}),
|
||||
},
|
||||
),
|
||||
};
|
||||
@@ -1160,6 +1169,9 @@ async fn submission_loop(
|
||||
}
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
// Ignore unknown ops; enum is non_exhaustive to allow extensions.
|
||||
}
|
||||
}
|
||||
}
|
||||
debug!("Agent loop exited");
|
||||
|
||||
@@ -228,3 +228,27 @@ pub enum ReasoningSummary {
|
||||
/// Option to disable reasoning summaries.
|
||||
None,
|
||||
}
|
||||
|
||||
// Conversions from protocol enums to core config enums used where protocol
|
||||
// values are supplied by clients and core needs its internal representations.
|
||||
impl From<codex_protocol::config_types::ReasoningEffort> for ReasoningEffort {
|
||||
fn from(v: codex_protocol::config_types::ReasoningEffort) -> Self {
|
||||
match v {
|
||||
codex_protocol::config_types::ReasoningEffort::Low => ReasoningEffort::Low,
|
||||
codex_protocol::config_types::ReasoningEffort::Medium => ReasoningEffort::Medium,
|
||||
codex_protocol::config_types::ReasoningEffort::High => ReasoningEffort::High,
|
||||
codex_protocol::config_types::ReasoningEffort::None => ReasoningEffort::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<codex_protocol::config_types::ReasoningSummary> for ReasoningSummary {
|
||||
fn from(v: codex_protocol::config_types::ReasoningSummary) -> Self {
|
||||
match v {
|
||||
codex_protocol::config_types::ReasoningSummary::Auto => ReasoningSummary::Auto,
|
||||
codex_protocol::config_types::ReasoningSummary::Concise => ReasoningSummary::Concise,
|
||||
codex_protocol::config_types::ReasoningSummary::Detailed => ReasoningSummary::Detailed,
|
||||
codex_protocol::config_types::ReasoningSummary::None => ReasoningSummary::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ mod openai_model_info;
|
||||
mod openai_tools;
|
||||
pub mod plan_tool;
|
||||
mod project_doc;
|
||||
pub mod protocol;
|
||||
mod rollout;
|
||||
pub(crate) mod safety;
|
||||
pub mod seatbelt;
|
||||
@@ -56,3 +55,9 @@ mod user_notification;
|
||||
pub mod util;
|
||||
pub use apply_patch::CODEX_APPLY_PATCH_ARG1;
|
||||
pub use safety::get_platform_sandbox;
|
||||
// Re-export the protocol types from the standalone `codex-protocol` crate so existing
|
||||
// `codex_core::protocol::...` references continue to work across the workspace.
|
||||
pub use codex_protocol::protocol;
|
||||
// Re-export protocol config enums to ensure call sites can use the same types
|
||||
// as those in the protocol crate when constructing protocol messages.
|
||||
pub use codex_protocol::config_types as protocol_config_types;
|
||||
|
||||
@@ -183,6 +183,7 @@ impl From<Vec<InputItem>> for ResponseInputItem {
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<ContentItem>>(),
|
||||
}
|
||||
|
||||
@@ -41,6 +41,24 @@ pub enum ParsedCommand {
|
||||
},
|
||||
}
|
||||
|
||||
// Convert core's parsed command enum into the protocol's simplified type so
|
||||
// events can carry the canonical representation across process boundaries.
|
||||
impl From<ParsedCommand> for codex_protocol::parse_command::ParsedCommand {
|
||||
fn from(v: ParsedCommand) -> Self {
|
||||
use codex_protocol::parse_command::ParsedCommand as P;
|
||||
match v {
|
||||
ParsedCommand::Read { cmd, name } => P::Read { cmd, name },
|
||||
ParsedCommand::ListFiles { cmd, path } => P::ListFiles { cmd, path },
|
||||
ParsedCommand::Search { cmd, query, path } => P::Search { cmd, query, path },
|
||||
ParsedCommand::Format { cmd, tool, targets } => P::Format { cmd, tool, targets },
|
||||
ParsedCommand::Test { cmd } => P::Test { cmd },
|
||||
ParsedCommand::Lint { cmd, tool, targets } => P::Lint { cmd, tool, targets },
|
||||
ParsedCommand::Noop { cmd } => P::Noop { cmd },
|
||||
ParsedCommand::Unknown { cmd } => P::Unknown { cmd },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn shlex_join(tokens: &[String]) -> String {
|
||||
shlex_try_join(tokens.iter().map(|s| s.as_str()))
|
||||
.unwrap_or_else(|_| "<command included NUL byte>".to_string())
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::codex::Session;
|
||||
use crate::models::FunctionCallOutputPayload;
|
||||
use crate::models::ResponseInputItem;
|
||||
@@ -13,29 +10,13 @@ use crate::openai_tools::ResponsesApiTool;
|
||||
use crate::protocol::Event;
|
||||
use crate::protocol::EventMsg;
|
||||
|
||||
// Use the canonical plan tool types from the protocol crate to ensure
|
||||
// type-identity matches events transported via `codex_protocol`.
|
||||
pub use codex_protocol::plan_tool::PlanItemArg;
|
||||
pub use codex_protocol::plan_tool::StepStatus;
|
||||
pub use codex_protocol::plan_tool::UpdatePlanArgs;
|
||||
|
||||
// Types for the TODO tool arguments matching codex-vscode/todo-mcp/src/main.rs
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum StepStatus {
|
||||
Pending,
|
||||
InProgress,
|
||||
Completed,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct PlanItemArg {
|
||||
pub step: String,
|
||||
pub status: StepStatus,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct UpdatePlanArgs {
|
||||
#[serde(default)]
|
||||
pub explanation: Option<String>,
|
||||
pub plan: Vec<PlanItemArg>,
|
||||
}
|
||||
|
||||
pub(crate) static PLAN_TOOL: LazyLock<OpenAiTool> = LazyLock::new(|| {
|
||||
let mut plan_item_props = BTreeMap::new();
|
||||
|
||||
@@ -2,12 +2,12 @@ use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use codex_core::config_types::ReasoningEffort;
|
||||
use codex_core::config_types::ReasoningSummary;
|
||||
use codex_core::protocol::AskForApproval;
|
||||
use codex_core::protocol::FileChange;
|
||||
use codex_core::protocol::ReviewDecision;
|
||||
use codex_core::protocol::SandboxPolicy;
|
||||
use codex_core::protocol_config_types::ReasoningEffort;
|
||||
use codex_core::protocol_config_types::ReasoningSummary;
|
||||
use mcp_types::RequestId;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::path::Path;
|
||||
|
||||
use codex_core::config_types::ReasoningEffort;
|
||||
use codex_core::config_types::ReasoningSummary;
|
||||
use codex_core::protocol::AskForApproval;
|
||||
use codex_core::protocol::SandboxPolicy;
|
||||
use codex_core::protocol_config_types::ReasoningEffort;
|
||||
use codex_core::protocol_config_types::ReasoningSummary;
|
||||
use codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR;
|
||||
use codex_mcp_server::wire_format::AddConversationListenerParams;
|
||||
use codex_mcp_server::wire_format::AddConversationSubscriptionResponse;
|
||||
|
||||
20
codex-rs/protocol/Cargo.toml
Normal file
20
codex-rs/protocol/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
edition = "2024"
|
||||
name = "codex-protocol"
|
||||
version = { workspace = true }
|
||||
|
||||
[lib]
|
||||
name = "codex_protocol"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
mcp-types = { path = "../mcp-types" }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_bytes = "0.11"
|
||||
serde_json = "1"
|
||||
strum = "0.27.2"
|
||||
strum_macros = "0.27.2"
|
||||
uuid = { version = "1", features = ["serde", "v4"] }
|
||||
7
codex-rs/protocol/README.md
Normal file
7
codex-rs/protocol/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# codex-protocol
|
||||
|
||||
This crate defines the "types" for the protocol used by Codex CLI, which includes both "internal types" for communication between `codex-core` and `codex-tui`, as well as "external types" used with `codex mcp`.
|
||||
|
||||
This crate should have minimal dependencies.
|
||||
|
||||
Ideally, we should avoid "material business logic" in this crate, as we can always introduce `Ext`-style traits to add functionality to types in other crates.
|
||||
31
codex-rs/protocol/src/config_types.rs
Normal file
31
codex-rs/protocol/src/config_types.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use strum_macros::Display;
|
||||
|
||||
/// See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning
|
||||
#[derive(Debug, Serialize, Deserialize, Default, Clone, Copy, PartialEq, Eq, Display)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
pub enum ReasoningEffort {
|
||||
Low,
|
||||
#[default]
|
||||
Medium,
|
||||
High,
|
||||
/// Option to disable reasoning.
|
||||
None,
|
||||
}
|
||||
|
||||
/// A summary of the reasoning performed by the model. This can be useful for
|
||||
/// debugging and understanding the model's reasoning process.
|
||||
/// See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#reasoning-summaries
|
||||
#[derive(Debug, Serialize, Deserialize, Default, Clone, Copy, PartialEq, Eq, Display)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
pub enum ReasoningSummary {
|
||||
#[default]
|
||||
Auto,
|
||||
Concise,
|
||||
Detailed,
|
||||
/// Option to disable reasoning summaries.
|
||||
None,
|
||||
}
|
||||
5
codex-rs/protocol/src/lib.rs
Normal file
5
codex-rs/protocol/src/lib.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
pub mod config_types;
|
||||
pub mod message_history;
|
||||
pub mod parse_command;
|
||||
pub mod plan_tool;
|
||||
pub mod protocol;
|
||||
9
codex-rs/protocol/src/message_history.rs
Normal file
9
codex-rs/protocol/src/message_history.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct HistoryEntry {
|
||||
pub session_id: String,
|
||||
pub ts: u64,
|
||||
pub text: String,
|
||||
}
|
||||
38
codex-rs/protocol/src/parse_command.rs
Normal file
38
codex-rs/protocol/src/parse_command.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub enum ParsedCommand {
|
||||
Read {
|
||||
cmd: String,
|
||||
name: String,
|
||||
},
|
||||
ListFiles {
|
||||
cmd: String,
|
||||
path: Option<String>,
|
||||
},
|
||||
Search {
|
||||
cmd: String,
|
||||
query: Option<String>,
|
||||
path: Option<String>,
|
||||
},
|
||||
Format {
|
||||
cmd: String,
|
||||
tool: Option<String>,
|
||||
targets: Option<Vec<String>>,
|
||||
},
|
||||
Test {
|
||||
cmd: String,
|
||||
},
|
||||
Lint {
|
||||
cmd: String,
|
||||
tool: Option<String>,
|
||||
targets: Option<Vec<String>>,
|
||||
},
|
||||
Noop {
|
||||
cmd: String,
|
||||
},
|
||||
Unknown {
|
||||
cmd: String,
|
||||
},
|
||||
}
|
||||
26
codex-rs/protocol/src/plan_tool.rs
Normal file
26
codex-rs/protocol/src/plan_tool.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
// Types for the TODO tool arguments matching codex-vscode/todo-mcp/src/main.rs
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum StepStatus {
|
||||
Pending,
|
||||
InProgress,
|
||||
Completed,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct PlanItemArg {
|
||||
pub step: String,
|
||||
pub status: StepStatus,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct UpdatePlanArgs {
|
||||
#[serde(default)]
|
||||
pub explanation: Option<String>,
|
||||
pub plan: Vec<PlanItemArg>,
|
||||
}
|
||||
@@ -193,7 +193,7 @@ pub struct WritableRoot {
|
||||
}
|
||||
|
||||
impl WritableRoot {
|
||||
pub(crate) fn is_path_writable(&self, path: &Path) -> bool {
|
||||
pub fn is_path_writable(&self, path: &Path) -> bool {
|
||||
// Check if the path is under the root.
|
||||
if !path.starts_with(&self.root) {
|
||||
return false;
|
||||
@@ -33,6 +33,7 @@ codex-common = { path = "../common", features = [
|
||||
"sandbox_summary",
|
||||
] }
|
||||
codex-core = { path = "../core" }
|
||||
codex-protocol = { path = "../protocol" }
|
||||
codex-file-search = { path = "../file-search" }
|
||||
codex-login = { path = "../login" }
|
||||
codex-ollama = { path = "../ollama" }
|
||||
|
||||
@@ -3,7 +3,6 @@ use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use codex_core::config::Config;
|
||||
use codex_core::parse_command::ParsedCommand;
|
||||
use codex_core::protocol::AgentMessageDeltaEvent;
|
||||
use codex_core::protocol::AgentMessageEvent;
|
||||
use codex_core::protocol::AgentReasoningDeltaEvent;
|
||||
@@ -26,6 +25,7 @@ use codex_core::protocol::PatchApplyBeginEvent;
|
||||
use codex_core::protocol::TaskCompleteEvent;
|
||||
use codex_core::protocol::TokenUsage;
|
||||
use codex_core::protocol::TurnDiffEvent;
|
||||
use codex_protocol::parse_command::ParsedCommand;
|
||||
use crossterm::event::KeyEvent;
|
||||
use crossterm::event::KeyEventKind;
|
||||
use ratatui::buffer::Buffer;
|
||||
|
||||
@@ -204,9 +204,12 @@ fn exec_history_cell_shows_working_then_completed() {
|
||||
call_id: "call-1".into(),
|
||||
command: vec!["bash".into(), "-lc".into(), "echo done".into()],
|
||||
cwd: std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")),
|
||||
parsed_cmd: vec![codex_core::parse_command::ParsedCommand::Unknown {
|
||||
cmd: "echo done".into(),
|
||||
}],
|
||||
parsed_cmd: vec![
|
||||
codex_core::parse_command::ParsedCommand::Unknown {
|
||||
cmd: "echo done".into(),
|
||||
}
|
||||
.into(),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -246,9 +249,12 @@ fn exec_history_cell_shows_working_then_failed() {
|
||||
call_id: "call-2".into(),
|
||||
command: vec!["bash".into(), "-lc".into(), "false".into()],
|
||||
cwd: std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")),
|
||||
parsed_cmd: vec![codex_core::parse_command::ParsedCommand::Unknown {
|
||||
cmd: "false".into(),
|
||||
}],
|
||||
parsed_cmd: vec![
|
||||
codex_core::parse_command::ParsedCommand::Unknown {
|
||||
cmd: "false".into(),
|
||||
}
|
||||
.into(),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ use codex_ansi_escape::ansi_escape_line;
|
||||
use codex_common::create_config_summary_entries;
|
||||
use codex_common::elapsed::format_duration;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::parse_command::ParsedCommand;
|
||||
use codex_core::plan_tool::PlanItemArg;
|
||||
use codex_core::plan_tool::StepStatus;
|
||||
use codex_core::plan_tool::UpdatePlanArgs;
|
||||
@@ -20,6 +19,7 @@ use codex_core::protocol::SessionConfiguredEvent;
|
||||
use codex_core::protocol::TokenUsage;
|
||||
use codex_login::get_auth_file;
|
||||
use codex_login::try_read_auth_json;
|
||||
use codex_protocol::parse_command::ParsedCommand;
|
||||
use image::DynamicImage;
|
||||
use image::ImageReader;
|
||||
use mcp_types::EmbeddedResourceResource;
|
||||
|
||||
Reference in New Issue
Block a user