mirror of
https://github.com/openai/codex.git
synced 2026-10-31 19:28:49 +00:00
Merge branch 'main' into pap/thread_name_in_db
# Conflicts: # codex-rs/state/src/extract.rs # codex-rs/state/src/runtime.rs
This commit is contained in:
@@ -30,7 +30,7 @@ pub struct Cli {
|
||||
#[arg(long = "oss", default_value_t = false)]
|
||||
pub oss: bool,
|
||||
|
||||
/// Specify which local provider to use (lmstudio, ollama, or ollama-chat).
|
||||
/// Specify which local provider to use (lmstudio or ollama).
|
||||
/// If not specified with --oss, will use config default or show selection.
|
||||
#[arg(long = "local-provider")]
|
||||
pub oss_provider: Option<String>,
|
||||
|
||||
@@ -20,6 +20,7 @@ use codex_core::protocol::EventMsg;
|
||||
use codex_core::protocol::ExecCommandBeginEvent;
|
||||
use codex_core::protocol::ExecCommandEndEvent;
|
||||
use codex_core::protocol::FileChange;
|
||||
use codex_core::protocol::ItemCompletedEvent;
|
||||
use codex_core::protocol::McpInvocation;
|
||||
use codex_core::protocol::McpToolCallBeginEvent;
|
||||
use codex_core::protocol::McpToolCallEndEvent;
|
||||
@@ -33,6 +34,7 @@ use codex_core::protocol::TurnDiffEvent;
|
||||
use codex_core::protocol::WarningEvent;
|
||||
use codex_core::protocol::WebSearchEndEvent;
|
||||
use codex_core::web_search::web_search_detail;
|
||||
use codex_protocol::items::TurnItem;
|
||||
use codex_protocol::num_format::format_with_separators;
|
||||
use owo_colors::OwoColorize;
|
||||
use owo_colors::Style;
|
||||
@@ -73,6 +75,7 @@ pub(crate) struct EventProcessorWithHumanOutput {
|
||||
last_message_path: Option<PathBuf>,
|
||||
last_total_token_usage: Option<codex_core::protocol::TokenUsageInfo>,
|
||||
final_message: Option<String>,
|
||||
last_proposed_plan: Option<String>,
|
||||
}
|
||||
|
||||
impl EventProcessorWithHumanOutput {
|
||||
@@ -99,6 +102,7 @@ impl EventProcessorWithHumanOutput {
|
||||
last_message_path,
|
||||
last_total_token_usage: None,
|
||||
final_message: None,
|
||||
last_proposed_plan: None,
|
||||
}
|
||||
} else {
|
||||
Self {
|
||||
@@ -116,6 +120,7 @@ impl EventProcessorWithHumanOutput {
|
||||
last_message_path,
|
||||
last_total_token_usage: None,
|
||||
final_message: None,
|
||||
last_proposed_plan: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,12 +265,14 @@ impl EventProcessor for EventProcessorWithHumanOutput {
|
||||
);
|
||||
}
|
||||
EventMsg::TurnComplete(TurnCompleteEvent { last_agent_message }) => {
|
||||
let last_message = last_agent_message.as_deref();
|
||||
let last_message = last_agent_message
|
||||
.as_deref()
|
||||
.or(self.last_proposed_plan.as_deref());
|
||||
if let Some(output_file) = self.last_message_path.as_deref() {
|
||||
handle_last_message(last_message, output_file);
|
||||
}
|
||||
|
||||
self.final_message = last_agent_message;
|
||||
self.final_message = last_agent_message.or_else(|| self.last_proposed_plan.clone());
|
||||
|
||||
return CodexStatus::InitiateShutdown;
|
||||
}
|
||||
@@ -297,6 +304,12 @@ impl EventProcessor for EventProcessorWithHumanOutput {
|
||||
message,
|
||||
);
|
||||
}
|
||||
EventMsg::ItemCompleted(ItemCompletedEvent {
|
||||
item: TurnItem::Plan(item),
|
||||
..
|
||||
}) => {
|
||||
self.last_proposed_plan = Some(item.text);
|
||||
}
|
||||
EventMsg::ExecCommandBegin(ExecCommandBeginEvent { command, cwd, .. }) => {
|
||||
eprint!(
|
||||
"{}\n{} in {}",
|
||||
@@ -362,7 +375,8 @@ impl EventProcessor for EventProcessorWithHumanOutput {
|
||||
ts_msg!(self, "{}", title.style(title_style));
|
||||
|
||||
if let Ok(res) = result {
|
||||
let val: serde_json::Value = res.into();
|
||||
let val = serde_json::to_value(res)
|
||||
.unwrap_or_else(|_| serde_json::Value::String("<result>".to_string()));
|
||||
let pretty =
|
||||
serde_json::to_string_pretty(&val).unwrap_or_else(|_| val.to_string());
|
||||
|
||||
@@ -579,17 +593,20 @@ impl EventProcessor for EventProcessorWithHumanOutput {
|
||||
view.path.display()
|
||||
);
|
||||
}
|
||||
EventMsg::TurnAborted(abort_reason) => match abort_reason.reason {
|
||||
TurnAbortReason::Interrupted => {
|
||||
ts_msg!(self, "task interrupted");
|
||||
EventMsg::TurnAborted(abort_reason) => {
|
||||
match abort_reason.reason {
|
||||
TurnAbortReason::Interrupted => {
|
||||
ts_msg!(self, "task interrupted");
|
||||
}
|
||||
TurnAbortReason::Replaced => {
|
||||
ts_msg!(self, "task aborted: replaced by a new task");
|
||||
}
|
||||
TurnAbortReason::ReviewEnded => {
|
||||
ts_msg!(self, "task aborted: review ended");
|
||||
}
|
||||
}
|
||||
TurnAbortReason::Replaced => {
|
||||
ts_msg!(self, "task aborted: replaced by a new task");
|
||||
}
|
||||
TurnAbortReason::ReviewEnded => {
|
||||
ts_msg!(self, "task aborted: review ended");
|
||||
}
|
||||
},
|
||||
return CodexStatus::InitiateShutdown;
|
||||
}
|
||||
EventMsg::ContextCompacted(_) => {
|
||||
ts_msg!(self, "context compacted");
|
||||
}
|
||||
@@ -759,6 +776,8 @@ impl EventProcessor for EventProcessorWithHumanOutput {
|
||||
| EventMsg::McpListToolsResponse(_)
|
||||
| EventMsg::ListCustomPromptsResponse(_)
|
||||
| EventMsg::ListSkillsResponse(_)
|
||||
| EventMsg::ListRemoteSkillsResponse(_)
|
||||
| EventMsg::RemoteSkillDownloaded(_)
|
||||
| EventMsg::RawResponseItem(_)
|
||||
| EventMsg::UserMessage(_)
|
||||
| EventMsg::EnteredReviewMode(_)
|
||||
@@ -769,6 +788,7 @@ impl EventProcessor for EventProcessorWithHumanOutput {
|
||||
| EventMsg::ItemStarted(_)
|
||||
| EventMsg::ItemCompleted(_)
|
||||
| EventMsg::AgentMessageContentDelta(_)
|
||||
| EventMsg::PlanDelta(_)
|
||||
| EventMsg::ReasoningContentDelta(_)
|
||||
| EventMsg::ReasoningRawContentDelta(_)
|
||||
| EventMsg::SkillsUpdateAvailable
|
||||
|
||||
@@ -58,6 +58,7 @@ use tracing::warn;
|
||||
|
||||
pub struct EventProcessorWithJsonOutput {
|
||||
last_message_path: Option<PathBuf>,
|
||||
last_proposed_plan: Option<String>,
|
||||
next_event_id: AtomicU64,
|
||||
// Tracks running commands by call_id, including the associated item id.
|
||||
running_commands: HashMap<String, RunningCommand>,
|
||||
@@ -102,6 +103,7 @@ impl EventProcessorWithJsonOutput {
|
||||
pub fn new(last_message_path: Option<PathBuf>) -> Self {
|
||||
Self {
|
||||
last_message_path,
|
||||
last_proposed_plan: None,
|
||||
next_event_id: AtomicU64::new(0),
|
||||
running_commands: HashMap::new(),
|
||||
running_patch_applies: HashMap::new(),
|
||||
@@ -119,6 +121,13 @@ impl EventProcessorWithJsonOutput {
|
||||
protocol::EventMsg::SessionConfigured(ev) => self.handle_session_configured(ev),
|
||||
protocol::EventMsg::ThreadNameUpdated(_) => Vec::new(),
|
||||
protocol::EventMsg::AgentMessage(ev) => self.handle_agent_message(ev),
|
||||
protocol::EventMsg::ItemCompleted(protocol::ItemCompletedEvent {
|
||||
item: codex_protocol::items::TurnItem::Plan(item),
|
||||
..
|
||||
}) => {
|
||||
self.last_proposed_plan = Some(item.text.clone());
|
||||
Vec::new()
|
||||
}
|
||||
protocol::EventMsg::AgentReasoning(ev) => self.handle_reasoning_event(ev),
|
||||
protocol::EventMsg::ExecCommandBegin(ev) => self.handle_exec_command_begin(ev),
|
||||
protocol::EventMsg::ExecCommandEnd(ev) => self.handle_exec_command_end(ev),
|
||||
@@ -855,10 +864,14 @@ impl EventProcessor for EventProcessorWithJsonOutput {
|
||||
last_agent_message,
|
||||
}) => {
|
||||
if let Some(output_file) = self.last_message_path.as_deref() {
|
||||
handle_last_message(last_agent_message.as_deref(), output_file);
|
||||
let last_message = last_agent_message
|
||||
.as_deref()
|
||||
.or(self.last_proposed_plan.as_deref());
|
||||
handle_last_message(last_message, output_file);
|
||||
}
|
||||
CodexStatus::InitiateShutdown
|
||||
}
|
||||
protocol::EventMsg::TurnAborted(_) => CodexStatus::InitiateShutdown,
|
||||
protocol::EventMsg::ShutdownComplete => CodexStatus::Shutdown,
|
||||
_ => CodexStatus::Running,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use codex_protocol::models::WebSearchAction;
|
||||
use mcp_types::ContentBlock as McpContentBlock;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value as JsonValue;
|
||||
@@ -256,7 +255,14 @@ pub struct CollabToolCallItem {
|
||||
/// Result payload produced by an MCP tool invocation.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, TS)]
|
||||
pub struct McpToolCallItemResult {
|
||||
pub content: Vec<McpContentBlock>,
|
||||
// NOTE: `rmcp::model::Content` (and its `RawContent` variants) would be a
|
||||
// more precise Rust representation of MCP content blocks. We intentionally
|
||||
// use `serde_json::Value` here because this crate exports JSON schema + TS
|
||||
// types (`schemars`/`ts-rs`), and the rmcp model types aren't set up to be
|
||||
// schema/TS friendly (and would introduce heavier coupling to rmcp's Rust
|
||||
// representations). Using `JsonValue` keeps the payload wire-shaped and
|
||||
// easy to export.
|
||||
pub content: Vec<JsonValue>,
|
||||
pub structured_content: Option<JsonValue>,
|
||||
}
|
||||
|
||||
|
||||
+55
-52
@@ -13,17 +13,17 @@ pub mod exec_events;
|
||||
pub use cli::Cli;
|
||||
pub use cli::Command;
|
||||
pub use cli::ReviewArgs;
|
||||
use codex_cloud_requirements::cloud_requirements_loader;
|
||||
use codex_common::oss::ensure_oss_provider_ready;
|
||||
use codex_common::oss::get_default_model_for_oss_provider;
|
||||
use codex_common::oss::ollama_chat_deprecation_notice;
|
||||
use codex_core::AuthManager;
|
||||
use codex_core::LMSTUDIO_OSS_PROVIDER_ID;
|
||||
use codex_core::NewThread;
|
||||
use codex_core::OLLAMA_CHAT_PROVIDER_ID;
|
||||
use codex_core::OLLAMA_OSS_PROVIDER_ID;
|
||||
use codex_core::ThreadManager;
|
||||
use codex_core::auth::enforce_login_restrictions;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::config::ConfigBuilder;
|
||||
use codex_core::config::ConfigOverrides;
|
||||
use codex_core::config::find_codex_home;
|
||||
use codex_core::config::load_config_as_toml_with_cli_overrides;
|
||||
@@ -64,6 +64,7 @@ use uuid::Uuid;
|
||||
use crate::cli::Command as ExecCommand;
|
||||
use crate::event_processor::CodexStatus;
|
||||
use crate::event_processor::EventProcessor;
|
||||
use codex_core::default_client::set_default_client_residency_requirement;
|
||||
use codex_core::default_client::set_default_originator;
|
||||
use codex_core::find_thread_path_by_id_str;
|
||||
use codex_core::find_thread_path_by_name_str;
|
||||
@@ -160,41 +161,52 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
|
||||
|
||||
// we load config.toml here to determine project state.
|
||||
#[allow(clippy::print_stderr)]
|
||||
let config_toml = {
|
||||
let codex_home = match find_codex_home() {
|
||||
Ok(codex_home) => codex_home,
|
||||
Err(err) => {
|
||||
eprintln!("Error finding codex home: {err}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
match load_config_as_toml_with_cli_overrides(
|
||||
&codex_home,
|
||||
&config_cwd,
|
||||
cli_kv_overrides.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(config_toml) => config_toml,
|
||||
Err(err) => {
|
||||
let config_error = err
|
||||
.get_ref()
|
||||
.and_then(|err| err.downcast_ref::<ConfigLoadError>())
|
||||
.map(ConfigLoadError::config_error);
|
||||
if let Some(config_error) = config_error {
|
||||
eprintln!(
|
||||
"Error loading config.toml:\n{}",
|
||||
format_config_error_with_source(config_error)
|
||||
);
|
||||
} else {
|
||||
eprintln!("Error loading config.toml: {err}");
|
||||
}
|
||||
std::process::exit(1);
|
||||
}
|
||||
let codex_home = match find_codex_home() {
|
||||
Ok(codex_home) => codex_home,
|
||||
Err(err) => {
|
||||
eprintln!("Error finding codex home: {err}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
#[allow(clippy::print_stderr)]
|
||||
let config_toml = match load_config_as_toml_with_cli_overrides(
|
||||
&codex_home,
|
||||
&config_cwd,
|
||||
cli_kv_overrides.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(config_toml) => config_toml,
|
||||
Err(err) => {
|
||||
let config_error = err
|
||||
.get_ref()
|
||||
.and_then(|err| err.downcast_ref::<ConfigLoadError>())
|
||||
.map(ConfigLoadError::config_error);
|
||||
if let Some(config_error) = config_error {
|
||||
eprintln!(
|
||||
"Error loading config.toml:\n{}",
|
||||
format_config_error_with_source(config_error)
|
||||
);
|
||||
} else {
|
||||
eprintln!("Error loading config.toml: {err}");
|
||||
}
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let cloud_auth_manager = AuthManager::shared(
|
||||
codex_home.clone(),
|
||||
false,
|
||||
config_toml.cli_auth_credentials_store.unwrap_or_default(),
|
||||
);
|
||||
let chatgpt_base_url = config_toml
|
||||
.chatgpt_base_url
|
||||
.clone()
|
||||
.unwrap_or_else(|| "https://chatgpt.com/backend-api/".to_string());
|
||||
// TODO(gt): Make cloud requirements failures blocking once we can fail-closed.
|
||||
let cloud_requirements = cloud_requirements_loader(cloud_auth_manager, chatgpt_base_url);
|
||||
|
||||
let model_provider = if oss {
|
||||
let resolved = resolve_oss_provider(
|
||||
oss_provider.as_deref(),
|
||||
@@ -206,7 +218,7 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
|
||||
Some(provider)
|
||||
} else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"No default OSS provider configured. Use --local-provider=provider or set oss_provider to one of: {LMSTUDIO_OSS_PROVIDER_ID}, {OLLAMA_OSS_PROVIDER_ID}, {OLLAMA_CHAT_PROVIDER_ID} in config.toml"
|
||||
"No default OSS provider configured. Use --local-provider=provider or set oss_provider to one of: {LMSTUDIO_OSS_PROVIDER_ID}, {OLLAMA_OSS_PROVIDER_ID} in config.toml"
|
||||
));
|
||||
}
|
||||
} else {
|
||||
@@ -238,7 +250,7 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
|
||||
codex_linux_sandbox_exe,
|
||||
base_instructions: None,
|
||||
developer_instructions: None,
|
||||
model_personality: None,
|
||||
personality: None,
|
||||
compact_prompt: None,
|
||||
include_apply_patch_tool: None,
|
||||
show_raw_agent_reasoning: oss.then_some(true),
|
||||
@@ -247,22 +259,19 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
|
||||
additional_writable_roots: add_dir,
|
||||
};
|
||||
|
||||
let config =
|
||||
Config::load_with_cli_overrides_and_harness_overrides(cli_kv_overrides, overrides).await?;
|
||||
let config = ConfigBuilder::default()
|
||||
.cli_overrides(cli_kv_overrides)
|
||||
.harness_overrides(overrides)
|
||||
.cloud_requirements(cloud_requirements)
|
||||
.build()
|
||||
.await?;
|
||||
set_default_client_residency_requirement(config.enforce_residency.value());
|
||||
|
||||
if let Err(err) = enforce_login_restrictions(&config) {
|
||||
eprintln!("{err}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let ollama_chat_support_notice = match ollama_chat_deprecation_notice(&config).await {
|
||||
Ok(notice) => notice,
|
||||
Err(err) => {
|
||||
tracing::warn!(?err, "Failed to detect Ollama wire API");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let otel = match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
||||
codex_core::otel_init::build_provider(&config, env!("CARGO_PKG_VERSION"), None, false)
|
||||
})) {
|
||||
@@ -295,12 +304,6 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
|
||||
last_message_file.clone(),
|
||||
)),
|
||||
};
|
||||
if let Some(notice) = ollama_chat_support_notice {
|
||||
event_processor.process_event(Event {
|
||||
id: String::new(),
|
||||
msg: EventMsg::DeprecationNotice(notice),
|
||||
});
|
||||
}
|
||||
|
||||
if oss {
|
||||
// We're in the oss section, so provider_id should be Some
|
||||
|
||||
Reference in New Issue
Block a user