mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:32:32 +00:00
merge: pull latest upstream (prefer ours)
This commit is contained in:
Generated
+13
@@ -786,6 +786,7 @@ dependencies = [
|
|||||||
"codex-login",
|
"codex-login",
|
||||||
"codex-mcp-server",
|
"codex-mcp-server",
|
||||||
"codex-protocol",
|
"codex-protocol",
|
||||||
|
"codex-protocol-ts",
|
||||||
"codex-tui",
|
"codex-tui",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -1030,9 +1031,20 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"strum 0.27.2",
|
"strum 0.27.2",
|
||||||
"strum_macros 0.27.2",
|
"strum_macros 0.27.2",
|
||||||
|
"ts-rs",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "codex-protocol-ts"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"clap",
|
||||||
|
"codex-protocol",
|
||||||
|
"ts-rs",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codex-tui"
|
name = "codex-tui"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
@@ -5574,6 +5586,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"ts-rs-macros",
|
"ts-rs-macros",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ members = [
|
|||||||
"protocol",
|
"protocol",
|
||||||
"ollama",
|
"ollama",
|
||||||
"protocol",
|
"protocol",
|
||||||
|
"protocol-ts",
|
||||||
"tui",
|
"tui",
|
||||||
]
|
]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|||||||
@@ -37,3 +37,4 @@ tokio = { version = "1", features = [
|
|||||||
] }
|
] }
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
tracing-subscriber = "0.3.19"
|
tracing-subscriber = "0.3.19"
|
||||||
|
codex-protocol-ts = { path = "../protocol-ts" }
|
||||||
|
|||||||
@@ -73,6 +73,10 @@ enum Subcommand {
|
|||||||
/// Apply the latest diff produced by Codex agent as a `git apply` to your local working tree.
|
/// Apply the latest diff produced by Codex agent as a `git apply` to your local working tree.
|
||||||
#[clap(visible_alias = "a")]
|
#[clap(visible_alias = "a")]
|
||||||
Apply(ApplyCommand),
|
Apply(ApplyCommand),
|
||||||
|
|
||||||
|
/// Internal: generate TypeScript protocol bindings.
|
||||||
|
#[clap(hide = true)]
|
||||||
|
GenerateTs(GenerateTsCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
@@ -121,6 +125,17 @@ struct LogoutCommand {
|
|||||||
config_overrides: CliConfigOverrides,
|
config_overrides: CliConfigOverrides,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
struct GenerateTsCommand {
|
||||||
|
/// Output directory where .ts files will be written
|
||||||
|
#[arg(short = 'o', long = "out", value_name = "DIR")]
|
||||||
|
out_dir: PathBuf,
|
||||||
|
|
||||||
|
/// Optional path to the Prettier executable to format generated files
|
||||||
|
#[arg(short = 'p', long = "prettier", value_name = "PRETTIER_BIN")]
|
||||||
|
prettier: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
arg0_dispatch_or_else(|codex_linux_sandbox_exe| async move {
|
arg0_dispatch_or_else(|codex_linux_sandbox_exe| async move {
|
||||||
cli_main(codex_linux_sandbox_exe).await?;
|
cli_main(codex_linux_sandbox_exe).await?;
|
||||||
@@ -195,6 +210,9 @@ async fn cli_main(codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()
|
|||||||
prepend_config_flags(&mut apply_cli.config_overrides, cli.config_overrides);
|
prepend_config_flags(&mut apply_cli.config_overrides, cli.config_overrides);
|
||||||
run_apply_command(apply_cli, None).await?;
|
run_apply_command(apply_cli, None).await?;
|
||||||
}
|
}
|
||||||
|
Some(Subcommand::GenerateTs(gen_cli)) => {
|
||||||
|
codex_protocol_ts::generate_ts(&gen_cli.out_dir, gen_cli.prettier.as_deref())?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ use crate::error::CodexErr;
|
|||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::error::UsageLimitReachedError;
|
use crate::error::UsageLimitReachedError;
|
||||||
use crate::flags::CODEX_RS_SSE_FIXTURE;
|
use crate::flags::CODEX_RS_SSE_FIXTURE;
|
||||||
|
use crate::model_family::ModelFamily;
|
||||||
use crate::model_provider_info::ModelProviderInfo;
|
use crate::model_provider_info::ModelProviderInfo;
|
||||||
use crate::model_provider_info::WireApi;
|
use crate::model_provider_info::WireApi;
|
||||||
use crate::models::ResponseItem;
|
use crate::models::ResponseItem;
|
||||||
@@ -44,6 +45,8 @@ use crate::openai_tools::create_tools_json_for_responses_api;
|
|||||||
use crate::protocol::TokenUsage;
|
use crate::protocol::TokenUsage;
|
||||||
use crate::user_agent::get_codex_user_agent;
|
use crate::user_agent::get_codex_user_agent;
|
||||||
use crate::util::backoff;
|
use crate::util::backoff;
|
||||||
|
use codex_protocol::config_types::ReasoningEffort as ReasoningEffortConfig;
|
||||||
|
use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
@@ -385,6 +388,30 @@ impl ModelClient {
|
|||||||
pub fn get_provider(&self) -> ModelProviderInfo {
|
pub fn get_provider(&self) -> ModelProviderInfo {
|
||||||
self.provider.clone()
|
self.provider.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the currently configured model slug.
|
||||||
|
pub fn get_model(&self) -> String {
|
||||||
|
self.config.model.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the currently configured model family.
|
||||||
|
pub fn get_model_family(&self) -> ModelFamily {
|
||||||
|
self.config.model_family.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current reasoning effort setting.
|
||||||
|
pub fn get_reasoning_effort(&self) -> ReasoningEffortConfig {
|
||||||
|
self.effort
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current reasoning summary setting.
|
||||||
|
pub fn get_reasoning_summary(&self) -> ReasoningSummaryConfig {
|
||||||
|
self.summary
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_auth(&self) -> Option<CodexAuth> {
|
||||||
|
self.auth.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use crate::models::ResponseItem;
|
|||||||
use crate::openai_tools::OpenAiTool;
|
use crate::openai_tools::OpenAiTool;
|
||||||
use crate::protocol::TokenUsage;
|
use crate::protocol::TokenUsage;
|
||||||
use codex_apply_patch::APPLY_PATCH_TOOL_INSTRUCTIONS;
|
use codex_apply_patch::APPLY_PATCH_TOOL_INSTRUCTIONS;
|
||||||
|
use codex_protocol::config_types::ReasoningEffort as ReasoningEffortConfig;
|
||||||
|
use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig;
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
@@ -178,55 +180,8 @@ pub enum ResponseEvent {
|
|||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub(crate) struct Reasoning {
|
pub(crate) struct Reasoning {
|
||||||
pub(crate) effort: OpenAiReasoningEffort,
|
pub(crate) effort: ReasoningEffortConfig,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
pub(crate) summary: ReasoningSummaryConfig,
|
||||||
pub(crate) summary: Option<OpenAiReasoningSummary>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning
|
|
||||||
#[derive(Debug, Serialize, Default, Clone, Copy)]
|
|
||||||
#[serde(rename_all = "lowercase")]
|
|
||||||
pub(crate) enum OpenAiReasoningEffort {
|
|
||||||
Minimal,
|
|
||||||
Low,
|
|
||||||
#[default]
|
|
||||||
Medium,
|
|
||||||
High,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ReasoningEffortConfig> for Option<OpenAiReasoningEffort> {
|
|
||||||
fn from(effort: ReasoningEffortConfig) -> Self {
|
|
||||||
match effort {
|
|
||||||
ReasoningEffortConfig::Minimal => Some(OpenAiReasoningEffort::Minimal),
|
|
||||||
ReasoningEffortConfig::Low => Some(OpenAiReasoningEffort::Low),
|
|
||||||
ReasoningEffortConfig::Medium => Some(OpenAiReasoningEffort::Medium),
|
|
||||||
ReasoningEffortConfig::High => Some(OpenAiReasoningEffort::High),
|
|
||||||
ReasoningEffortConfig::None => 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, Default, Clone, Copy)]
|
|
||||||
#[serde(rename_all = "lowercase")]
|
|
||||||
pub(crate) enum OpenAiReasoningSummary {
|
|
||||||
#[default]
|
|
||||||
Auto,
|
|
||||||
Concise,
|
|
||||||
Detailed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ReasoningSummaryConfig> for Option<OpenAiReasoningSummary> {
|
|
||||||
fn from(summary: ReasoningSummaryConfig) -> Self {
|
|
||||||
match summary {
|
|
||||||
ReasoningSummaryConfig::Auto => Some(OpenAiReasoningSummary::Auto),
|
|
||||||
ReasoningSummaryConfig::Concise => Some(OpenAiReasoningSummary::Concise),
|
|
||||||
ReasoningSummaryConfig::Detailed => Some(OpenAiReasoningSummary::Detailed),
|
|
||||||
ReasoningSummaryConfig::None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Text configuration for verbosity level in OpenAI API responses.
|
/// Text configuration for verbosity level in OpenAI API responses.
|
||||||
@@ -350,12 +305,7 @@ pub(crate) fn create_reasoning_param_for_request(
|
|||||||
summary: ReasoningSummaryConfig,
|
summary: ReasoningSummaryConfig,
|
||||||
) -> Option<Reasoning> {
|
) -> Option<Reasoning> {
|
||||||
if model_family.supports_reasoning_summaries {
|
if model_family.supports_reasoning_summaries {
|
||||||
let effort: Option<OpenAiReasoningEffort> = effort.into();
|
Some(Reasoning { effort, summary })
|
||||||
let effort = effort?;
|
|
||||||
Some(Reasoning {
|
|
||||||
effort,
|
|
||||||
summary: summary.into(),
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ use crate::config_types::BrowserConfig;
|
|||||||
use crate::config_types::History;
|
use crate::config_types::History;
|
||||||
use crate::config_types::ThemeName;
|
use crate::config_types::ThemeName;
|
||||||
use crate::config_types::McpServerConfig;
|
use crate::config_types::McpServerConfig;
|
||||||
use crate::config_types::ReasoningEffort;
|
|
||||||
use crate::config_types::ReasoningSummary;
|
|
||||||
use crate::config_types::SandboxWorkspaceWrite;
|
use crate::config_types::SandboxWorkspaceWrite;
|
||||||
use crate::config_types::ShellEnvironmentPolicy;
|
use crate::config_types::ShellEnvironmentPolicy;
|
||||||
use crate::config_types::ShellEnvironmentPolicyToml;
|
use crate::config_types::ShellEnvironmentPolicyToml;
|
||||||
@@ -19,6 +17,8 @@ use crate::model_provider_info::built_in_model_providers;
|
|||||||
use crate::openai_model_info::get_model_info;
|
use crate::openai_model_info::get_model_info;
|
||||||
use crate::protocol::AskForApproval;
|
use crate::protocol::AskForApproval;
|
||||||
use crate::protocol::SandboxPolicy;
|
use crate::protocol::SandboxPolicy;
|
||||||
|
use codex_protocol::config_types::ReasoningEffort;
|
||||||
|
use codex_protocol::config_types::ReasoningSummary;
|
||||||
use codex_protocol::config_types::SandboxMode;
|
use codex_protocol::config_types::SandboxMode;
|
||||||
use dirs::home_dir;
|
use dirs::home_dir;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ use crate::config_types::ReasoningEffort;
|
|||||||
use crate::config_types::ReasoningSummary;
|
use crate::config_types::ReasoningSummary;
|
||||||
use crate::config_types::TextVerbosity;
|
use crate::config_types::TextVerbosity;
|
||||||
use crate::protocol::AskForApproval;
|
use crate::protocol::AskForApproval;
|
||||||
|
use codex_protocol::config_types::ReasoningEffort;
|
||||||
|
use codex_protocol::config_types::ReasoningSummary;
|
||||||
|
|
||||||
/// Collection of common configuration options that a user can define as a unit
|
/// Collection of common configuration options that a user can define as a unit
|
||||||
/// in `config.toml`.
|
/// in `config.toml`.
|
||||||
|
|||||||
@@ -5,11 +5,9 @@
|
|||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use strum_macros::Display;
|
|
||||||
use wildmatch::WildMatchPattern;
|
use wildmatch::WildMatchPattern;
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
/// Configuration for external agent models
|
/// Configuration for external agent models
|
||||||
#[derive(Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Deserialize, Debug, Clone, PartialEq)]
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
use codex_core::ConversationManager;
|
use codex_core::ConversationManager;
|
||||||
use codex_core::ModelProviderInfo;
|
use codex_core::ModelProviderInfo;
|
||||||
use codex_core::built_in_model_providers;
|
use codex_core::built_in_model_providers;
|
||||||
|
use codex_core::protocol::AskForApproval;
|
||||||
use codex_core::protocol::EventMsg;
|
use codex_core::protocol::EventMsg;
|
||||||
use codex_core::protocol::InputItem;
|
use codex_core::protocol::InputItem;
|
||||||
use codex_core::protocol::Op;
|
use codex_core::protocol::Op;
|
||||||
|
use codex_core::protocol::SandboxPolicy;
|
||||||
|
use codex_core::protocol_config_types::ReasoningEffort;
|
||||||
|
use codex_core::protocol_config_types::ReasoningSummary;
|
||||||
use codex_login::CodexAuth;
|
use codex_login::CodexAuth;
|
||||||
use core_test_support::load_default_config_for_test;
|
use core_test_support::load_default_config_for_test;
|
||||||
use core_test_support::load_sse_fixture_with_id;
|
use core_test_support::load_sse_fixture_with_id;
|
||||||
@@ -129,3 +133,230 @@ async fn prefixes_context_and_instructions_once_and_consistently_across_requests
|
|||||||
);
|
);
|
||||||
assert_eq!(body2["input"], expected_body2);
|
assert_eq!(body2["input"], expected_body2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn overrides_turn_context_but_keeps_cached_prefix_and_key_constant() {
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
let server = MockServer::start().await;
|
||||||
|
|
||||||
|
let sse = sse_completed("resp");
|
||||||
|
let template = ResponseTemplate::new(200)
|
||||||
|
.insert_header("content-type", "text/event-stream")
|
||||||
|
.set_body_raw(sse, "text/event-stream");
|
||||||
|
|
||||||
|
// Expect two POSTs to /v1/responses
|
||||||
|
Mock::given(method("POST"))
|
||||||
|
.and(path("/v1/responses"))
|
||||||
|
.respond_with(template)
|
||||||
|
.expect(2)
|
||||||
|
.mount(&server)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let model_provider = ModelProviderInfo {
|
||||||
|
base_url: Some(format!("{}/v1", server.uri())),
|
||||||
|
..built_in_model_providers()["openai"].clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let cwd = TempDir::new().unwrap();
|
||||||
|
let codex_home = TempDir::new().unwrap();
|
||||||
|
let mut config = load_default_config_for_test(&codex_home);
|
||||||
|
config.cwd = cwd.path().to_path_buf();
|
||||||
|
config.model_provider = model_provider;
|
||||||
|
config.user_instructions = Some("be consistent and helpful".to_string());
|
||||||
|
|
||||||
|
let conversation_manager = ConversationManager::default();
|
||||||
|
let codex = conversation_manager
|
||||||
|
.new_conversation_with_auth(config, Some(CodexAuth::from_api_key("Test API Key")))
|
||||||
|
.await
|
||||||
|
.expect("create new conversation")
|
||||||
|
.conversation;
|
||||||
|
|
||||||
|
// First turn
|
||||||
|
codex
|
||||||
|
.submit(Op::UserInput {
|
||||||
|
items: vec![InputItem::Text {
|
||||||
|
text: "hello 1".into(),
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await;
|
||||||
|
|
||||||
|
// Change everything about the turn context.
|
||||||
|
let new_cwd = TempDir::new().unwrap();
|
||||||
|
let writable = TempDir::new().unwrap();
|
||||||
|
codex
|
||||||
|
.submit(Op::OverrideTurnContext {
|
||||||
|
cwd: Some(new_cwd.path().to_path_buf()),
|
||||||
|
approval_policy: Some(AskForApproval::Never),
|
||||||
|
sandbox_policy: Some(SandboxPolicy::WorkspaceWrite {
|
||||||
|
writable_roots: vec![writable.path().to_path_buf()],
|
||||||
|
network_access: true,
|
||||||
|
exclude_tmpdir_env_var: true,
|
||||||
|
exclude_slash_tmp: true,
|
||||||
|
}),
|
||||||
|
model: Some("o3".to_string()),
|
||||||
|
effort: Some(ReasoningEffort::High),
|
||||||
|
summary: Some(ReasoningSummary::Detailed),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Second turn after overrides
|
||||||
|
codex
|
||||||
|
.submit(Op::UserInput {
|
||||||
|
items: vec![InputItem::Text {
|
||||||
|
text: "hello 2".into(),
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await;
|
||||||
|
|
||||||
|
// Verify we issued exactly two requests, and the cached prefix stayed identical.
|
||||||
|
let requests = server.received_requests().await.unwrap();
|
||||||
|
assert_eq!(requests.len(), 2, "expected two POST requests");
|
||||||
|
|
||||||
|
let body1 = requests[0].body_json::<serde_json::Value>().unwrap();
|
||||||
|
let body2 = requests[1].body_json::<serde_json::Value>().unwrap();
|
||||||
|
|
||||||
|
// prompt_cache_key should remain constant across overrides
|
||||||
|
assert_eq!(
|
||||||
|
body1["prompt_cache_key"], body2["prompt_cache_key"],
|
||||||
|
"prompt_cache_key should not change across overrides"
|
||||||
|
);
|
||||||
|
|
||||||
|
// The entire prefix from the first request should be identical and reused
|
||||||
|
// as the prefix of the second request, ensuring cache hit potential.
|
||||||
|
let expected_user_message_2 = serde_json::json!({
|
||||||
|
"type": "message",
|
||||||
|
"id": serde_json::Value::Null,
|
||||||
|
"role": "user",
|
||||||
|
"content": [ { "type": "input_text", "text": "hello 2" } ]
|
||||||
|
});
|
||||||
|
// After overriding the turn context, the environment context should be emitted again
|
||||||
|
// reflecting the new cwd, approval policy and sandbox settings.
|
||||||
|
let expected_env_text_2 = format!(
|
||||||
|
"<environment_context>\nCurrent working directory: {}\nApproval policy: never\nSandbox mode: workspace-write\nNetwork access: enabled\n</environment_context>",
|
||||||
|
new_cwd.path().to_string_lossy()
|
||||||
|
);
|
||||||
|
let expected_env_msg_2 = serde_json::json!({
|
||||||
|
"type": "message",
|
||||||
|
"id": serde_json::Value::Null,
|
||||||
|
"role": "user",
|
||||||
|
"content": [ { "type": "input_text", "text": expected_env_text_2 } ]
|
||||||
|
});
|
||||||
|
let expected_body2 = serde_json::json!(
|
||||||
|
[
|
||||||
|
body1["input"].as_array().unwrap().as_slice(),
|
||||||
|
[expected_env_msg_2, expected_user_message_2].as_slice(),
|
||||||
|
]
|
||||||
|
.concat()
|
||||||
|
);
|
||||||
|
assert_eq!(body2["input"], expected_body2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn per_turn_overrides_keep_cached_prefix_and_key_constant() {
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
let server = MockServer::start().await;
|
||||||
|
|
||||||
|
let sse = sse_completed("resp");
|
||||||
|
let template = ResponseTemplate::new(200)
|
||||||
|
.insert_header("content-type", "text/event-stream")
|
||||||
|
.set_body_raw(sse, "text/event-stream");
|
||||||
|
|
||||||
|
// Expect two POSTs to /v1/responses
|
||||||
|
Mock::given(method("POST"))
|
||||||
|
.and(path("/v1/responses"))
|
||||||
|
.respond_with(template)
|
||||||
|
.expect(2)
|
||||||
|
.mount(&server)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let model_provider = ModelProviderInfo {
|
||||||
|
base_url: Some(format!("{}/v1", server.uri())),
|
||||||
|
..built_in_model_providers()["openai"].clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let cwd = TempDir::new().unwrap();
|
||||||
|
let codex_home = TempDir::new().unwrap();
|
||||||
|
let mut config = load_default_config_for_test(&codex_home);
|
||||||
|
config.cwd = cwd.path().to_path_buf();
|
||||||
|
config.model_provider = model_provider;
|
||||||
|
config.user_instructions = Some("be consistent and helpful".to_string());
|
||||||
|
|
||||||
|
let conversation_manager = ConversationManager::default();
|
||||||
|
let codex = conversation_manager
|
||||||
|
.new_conversation_with_auth(config, Some(CodexAuth::from_api_key("Test API Key")))
|
||||||
|
.await
|
||||||
|
.expect("create new conversation")
|
||||||
|
.conversation;
|
||||||
|
|
||||||
|
// First turn
|
||||||
|
codex
|
||||||
|
.submit(Op::UserInput {
|
||||||
|
items: vec![InputItem::Text {
|
||||||
|
text: "hello 1".into(),
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await;
|
||||||
|
|
||||||
|
// Second turn using per-turn overrides via UserTurn
|
||||||
|
let new_cwd = TempDir::new().unwrap();
|
||||||
|
let writable = TempDir::new().unwrap();
|
||||||
|
codex
|
||||||
|
.submit(Op::UserTurn {
|
||||||
|
items: vec![InputItem::Text {
|
||||||
|
text: "hello 2".into(),
|
||||||
|
}],
|
||||||
|
cwd: new_cwd.path().to_path_buf(),
|
||||||
|
approval_policy: AskForApproval::Never,
|
||||||
|
sandbox_policy: SandboxPolicy::WorkspaceWrite {
|
||||||
|
writable_roots: vec![writable.path().to_path_buf()],
|
||||||
|
network_access: true,
|
||||||
|
exclude_tmpdir_env_var: true,
|
||||||
|
exclude_slash_tmp: true,
|
||||||
|
},
|
||||||
|
model: "o3".to_string(),
|
||||||
|
effort: ReasoningEffort::High,
|
||||||
|
summary: ReasoningSummary::Detailed,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await;
|
||||||
|
|
||||||
|
// Verify we issued exactly two requests, and the cached prefix stayed identical.
|
||||||
|
let requests = server.received_requests().await.unwrap();
|
||||||
|
assert_eq!(requests.len(), 2, "expected two POST requests");
|
||||||
|
|
||||||
|
let body1 = requests[0].body_json::<serde_json::Value>().unwrap();
|
||||||
|
let body2 = requests[1].body_json::<serde_json::Value>().unwrap();
|
||||||
|
|
||||||
|
// prompt_cache_key should remain constant across per-turn overrides
|
||||||
|
assert_eq!(
|
||||||
|
body1["prompt_cache_key"], body2["prompt_cache_key"],
|
||||||
|
"prompt_cache_key should not change across per-turn overrides"
|
||||||
|
);
|
||||||
|
|
||||||
|
// The entire prefix from the first request should be identical and reused
|
||||||
|
// as the prefix of the second request.
|
||||||
|
let expected_user_message_2 = serde_json::json!({
|
||||||
|
"type": "message",
|
||||||
|
"id": serde_json::Value::Null,
|
||||||
|
"role": "user",
|
||||||
|
"content": [ { "type": "input_text", "text": "hello 2" } ]
|
||||||
|
});
|
||||||
|
let expected_body2 = serde_json::json!(
|
||||||
|
[
|
||||||
|
body1["input"].as_array().unwrap().as_slice(),
|
||||||
|
[expected_user_message_2].as_slice(),
|
||||||
|
]
|
||||||
|
.concat()
|
||||||
|
);
|
||||||
|
assert_eq!(body2["input"], expected_body2);
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
[package]
|
||||||
|
edition = "2024"
|
||||||
|
name = "codex-protocol-ts"
|
||||||
|
version = { workspace = true }
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "codex_protocol_ts"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "codex-protocol-ts"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1"
|
||||||
|
codex-protocol = { path = "../protocol" }
|
||||||
|
ts-rs = "11"
|
||||||
|
clap = { version = "4", features = ["derive"] }
|
||||||
Executable
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"/..
|
||||||
|
|
||||||
|
tmpdir=$(mktemp -d)
|
||||||
|
just codex generate-ts --prettier ../node_modules/.bin/prettier --out "$tmpdir"
|
||||||
|
|
||||||
|
echo "wrote output to $tmpdir"
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
use anyhow::Context;
|
||||||
|
use anyhow::Result;
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::fs;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process::Command;
|
||||||
|
use ts_rs::TS;
|
||||||
|
|
||||||
|
const HEADER: &str = "// GENERATED CODE! DO NOT MODIFY BY HAND!\n\n";
|
||||||
|
|
||||||
|
pub fn generate_ts(out_dir: &Path, prettier: Option<&Path>) -> Result<()> {
|
||||||
|
ensure_dir(out_dir)?;
|
||||||
|
|
||||||
|
// Generate TS bindings
|
||||||
|
codex_protocol::mcp_protocol::ConversationId::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::InputItem::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::ClientRequest::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::ServerRequest::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::NewConversationParams::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::NewConversationResponse::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::AddConversationListenerParams::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::AddConversationSubscriptionResponse::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::RemoveConversationListenerParams::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::RemoveConversationSubscriptionResponse::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::SendUserMessageParams::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::SendUserMessageResponse::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::SendUserTurnParams::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::SendUserTurnResponse::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::InterruptConversationParams::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::InterruptConversationResponse::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::LoginChatGptResponse::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::LoginChatGptCompleteNotification::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::CancelLoginChatGptParams::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::CancelLoginChatGptResponse::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::ApplyPatchApprovalParams::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::ApplyPatchApprovalResponse::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::ExecCommandApprovalParams::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::ExecCommandApprovalResponse::export_all_to(out_dir)?;
|
||||||
|
|
||||||
|
// Prepend header to each generated .ts file
|
||||||
|
let ts_files = ts_files_in(out_dir)?;
|
||||||
|
for file in &ts_files {
|
||||||
|
prepend_header_if_missing(file)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format with Prettier by passing individual files (no shell globbing)
|
||||||
|
if let Some(prettier_bin) = prettier {
|
||||||
|
if !ts_files.is_empty() {
|
||||||
|
let status = Command::new(prettier_bin)
|
||||||
|
.arg("--write")
|
||||||
|
.args(ts_files.iter().map(|p| p.as_os_str()))
|
||||||
|
.status()
|
||||||
|
.with_context(|| {
|
||||||
|
format!("Failed to invoke Prettier at {}", prettier_bin.display())
|
||||||
|
})?;
|
||||||
|
if !status.success() {
|
||||||
|
return Err(anyhow!("Prettier failed with status {}", status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_dir(dir: &Path) -> Result<()> {
|
||||||
|
fs::create_dir_all(dir)
|
||||||
|
.with_context(|| format!("Failed to create output directory {}", dir.display()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepend_header_if_missing(path: &Path) -> Result<()> {
|
||||||
|
let mut content = String::new();
|
||||||
|
{
|
||||||
|
let mut f = fs::File::open(path)
|
||||||
|
.with_context(|| format!("Failed to open {} for reading", path.display()))?;
|
||||||
|
f.read_to_string(&mut content)
|
||||||
|
.with_context(|| format!("Failed to read {}", path.display()))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if content.starts_with(HEADER) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut f = fs::File::create(path)
|
||||||
|
.with_context(|| format!("Failed to open {} for writing", path.display()))?;
|
||||||
|
f.write_all(HEADER.as_bytes())
|
||||||
|
.with_context(|| format!("Failed to write header to {}", path.display()))?;
|
||||||
|
f.write_all(content.as_bytes())
|
||||||
|
.with_context(|| format!("Failed to write content to {}", path.display()))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ts_files_in(dir: &Path) -> Result<Vec<PathBuf>> {
|
||||||
|
let mut files = Vec::new();
|
||||||
|
for entry in
|
||||||
|
fs::read_dir(dir).with_context(|| format!("Failed to read dir {}", dir.display()))?
|
||||||
|
{
|
||||||
|
let entry = entry?;
|
||||||
|
let path = entry.path();
|
||||||
|
if path.is_file() && path.extension() == Some(OsStr::new("ts")) {
|
||||||
|
files.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(files)
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use clap::Parser;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(about = "Generate TypeScript bindings for the Codex protocol")]
|
||||||
|
struct Args {
|
||||||
|
/// Output directory where .ts files will be written
|
||||||
|
#[arg(short = 'o', long = "out", value_name = "DIR")]
|
||||||
|
out_dir: PathBuf,
|
||||||
|
|
||||||
|
/// Optional path to the Prettier executable to format generated files
|
||||||
|
#[arg(short = 'p', long = "prettier", value_name = "PRETTIER_BIN")]
|
||||||
|
prettier: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let args = Args::parse();
|
||||||
|
codex_protocol_ts::generate_ts(&args.out_dir, args.prettier.as_deref())
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ serde_bytes = "0.11"
|
|||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
strum = "0.27.2"
|
strum = "0.27.2"
|
||||||
strum_macros = "0.27.2"
|
strum_macros = "0.27.2"
|
||||||
|
ts-rs = { version = "11", features = ["uuid-impl", "serde-json-impl"] }
|
||||||
uuid = { version = "1", features = ["serde", "v4"] }
|
uuid = { version = "1", features = ["serde", "v4"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use strum_macros::Display;
|
use strum_macros::Display;
|
||||||
|
use ts_rs::TS;
|
||||||
|
|
||||||
/// See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning
|
/// 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)]
|
#[derive(Debug, Serialize, Deserialize, Default, Clone, Copy, PartialEq, Eq, Display, TS)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
#[strum(serialize_all = "lowercase")]
|
#[strum(serialize_all = "lowercase")]
|
||||||
pub enum ReasoningEffort {
|
pub enum ReasoningEffort {
|
||||||
@@ -20,7 +21,7 @@ pub enum ReasoningEffort {
|
|||||||
/// A summary of the reasoning performed by the model. This can be useful for
|
/// A summary of the reasoning performed by the model. This can be useful for
|
||||||
/// debugging and understanding the model's reasoning process.
|
/// debugging and understanding the model's reasoning process.
|
||||||
/// See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#reasoning-summaries
|
/// See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#reasoning-summaries
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, Clone, Copy, PartialEq, Eq, Display)]
|
#[derive(Debug, Serialize, Deserialize, Default, Clone, Copy, PartialEq, Eq, Display, TS)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
#[strum(serialize_all = "lowercase")]
|
#[strum(serialize_all = "lowercase")]
|
||||||
pub enum ReasoningSummary {
|
pub enum ReasoningSummary {
|
||||||
@@ -32,7 +33,7 @@ pub enum ReasoningSummary {
|
|||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Default, Serialize, Display)]
|
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Default, Serialize, Display, TS)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
pub enum SandboxMode {
|
pub enum SandboxMode {
|
||||||
|
|||||||
@@ -13,10 +13,11 @@ use crate::protocol::TurnAbortReason;
|
|||||||
use mcp_types::RequestId;
|
use mcp_types::RequestId;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use ts_rs::TS;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)]
|
||||||
#[serde(transparent)]
|
#[ts(type = "string")]
|
||||||
pub struct ConversationId(pub Uuid);
|
pub struct ConversationId(pub Uuid);
|
||||||
|
|
||||||
impl Display for ConversationId {
|
impl Display for ConversationId {
|
||||||
@@ -26,7 +27,7 @@ impl Display for ConversationId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Request from the client to the server.
|
/// Request from the client to the server.
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
#[serde(tag = "method", rename_all = "camelCase")]
|
#[serde(tag = "method", rename_all = "camelCase")]
|
||||||
pub enum ClientRequest {
|
pub enum ClientRequest {
|
||||||
NewConversation {
|
NewConversation {
|
||||||
@@ -70,7 +71,7 @@ pub enum ClientRequest {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct NewConversationParams {
|
pub struct NewConversationParams {
|
||||||
/// Optional override for the model name (e.g. "o3", "o4-mini").
|
/// Optional override for the model name (e.g. "o3", "o4-mini").
|
||||||
@@ -113,24 +114,24 @@ pub struct NewConversationParams {
|
|||||||
pub include_apply_patch_tool: Option<bool>,
|
pub include_apply_patch_tool: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct NewConversationResponse {
|
pub struct NewConversationResponse {
|
||||||
pub conversation_id: ConversationId,
|
pub conversation_id: ConversationId,
|
||||||
pub model: String,
|
pub model: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct AddConversationSubscriptionResponse {
|
pub struct AddConversationSubscriptionResponse {
|
||||||
pub subscription_id: Uuid,
|
pub subscription_id: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct RemoveConversationSubscriptionResponse {}
|
pub struct RemoveConversationSubscriptionResponse {}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct LoginChatGptResponse {
|
pub struct LoginChatGptResponse {
|
||||||
pub login_id: Uuid,
|
pub login_id: Uuid,
|
||||||
@@ -141,7 +142,7 @@ pub struct LoginChatGptResponse {
|
|||||||
// Event name for notifying client of login completion or failure.
|
// Event name for notifying client of login completion or failure.
|
||||||
pub const LOGIN_CHATGPT_COMPLETE_EVENT: &str = "codex/event/login_chatgpt_complete";
|
pub const LOGIN_CHATGPT_COMPLETE_EVENT: &str = "codex/event/login_chatgpt_complete";
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct LoginChatGptCompleteNotification {
|
pub struct LoginChatGptCompleteNotification {
|
||||||
pub login_id: Uuid,
|
pub login_id: Uuid,
|
||||||
@@ -150,24 +151,24 @@ pub struct LoginChatGptCompleteNotification {
|
|||||||
pub error: Option<String>,
|
pub error: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CancelLoginChatGptParams {
|
pub struct CancelLoginChatGptParams {
|
||||||
pub login_id: Uuid,
|
pub login_id: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CancelLoginChatGptResponse {}
|
pub struct CancelLoginChatGptResponse {}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct SendUserMessageParams {
|
pub struct SendUserMessageParams {
|
||||||
pub conversation_id: ConversationId,
|
pub conversation_id: ConversationId,
|
||||||
pub items: Vec<InputItem>,
|
pub items: Vec<InputItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct SendUserTurnParams {
|
pub struct SendUserTurnParams {
|
||||||
pub conversation_id: ConversationId,
|
pub conversation_id: ConversationId,
|
||||||
@@ -180,39 +181,39 @@ pub struct SendUserTurnParams {
|
|||||||
pub summary: ReasoningSummary,
|
pub summary: ReasoningSummary,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct SendUserTurnResponse {}
|
pub struct SendUserTurnResponse {}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct InterruptConversationParams {
|
pub struct InterruptConversationParams {
|
||||||
pub conversation_id: ConversationId,
|
pub conversation_id: ConversationId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct InterruptConversationResponse {
|
pub struct InterruptConversationResponse {
|
||||||
pub abort_reason: TurnAbortReason,
|
pub abort_reason: TurnAbortReason,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct SendUserMessageResponse {}
|
pub struct SendUserMessageResponse {}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct AddConversationListenerParams {
|
pub struct AddConversationListenerParams {
|
||||||
pub conversation_id: ConversationId,
|
pub conversation_id: ConversationId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct RemoveConversationListenerParams {
|
pub struct RemoveConversationListenerParams {
|
||||||
pub subscription_id: Uuid,
|
pub subscription_id: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[serde(tag = "type", content = "data")]
|
#[serde(tag = "type", content = "data")]
|
||||||
pub enum InputItem {
|
pub enum InputItem {
|
||||||
@@ -237,7 +238,7 @@ pub const APPLY_PATCH_APPROVAL_METHOD: &str = "applyPatchApproval";
|
|||||||
pub const EXEC_COMMAND_APPROVAL_METHOD: &str = "execCommandApproval";
|
pub const EXEC_COMMAND_APPROVAL_METHOD: &str = "execCommandApproval";
|
||||||
|
|
||||||
/// Request initiated from the server and sent to the client.
|
/// Request initiated from the server and sent to the client.
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
#[serde(tag = "method", rename_all = "camelCase")]
|
#[serde(tag = "method", rename_all = "camelCase")]
|
||||||
pub enum ServerRequest {
|
pub enum ServerRequest {
|
||||||
/// Request to approve a patch.
|
/// Request to approve a patch.
|
||||||
@@ -254,7 +255,7 @@ pub enum ServerRequest {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
pub struct ApplyPatchApprovalParams {
|
pub struct ApplyPatchApprovalParams {
|
||||||
pub conversation_id: ConversationId,
|
pub conversation_id: ConversationId,
|
||||||
/// Use to correlate this with [codex_core::protocol::PatchApplyBeginEvent]
|
/// Use to correlate this with [codex_core::protocol::PatchApplyBeginEvent]
|
||||||
@@ -270,7 +271,7 @@ pub struct ApplyPatchApprovalParams {
|
|||||||
pub grant_root: Option<PathBuf>,
|
pub grant_root: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
pub struct ExecCommandApprovalParams {
|
pub struct ExecCommandApprovalParams {
|
||||||
pub conversation_id: ConversationId,
|
pub conversation_id: ConversationId,
|
||||||
/// Use to correlate this with [codex_core::protocol::ExecCommandBeginEvent]
|
/// Use to correlate this with [codex_core::protocol::ExecCommandBeginEvent]
|
||||||
@@ -282,12 +283,12 @@ pub struct ExecCommandApprovalParams {
|
|||||||
pub reason: Option<String>,
|
pub reason: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
pub struct ExecCommandApprovalResponse {
|
pub struct ExecCommandApprovalResponse {
|
||||||
pub decision: ReviewDecision,
|
pub decision: ReviewDecision,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
pub struct ApplyPatchApprovalResponse {
|
pub struct ApplyPatchApprovalResponse {
|
||||||
pub decision: ReviewDecision,
|
pub decision: ReviewDecision,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use serde::Deserialize;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_bytes::ByteBuf;
|
use serde_bytes::ByteBuf;
|
||||||
use strum_macros::Display;
|
use strum_macros::Display;
|
||||||
|
use ts_rs::TS;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::config_types::ReasoningEffort as ReasoningEffortConfig;
|
use crate::config_types::ReasoningEffort as ReasoningEffortConfig;
|
||||||
@@ -75,6 +76,38 @@ pub enum Op {
|
|||||||
summary: ReasoningSummaryConfig,
|
summary: ReasoningSummaryConfig,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Override parts of the persistent turn context for subsequent turns.
|
||||||
|
///
|
||||||
|
/// All fields are optional; when omitted, the existing value is preserved.
|
||||||
|
/// This does not enqueue any input – it only updates defaults used for
|
||||||
|
/// future `UserInput` turns.
|
||||||
|
OverrideTurnContext {
|
||||||
|
/// Updated `cwd` for sandbox/tool calls.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
cwd: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Updated command approval policy.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
approval_policy: Option<AskForApproval>,
|
||||||
|
|
||||||
|
/// Updated sandbox policy for tool calls.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
sandbox_policy: Option<SandboxPolicy>,
|
||||||
|
|
||||||
|
/// Updated model slug. When set, the model family is derived
|
||||||
|
/// automatically.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
model: Option<String>,
|
||||||
|
|
||||||
|
/// Updated reasoning effort (honored only for reasoning-capable models).
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
effort: Option<ReasoningEffortConfig>,
|
||||||
|
|
||||||
|
/// Updated reasoning summary preference (honored only for reasoning-capable models).
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
summary: Option<ReasoningSummaryConfig>,
|
||||||
|
},
|
||||||
|
|
||||||
/// Approve a command execution
|
/// Approve a command execution
|
||||||
ExecApproval {
|
ExecApproval {
|
||||||
/// The id of the submission we are approving
|
/// The id of the submission we are approving
|
||||||
@@ -113,7 +146,7 @@ pub enum Op {
|
|||||||
|
|
||||||
/// Determines the conditions under which the user is consulted to approve
|
/// Determines the conditions under which the user is consulted to approve
|
||||||
/// running the command proposed by Codex.
|
/// running the command proposed by Codex.
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize, Display)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize, Display, TS)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
pub enum AskForApproval {
|
pub enum AskForApproval {
|
||||||
@@ -140,7 +173,7 @@ pub enum AskForApproval {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Determines execution restrictions for model shell commands.
|
/// Determines execution restrictions for model shell commands.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Display)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Display, TS)]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
#[serde(tag = "mode", rename_all = "kebab-case")]
|
#[serde(tag = "mode", rename_all = "kebab-case")]
|
||||||
pub enum SandboxPolicy {
|
pub enum SandboxPolicy {
|
||||||
@@ -706,7 +739,7 @@ pub struct SessionConfiguredEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// User's decision in response to an ExecApprovalRequest.
|
/// User's decision in response to an ExecApprovalRequest.
|
||||||
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
|
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, TS)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum ReviewDecision {
|
pub enum ReviewDecision {
|
||||||
/// User has approved this command and the agent should execute it.
|
/// User has approved this command and the agent should execute it.
|
||||||
@@ -727,7 +760,7 @@ pub enum ReviewDecision {
|
|||||||
Abort,
|
Abort,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, TS)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum FileChange {
|
pub enum FileChange {
|
||||||
Add {
|
Add {
|
||||||
@@ -753,7 +786,7 @@ pub struct TurnAbortedEvent {
|
|||||||
pub reason: TurnAbortReason,
|
pub reason: TurnAbortReason,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, TS)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum TurnAbortReason {
|
pub enum TurnAbortReason {
|
||||||
Interrupted,
|
Interrupted,
|
||||||
|
|||||||
Reference in New Issue
Block a user