diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock
index 873d0a222a..5a333c2616 100644
--- a/codex-rs/Cargo.lock
+++ b/codex-rs/Cargo.lock
@@ -298,17 +298,6 @@ dependencies = [
"syn 2.0.104",
]
-[[package]]
-name = "async-trait"
-version = "0.1.88"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.104",
-]
-
[[package]]
name = "atomic-waker"
version = "1.1.2"
@@ -1279,12 +1268,12 @@ dependencies = [
[[package]]
name = "deadpool"
-version = "0.10.0"
+version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb84100978c1c7b37f09ed3ce3e5f843af02c2a2c431bae5b19230dad2c1b490"
+checksum = "0be2b1d1d6ec8d846f05e137292d0b89133caf95ef33695424c09568bdd39b1b"
dependencies = [
- "async-trait",
"deadpool-runtime",
+ "lazy_static",
"num_cpus",
"tokio",
]
@@ -2131,13 +2120,14 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
-version = "1.6.0"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80"
+checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e"
dependencies = [
+ "atomic-waker",
"bytes",
"futures-channel",
- "futures-util",
+ "futures-core",
"h2",
"http",
"http-body",
@@ -2145,6 +2135,7 @@ dependencies = [
"httpdate",
"itoa",
"pin-project-lite",
+ "pin-utils",
"smallvec",
"tokio",
"want",
@@ -6272,12 +6263,11 @@ checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
[[package]]
name = "wiremock"
-version = "0.6.4"
+version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2b8b99d4cdbf36b239a9532e31fe4fb8acc38d1897c1761e161550a7dc78e6a"
+checksum = "08db1edfb05d9b3c1542e521aea074442088292f00b5f28e435c714a98f85031"
dependencies = [
"assert-json-diff",
- "async-trait",
"base64 0.22.1",
"deadpool",
"futures",
diff --git a/codex-rs/core/src/client_common.rs b/codex-rs/core/src/client_common.rs
index e326ea3324..d561d36946 100644
--- a/codex-rs/core/src/client_common.rs
+++ b/codex-rs/core/src/client_common.rs
@@ -6,7 +6,6 @@ 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 codex_protocol::config_types::Verbosity as VerbosityConfig;
-use codex_protocol::models::ContentItem;
use codex_protocol::models::ResponseItem;
use futures::Stream;
use serde::Serialize;
@@ -20,10 +19,6 @@ use tokio::sync::mpsc;
/// with this content.
const BASE_INSTRUCTIONS: &str = include_str!("../prompt.md");
-/// wraps user instructions message in a tag for the model to parse more easily.
-const USER_INSTRUCTIONS_START: &str = "\n\n";
-const USER_INSTRUCTIONS_END: &str = "\n\n";
-
/// API request payload for a single model turn
#[derive(Default, Debug, Clone)]
pub struct Prompt {
@@ -68,17 +63,6 @@ impl Prompt {
pub(crate) fn get_formatted_input(&self) -> Vec {
self.input.clone()
}
-
- /// Creates a formatted user instructions message from a string
- pub(crate) fn format_user_instructions_message(ui: &str) -> ResponseItem {
- ResponseItem::Message {
- id: None,
- role: "user".to_string(),
- content: vec![ContentItem::InputText {
- text: format!("{USER_INSTRUCTIONS_START}{ui}{USER_INSTRUCTIONS_END}"),
- }],
- }
- }
}
#[derive(Debug)]
diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs
index 36c48020bc..2028c4240e 100644
--- a/codex-rs/core/src/codex.rs
+++ b/codex-rs/core/src/codex.rs
@@ -9,6 +9,7 @@ use std::sync::atomic::AtomicU64;
use std::time::Duration;
use crate::AuthManager;
+use crate::event_mapping::map_response_item_to_event_messages;
use async_channel::Receiver;
use async_channel::Sender;
use codex_apply_patch::ApplyPatchAction;
@@ -75,9 +76,7 @@ use crate::project_doc::get_user_instructions;
use crate::protocol::AgentMessageDeltaEvent;
use crate::protocol::AgentMessageEvent;
use crate::protocol::AgentReasoningDeltaEvent;
-use crate::protocol::AgentReasoningEvent;
use crate::protocol::AgentReasoningRawContentDeltaEvent;
-use crate::protocol::AgentReasoningRawContentEvent;
use crate::protocol::AgentReasoningSectionBreakEvent;
use crate::protocol::ApplyPatchApprovalRequestEvent;
use crate::protocol::AskForApproval;
@@ -102,13 +101,13 @@ use crate::protocol::Submission;
use crate::protocol::TaskCompleteEvent;
use crate::protocol::TurnDiffEvent;
use crate::protocol::WebSearchBeginEvent;
-use crate::protocol::WebSearchEndEvent;
use crate::rollout::RolloutRecorder;
use crate::safety::SafetyCheck;
use crate::safety::assess_command_safety;
use crate::safety::assess_safety_for_untrusted_command;
use crate::shell;
use crate::turn_diff_tracker::TurnDiffTracker;
+use crate::user_instructions::UserInstructions;
use crate::user_notification::UserNotification;
use crate::util::backoff;
use codex_protocol::config_types::ReasoningEffort as ReasoningEffortConfig;
@@ -117,12 +116,9 @@ use codex_protocol::custom_prompts::CustomPrompt;
use codex_protocol::models::ContentItem;
use codex_protocol::models::FunctionCallOutputPayload;
use codex_protocol::models::LocalShellAction;
-use codex_protocol::models::ReasoningItemContent;
-use codex_protocol::models::ReasoningItemReasoningSummary;
use codex_protocol::models::ResponseInputItem;
use codex_protocol::models::ResponseItem;
use codex_protocol::models::ShellToolCallParams;
-use codex_protocol::models::WebSearchAction;
// A convenience extension trait for acquiring mutex locks where poisoning is
// unrecoverable and should abort the program. This avoids scattered `.unwrap()`
@@ -199,6 +195,7 @@ impl Codex {
config.clone(),
auth_manager.clone(),
tx_event.clone(),
+ conversation_history.clone(),
)
.await
.map_err(|e| {
@@ -361,6 +358,7 @@ impl Session {
config: Arc,
auth_manager: Arc,
tx_event: Sender,
+ initial_history: InitialHistory,
) -> anyhow::Result<(Arc, TurnContext)> {
let session_id = Uuid::new_v4();
let ConfigureSession {
@@ -480,6 +478,12 @@ impl Session {
});
// Dispatch the SessionConfiguredEvent first and then report any errors.
+ // If resuming, include converted initial messages in the payload so UIs can render them immediately.
+ let initial_messages = match &initial_history {
+ InitialHistory::New => None,
+ InitialHistory::Resumed(items) => Some(sess.build_initial_messages(items)),
+ };
+
let events = std::iter::once(Event {
id: INITIAL_SUBMIT_ID.to_owned(),
msg: EventMsg::SessionConfigured(SessionConfiguredEvent {
@@ -487,6 +491,7 @@ impl Session {
model,
history_log_id,
history_entry_count,
+ initial_messages,
}),
})
.chain(post_session_configured_error_events.into_iter());
@@ -537,7 +542,7 @@ impl Session {
// TODO: Those items shouldn't be "user messages" IMO. Maybe developer messages.
let mut conversation_items = Vec::::with_capacity(2);
if let Some(user_instructions) = turn_context.user_instructions.as_deref() {
- conversation_items.push(Prompt::format_user_instructions_message(user_instructions));
+ conversation_items.push(UserInstructions::new(user_instructions.to_string()).into());
}
conversation_items.push(ResponseItem::from(EnvironmentContext::new(
Some(turn_context.cwd.clone()),
@@ -552,6 +557,17 @@ impl Session {
self.record_conversation_items(&items).await;
}
+ /// build the initial messages vector for SessionConfigured by converting
+ /// ResponseItems into EventMsg.
+ fn build_initial_messages(&self, items: &[ResponseItem]) -> Vec {
+ items
+ .iter()
+ .flat_map(|item| {
+ map_response_item_to_event_messages(item, self.show_raw_agent_reasoning)
+ })
+ .collect()
+ }
+
/// Sends the given event to the client and swallows the send event, if
/// any, logging it as an error.
pub(crate) async fn send_event(&self, event: Event) {
@@ -1915,53 +1931,6 @@ async fn handle_response_item(
) -> CodexResult