mirror of
https://github.com/openai/codex.git
synced 2026-06-01 19:02:59 +00:00
feat: persist and restore codex app's tools after search (#11780)
### What changed 1. Removed per-turn MCP selection reset in `core/src/tasks/mod.rs`. 2. Added `SessionState::set_mcp_tool_selection(Vec<String>)` in `core/src/state/session.rs` for authoritative restore behavior (deduped, order-preserving, empty clears). 3. Added rollout parsing in `core/src/codex.rs` to recover `active_selected_tools` from prior `search_tool_bm25` outputs: - tracks matching `call_id`s - parses function output text JSON - extracts `active_selected_tools` - latest valid payload wins - malformed/non-matching payloads are ignored 4. Applied restore logic to resumed and forked startup paths in `core/src/codex.rs`. 5. Updated instruction text to session/thread scope in `core/templates/search_tool/tool_description.md`. 6. Expanded tests in `core/tests/suite/search_tool.rs`, plus unit coverage in: - `core/src/codex.rs` - `core/src/state/session.rs` ### Behavior after change 1. Search activates matched tools. 2. Additional searches union into active selection. 3. Selection survives new turns in the same thread. 4. Resume/fork restores selection from rollout history. 5. Separate threads do not inherit selection unless forked.
This commit is contained in:
@@ -170,6 +170,27 @@ impl SessionState {
|
||||
merged
|
||||
}
|
||||
|
||||
pub(crate) fn set_mcp_tool_selection(&mut self, tool_names: Vec<String>) {
|
||||
if tool_names.is_empty() {
|
||||
self.active_mcp_tool_selection = None;
|
||||
return;
|
||||
}
|
||||
|
||||
let mut selected = Vec::new();
|
||||
let mut seen = HashSet::new();
|
||||
for tool_name in tool_names {
|
||||
if seen.insert(tool_name.clone()) {
|
||||
selected.push(tool_name);
|
||||
}
|
||||
}
|
||||
|
||||
self.active_mcp_tool_selection = if selected.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(selected)
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) fn get_mcp_tool_selection(&self) -> Option<Vec<String>> {
|
||||
self.active_mcp_tool_selection.clone()
|
||||
}
|
||||
@@ -293,6 +314,40 @@ mod tests {
|
||||
assert_eq!(state.get_mcp_tool_selection(), None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn set_mcp_tool_selection_deduplicates_and_preserves_order() {
|
||||
let session_configuration = make_session_configuration_for_tests().await;
|
||||
let mut state = SessionState::new(session_configuration);
|
||||
state.merge_mcp_tool_selection(vec!["mcp__rmcp__old".to_string()]);
|
||||
|
||||
state.set_mcp_tool_selection(vec![
|
||||
"mcp__rmcp__echo".to_string(),
|
||||
"mcp__rmcp__image".to_string(),
|
||||
"mcp__rmcp__echo".to_string(),
|
||||
"mcp__rmcp__search".to_string(),
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
state.get_mcp_tool_selection(),
|
||||
Some(vec![
|
||||
"mcp__rmcp__echo".to_string(),
|
||||
"mcp__rmcp__image".to_string(),
|
||||
"mcp__rmcp__search".to_string(),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn set_mcp_tool_selection_empty_input_clears_selection() {
|
||||
let session_configuration = make_session_configuration_for_tests().await;
|
||||
let mut state = SessionState::new(session_configuration);
|
||||
state.merge_mcp_tool_selection(vec!["mcp__rmcp__echo".to_string()]);
|
||||
|
||||
state.set_mcp_tool_selection(Vec::new());
|
||||
|
||||
assert_eq!(state.get_mcp_tool_selection(), None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
// Verifies connector merging deduplicates repeated IDs.
|
||||
async fn merge_connector_selection_deduplicates_entries() {
|
||||
|
||||
Reference in New Issue
Block a user