Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ impl ExternalAgentSessionImporter {
selected_capability_roots: Vec::new(),
multi_agent_version: Some(MultiAgentVersion::V1),
history_mode: ThreadHistoryMode::Legacy,
subagent_history_start_ordinal: None,
initial_window_id: uuid::Uuid::now_v7().to_string(),
metadata: ThreadPersistenceMetadata {
cwd: Some(cwd.clone()),
Expand Down
2 changes: 2 additions & 0 deletions codex-rs/app-server/tests/common/rollout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ fn create_fake_rollout_with_source_and_parent_thread_id(
selected_capability_roots: Vec::new(),
memory_mode: None,
history_mode: Default::default(),
subagent_history_start_ordinal: None,
multi_agent_version: None,
context_window: None,
};
Expand Down Expand Up @@ -330,6 +331,7 @@ pub fn create_fake_rollout_with_text_elements(
selected_capability_roots: Vec::new(),
memory_mode: None,
history_mode: Default::default(),
subagent_history_start_ordinal: None,
multi_agent_version: None,
context_window: None,
};
Expand Down
1 change: 1 addition & 0 deletions codex-rs/app-server/tests/suite/conversation_summary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ async fn get_conversation_summary_by_thread_id_reads_pathless_store_thread() ->
selected_capability_roots: Vec::new(),
multi_agent_version: None,
history_mode: Default::default(),
subagent_history_start_ordinal: None,
initial_window_id: Uuid::now_v7().to_string(),
metadata: ThreadPersistenceMetadata {
cwd: None,
Expand Down
1 change: 1 addition & 0 deletions codex-rs/app-server/tests/suite/v2/remote_thread_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ async fn thread_delete_with_non_local_thread_store_does_not_create_local_persist
selected_capability_roots: Vec::new(),
multi_agent_version: None,
history_mode: Default::default(),
subagent_history_start_ordinal: None,
initial_window_id: Uuid::now_v7().to_string(),
metadata: ThreadPersistenceMetadata {
cwd: Some(codex_home.path().to_path_buf()),
Expand Down
2 changes: 2 additions & 0 deletions codex-rs/app-server/tests/suite/v2/thread_read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1339,6 +1339,7 @@ async fn paginated_history_lists_use_projected_turns_and_items() -> Result<()> {
selected_capability_roots: Vec::new(),
multi_agent_version: None,
history_mode: codex_protocol::protocol::ThreadHistoryMode::Paginated,
subagent_history_start_ordinal: None,
initial_window_id: Uuid::now_v7().to_string(),
metadata: ThreadPersistenceMetadata {
cwd: Some(codex_home.path().to_path_buf()),
Expand Down Expand Up @@ -1937,6 +1938,7 @@ async fn seed_pathless_store_thread(
selected_capability_roots: Vec::new(),
multi_agent_version: None,
history_mode: Default::default(),
subagent_history_start_ordinal: None,
initial_window_id: Uuid::now_v7().to_string(),
metadata: ThreadPersistenceMetadata {
cwd: None,
Expand Down
1 change: 1 addition & 0 deletions codex-rs/app-server/tests/suite/v2/thread_resume.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2589,6 +2589,7 @@ stream_max_retries = 0
selected_capability_roots: Vec::new(),
memory_mode: None,
history_mode: Default::default(),
subagent_history_start_ordinal: None,
multi_agent_version: None,
context_window: None,
};
Expand Down
1 change: 1 addition & 0 deletions codex-rs/app-server/tests/suite/v2/thread_unarchive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ async fn thread_unarchive_preserves_pathless_store_metadata() -> Result<()> {
selected_capability_roots: Vec::new(),
multi_agent_version: None,
history_mode: Default::default(),
subagent_history_start_ordinal: None,
initial_window_id: Uuid::now_v7().to_string(),
metadata: ThreadPersistenceMetadata {
cwd: None,
Expand Down
3 changes: 3 additions & 0 deletions codex-rs/core/src/agent/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use codex_protocol::error::Result as CodexResult;
use codex_protocol::models::ContentItem;
use codex_protocol::models::MessagePhase;
use codex_protocol::models::ResponseItem;
use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::InitialHistory;
use codex_protocol::protocol::InterAgentCommunication;
use codex_protocol::protocol::MultiAgentVersion;
Expand All @@ -34,9 +35,11 @@ use codex_protocol::protocol::ResumedHistory;
use codex_protocol::protocol::RolloutItem;
use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::SubAgentSource;
use codex_protocol::protocol::ThreadHistoryMode;
use codex_protocol::protocol::ThreadSource;
use codex_protocol::protocol::TurnEnvironmentSelection;
use codex_protocol::user_input::UserInput;
use codex_thread_store::LoadThreadHistoryParams;
use codex_thread_store::ReadThreadParams;
use serde::Serialize;
use std::collections::HashMap;
Expand Down
1 change: 1 addition & 0 deletions codex-rs/core/src/agent/control/residency_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ async fn spawn_v2_subagent(
config,
control.clone(),
SessionSource::SubAgent(SubAgentSource::Other(label.to_string())),
/*history_mode*/ None,
Some(parent_thread_id),
/*forked_from_thread_id*/ None,
Some(ThreadSource::Subagent),
Expand Down
100 changes: 77 additions & 23 deletions codex-rs/core/src/agent/control/spawn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,33 @@ fn is_multi_agent_v2_usage_hint_message(item: &ResponseItem, usage_hint_texts: &
.any(|usage_hint_text| usage_hint_text == text)
}

async fn load_agent_model_context(
state: &ThreadManagerState,
thread_id: ThreadId,
history_mode: ThreadHistoryMode,
) -> CodexResult<Option<Vec<RolloutItem>>> {
match history_mode {
ThreadHistoryMode::Legacy => Ok(state
.read_stored_thread(ReadThreadParams {
thread_id,
include_archived: true,
include_history: true,
})
.await?
.history
.map(|history| history.items)),
ThreadHistoryMode::Paginated => Ok(Some(
state
.load_latest_model_context(LoadThreadHistoryParams {
thread_id,
include_archived: true,
})
.await?
.items,
)),
}
}

impl AgentControl {
/// Restore persisted V2 agent identities without reopening their runtimes.
pub(crate) async fn restore_v2_agent_metadata(
Expand Down Expand Up @@ -236,15 +263,14 @@ impl AgentControl {
.read_stored_thread(ReadThreadParams {
thread_id,
include_archived: true,
include_history: true,
include_history: false,
})
.await?;
let stored_source = stored_thread.source.clone();
let stored_parent_thread_id = stored_thread.parent_thread_id;
let history = stored_thread
.history
.ok_or(CodexErr::ThreadNotFound(thread_id))?
.items;
let history = load_agent_model_context(&state, thread_id, stored_thread.history_mode)
.await?
.ok_or(CodexErr::ThreadNotFound(thread_id))?;
let initial_history = InitialHistory::Resumed(ResumedHistory {
conversation_id: thread_id,
history: Arc::new(history),
Expand Down Expand Up @@ -382,10 +408,22 @@ impl AgentControl {
.await?
}
(Some(session_source), None, inheritance) => {
let history_mode = if let Some(parent_thread_id) = options.parent_thread_id
&& let Ok(parent_thread) = state.get_thread(parent_thread_id).await
{
matches!(
parent_thread.config_snapshot().await.history_mode,
ThreadHistoryMode::Paginated
)
.then_some(ThreadHistoryMode::Paginated)
} else {
None
};
Box::pin(state.spawn_new_thread_with_source(
config.clone(),
self.clone(),
session_source,
history_mode,
options.parent_thread_id,
/*forked_from_thread_id*/ None,
/*thread_source*/ Some(ThreadSource::Subagent),
Expand Down Expand Up @@ -525,23 +563,27 @@ impl AgentControl {
parent_thread.ensure_rollout_materialized().await;
parent_thread.flush_rollout().await?;
}

let parent_history = state
let parent_metadata = state
.read_stored_thread(ReadThreadParams {
thread_id: parent_thread_id,
include_archived: true,
include_history: true,
include_history: false,
})
.await?
.history
.ok_or_else(|| {
CodexErr::Fatal(format!(
"parent thread history unavailable for fork: {parent_thread_id}"
))
})?;
.await?;

let destination_history_mode =
matches!(parent_metadata.history_mode, ThreadHistoryMode::Paginated)
.then_some(ThreadHistoryMode::Paginated);
let mut forked_rollout_items =
load_agent_model_context(state, parent_thread_id, parent_metadata.history_mode)
.await?
.ok_or_else(|| {
CodexErr::Fatal(format!(
"parent thread history unavailable for fork: {parent_thread_id}"
))
})?;

let selected_capability_roots = parent_history
.items
let selected_capability_roots = forked_rollout_items
.iter()
.find_map(|item| {
let RolloutItem::SessionMeta(meta_line) = item else {
Expand All @@ -550,7 +592,6 @@ impl AgentControl {
Some(meta_line.meta.selected_capability_roots.clone())
})
.unwrap_or_default();
let mut forked_rollout_items = parent_history.items;
if let SpawnAgentForkMode::LastNTurns(last_n_turns) = fork_mode {
forked_rollout_items =
truncate_rollout_to_last_n_fork_turns(&forked_rollout_items, *last_n_turns);
Expand Down Expand Up @@ -598,6 +639,19 @@ impl AgentControl {
)
)
});
if destination_history_mode == Some(ThreadHistoryMode::Paginated) {
forked_rollout_items.retain(|item| {
!matches!(
item,
RolloutItem::EventMsg(
EventMsg::ItemCompleted(_)
| EventMsg::TokenCount(_)
| EventMsg::ThreadGoalUpdated(_)
| EventMsg::ThreadSettingsApplied(_),
)
)
});
}
for item in &mut forked_rollout_items {
if let RolloutItem::Compacted(compacted) = item
&& let Some(replacement_history) = compacted.replacement_history.as_mut()
Expand Down Expand Up @@ -628,6 +682,7 @@ impl AgentControl {
.fork_thread_with_source(
config.clone(),
InitialHistory::Forked(forked_rollout_items),
destination_history_mode,
self.clone(),
session_source,
/*thread_source*/ Some(ThreadSource::Subagent),
Expand Down Expand Up @@ -728,7 +783,7 @@ impl AgentControl {
.read_stored_thread(ReadThreadParams {
thread_id,
include_archived: true,
include_history: true,
include_history: false,
})
.await?;
let resumed_agent_path = stored_thread
Expand All @@ -739,10 +794,9 @@ impl AgentControl {
.map_err(|err| CodexErr::InvalidRequest(format!("invalid stored agent path: {err}")))?;
let resumed_agent_nickname = stored_thread.agent_nickname.clone();
let resumed_agent_role = stored_thread.agent_role.clone();
let history = stored_thread
.history
.ok_or_else(|| CodexErr::ThreadNotFound(thread_id))?
.items;
let history = load_agent_model_context(&state, thread_id, stored_thread.history_mode)
.await?
.ok_or(CodexErr::ThreadNotFound(thread_id))?;
let initial_history = InitialHistory::Resumed(ResumedHistory {
conversation_id: thread_id,
history: Arc::new(history),
Expand Down
Loading
Loading