code-mode: merge stored values by key (#24159)

## Summary

Change code-mode stored value updates to merge writes by key instead of
replacing the session's complete stored-value map after each cell
completes.

Previously, each cell received a snapshot of stored values and returned
the complete resulting map. When multiple cells ran concurrently, a
later completion could overwrite values written by another cell because
it committed an older snapshot.

This change moves stored-value ownership into `CodeModeService`:

- Each runtime starts from the service's current stored values.
- Runtime completion reports only keys written by that cell.
- The service merges those writes into the current stored-value map on
successful completion.
- Core no longer replaces its stored-value state from a cell result.

As a result, concurrently executing cells can update different stored
keys without clobbering one another.

The move into CodeModeService is motivated by a desire to have this
lifetime tied to a new lifetime object on that side in a subsequent PR.
This commit is contained in:
Channing Conger
2026-05-22 19:09:02 -07:00
committed by GitHub
parent 0febb1100f
commit f94157a4b2
7 changed files with 188 additions and 77 deletions
@@ -38,12 +38,6 @@ impl CodeModeExecuteHandler {
let exec = ExecContext { session, turn };
let enabled_tools =
codex_tools::collect_code_mode_tool_definitions(&self.nested_tool_specs);
let stored_values = exec
.session
.services
.code_mode_service
.stored_values()
.await;
// Allocate before starting V8 so the trace can create the parent
// CodeCell before model-authored JavaScript issues nested tool calls.
let runtime_cell_id = exec.session.services.code_mode_service.allocate_cell_id();
@@ -67,7 +61,6 @@ impl CodeModeExecuteHandler {
tool_call_id: call_id,
enabled_tools,
source: args.code,
stored_values,
yield_time_ms: args.yield_time_ms,
max_output_tokens: args.max_output_tokens,
})
-17
View File
@@ -66,17 +66,6 @@ impl CodeModeService {
}
}
pub(crate) async fn stored_values(&self) -> std::collections::HashMap<String, JsonValue> {
self.inner.stored_values().await
}
pub(crate) async fn replace_stored_values(
&self,
values: std::collections::HashMap<String, JsonValue>,
) {
self.inner.replace_stored_values(values).await;
}
pub(crate) fn allocate_cell_id(&self) -> String {
self.inner.allocate_cell_id()
}
@@ -182,17 +171,11 @@ pub(super) async fn handle_runtime_response(
}
RuntimeResponse::Result {
content_items,
stored_values,
error_text,
..
} => {
let mut content_items = into_function_call_output_content_items(content_items);
sanitize_runtime_image_detail(exec.turn.as_ref(), &mut content_items);
exec.session
.services
.code_mode_service
.replace_stored_values(stored_values)
.await;
let success = error_text.is_none();
if let Some(error_text) = error_text {
content_items.push(FunctionCallOutputContentItem::InputText {