mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Move message history out of core (#21278)
## Why Message history was implemented inside `codex-core` and surfaced through core protocol ops and `SessionConfiguredEvent` fields even though the current consumer is TUI-local prompt recall. That made core own UI history persistence and exposed `history_log_id` / `history_entry_count` through surfaces that app-server and other clients do not need. This change moves message history persistence out of core and keeps the recall plumbing local to the TUI. ## What changed - Added a new `codex-message-history` crate for appending, looking up, trimming, and reading metadata from `history.jsonl`. - Removed core protocol history ops/events: `AddToHistory`, `GetHistoryEntryRequest`, and `GetHistoryEntryResponse`. - Removed `history_log_id` and `history_entry_count` from `SessionConfiguredEvent` and updated exec/MCP/test fixtures accordingly. - Updated the TUI to dispatch local app events for message-history append/lookup and keep its persistent-history metadata in TUI session state. ## Validation - `cargo test -p codex-message-history -p codex-protocol` - `cargo test -p codex-exec event_processor_with_json_output` - `cargo test -p codex-mcp-server outgoing_message` - `cargo test -p codex-tui` - `just fix -p codex-message-history -p codex-protocol -p codex-core -p codex-tui -p codex-exec -p codex-mcp-server`
This commit is contained in:
@@ -460,53 +460,6 @@ pub async fn dynamic_tool_response(sess: &Arc<Session>, id: String, response: Dy
|
||||
sess.notify_dynamic_tool_response(&id, response).await;
|
||||
}
|
||||
|
||||
pub async fn add_to_history(sess: &Arc<Session>, config: &Arc<Config>, text: String) {
|
||||
let id = sess.conversation_id;
|
||||
let config = Arc::clone(config);
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = crate::message_history::append_entry(&text, &id, &config).await {
|
||||
warn!("failed to append to message history: {e}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub async fn get_history_entry_request(
|
||||
sess: &Arc<Session>,
|
||||
config: &Arc<Config>,
|
||||
sub_id: String,
|
||||
offset: usize,
|
||||
log_id: u64,
|
||||
) {
|
||||
let config = Arc::clone(config);
|
||||
let sess_clone = Arc::clone(sess);
|
||||
|
||||
tokio::spawn(async move {
|
||||
// Run lookup in blocking thread because it does file IO + locking.
|
||||
let entry_opt = tokio::task::spawn_blocking(move || {
|
||||
crate::message_history::lookup(log_id, offset, &config)
|
||||
})
|
||||
.await
|
||||
.unwrap_or(None);
|
||||
|
||||
let event = Event {
|
||||
id: sub_id,
|
||||
msg: EventMsg::GetHistoryEntryResponse(
|
||||
codex_protocol::protocol::GetHistoryEntryResponseEvent {
|
||||
offset,
|
||||
log_id,
|
||||
entry: entry_opt.map(|e| codex_protocol::message_history::HistoryEntry {
|
||||
conversation_id: e.session_id,
|
||||
ts: e.ts,
|
||||
text: e.text,
|
||||
}),
|
||||
},
|
||||
),
|
||||
};
|
||||
|
||||
sess_clone.send_event_raw(event).await;
|
||||
});
|
||||
}
|
||||
|
||||
pub async fn refresh_mcp_servers(sess: &Arc<Session>, refresh_config: McpServerRefreshConfig) {
|
||||
let mut guard = sess.pending_mcp_server_refresh_config.lock().await;
|
||||
*guard = Some(refresh_config);
|
||||
@@ -910,14 +863,6 @@ pub(super) async fn submission_loop(
|
||||
dynamic_tool_response(&sess, id, response).await;
|
||||
false
|
||||
}
|
||||
Op::AddToHistory { text } => {
|
||||
add_to_history(&sess, &config, text).await;
|
||||
false
|
||||
}
|
||||
Op::GetHistoryEntryRequest { offset, log_id } => {
|
||||
get_history_entry_request(&sess, &config, sub.id.clone(), offset, log_id).await;
|
||||
false
|
||||
}
|
||||
Op::ListMcpTools => {
|
||||
list_mcp_tools(&sess, &config, sub.id.clone()).await;
|
||||
false
|
||||
|
||||
@@ -470,19 +470,6 @@ impl Session {
|
||||
));
|
||||
let state_db_ctx = if config.ephemeral { None } else { state_db };
|
||||
|
||||
let is_subagent = session_configuration.session_source.is_non_root_agent();
|
||||
let history_meta_fut = async {
|
||||
if is_subagent {
|
||||
(0, 0)
|
||||
} else {
|
||||
crate::message_history::history_metadata(&config).await
|
||||
}
|
||||
}
|
||||
.instrument(info_span!(
|
||||
"session_init.history_metadata",
|
||||
otel.name = "session_init.history_metadata",
|
||||
session_init.is_subagent = is_subagent,
|
||||
));
|
||||
let auth_manager_clone = Arc::clone(&auth_manager);
|
||||
let config_for_mcp = Arc::clone(&config);
|
||||
let mcp_manager_for_mcp = Arc::clone(&mcp_manager);
|
||||
@@ -505,11 +492,8 @@ impl Session {
|
||||
));
|
||||
|
||||
// Join all independent futures.
|
||||
let (
|
||||
thread_persistence_result,
|
||||
(history_log_id, history_entry_count),
|
||||
(auth, mcp_servers, auth_statuses),
|
||||
) = tokio::join!(thread_persistence_fut, history_meta_fut, auth_and_mcp_fut);
|
||||
let (thread_persistence_result, (auth, mcp_servers, auth_statuses)) =
|
||||
tokio::join!(thread_persistence_fut, auth_and_mcp_fut);
|
||||
|
||||
let mut live_thread_init =
|
||||
LiveThreadInitGuard::new(thread_persistence_result.map_err(|e| {
|
||||
@@ -920,8 +904,6 @@ impl Session {
|
||||
active_permission_profile: session_configuration.active_permission_profile(),
|
||||
cwd: session_configuration.cwd.clone(),
|
||||
reasoning_effort: session_configuration.collaboration_mode.reasoning_effort(),
|
||||
history_log_id,
|
||||
history_entry_count,
|
||||
initial_messages,
|
||||
network_proxy: session_network_proxy.filter(|_| {
|
||||
Self::managed_network_proxy_active_for_permission_profile(
|
||||
|
||||
@@ -1509,7 +1509,6 @@ pub(super) fn realtime_text_for_event(msg: &EventMsg) -> Option<String> {
|
||||
| EventMsg::DeprecationNotice(_)
|
||||
| EventMsg::StreamError(_)
|
||||
| EventMsg::TurnDiff(_)
|
||||
| EventMsg::GetHistoryEntryResponse(_)
|
||||
| EventMsg::McpListToolsResponse(_)
|
||||
| EventMsg::RealtimeConversationListVoicesResponse(_)
|
||||
| EventMsg::SkillsUpdateAvailable
|
||||
|
||||
Reference in New Issue
Block a user