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:
pap
2026-02-04 13:43:28 +00:00
1027 changed files with 120148 additions and 15963 deletions
+1 -1
View File
@@ -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,
}
+8 -2
View File
@@ -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
View File
@@ -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