Use captured step settings for request metadata and tool hooks (#45248)

## Why

Model and reasoning effort updates during a turn can leave request metadata and tool hooks reporting the turn's initial settings. Metadata should describe the step that issued the request or tool call.

## What changed

- Share captured execution metadata across Responses, MCP, and extension tool calls, including model, reasoning effort, and automatic review and Node REPL flags.
- Build Responses tool inventory metadata from the issuing step's finalized tool router. Attach the finalized inventory separately for remote compaction.
- Use captured step settings for pre- and post-tool hooks, and captured review settings for permission-request hooks.

## Testing

Add and extend regression tests for model and effort changes during a turn, MCP metadata, pre-tool hook model attribution, captured review flags, and tool inventory matching the issuing request.

GitOrigin-RevId: 9dee46a8b4839de8b434cdad4ed441cc0bb6ff9b
This commit is contained in:
Abhinav authored and copyberry committed 2026-09-13 15:47:59 +00:00
1 parent 36f0dbe796
commit 16537b20a5
20 files changed
+545 -255

No files matched your search

-14
View File
@@ -3812,20 +3812,6 @@ impl Session {
)
.or_cancel(cancellation_token)
.await??;
// Publish inventory only after the step's tool plan has been finalized.
if turn_context
.config
.tool_registry
.turn_metadata_includes_tool_info
&& turn_context.model_info().use_responses_lite
{
turn_context.turn_metadata_state.set_tool_namespaces_info(
tool_router
.tool_namespaces_info()
.cloned()
.unwrap_or_default(),
);
}
Ok(Arc::new(StepContext {
settings,
token_budget,
+54 -2
View File
@@ -1,5 +1,6 @@
use super::input_queue::InputQueue;
use super::mcp_refresh::McpRefresh;
use super::step_context::StepContext;
use super::step_settings::ModelInfoOverrides;
use super::step_settings::StepSettings;
use super::step_settings::StepSettingsConstraints;
@@ -15,9 +16,11 @@ use crate::hook_mcp_executor::CoreHookMcpExecutor;
use crate::mcp_tool_call::McpToolApprovalMetadata;
use crate::responses_metadata::CodexResponsesMetadata;
use crate::responses_metadata::CodexResponsesRequestKind;
use crate::responses_metadata::CompactionTurnMetadata;
use crate::shell_snapshot::ShellSnapshot;
use crate::shell_snapshot::SnapshotCredentialBrokerState;
use crate::state::ActiveTurn;
use crate::turn_metadata::ExecutionMetadata;
use codex_extension_api::ExtensionDataInit;
use codex_http_client::ClientRouteClass;
use codex_http_client::RouteAwareClientPool;
@@ -644,15 +647,64 @@ impl Session {
pub(crate) async fn responses_metadata(
&self,
turn_context: &TurnContext,
step_context: &StepContext,
request_kind: CodexResponsesRequestKind,
) -> CodexResponsesMetadata {
let (window_id, window_number, context_window_id) = self.current_window().await;
let mut responses_metadata = step_context.turn.turn_metadata_state.to_responses_metadata(
self.installation_id.clone(),
window_id,
request_kind,
);
ExecutionMetadata::from_settings(&step_context.settings).apply_to(&mut responses_metadata);
responses_metadata.tool_namespaces_info = if step_context
.turn
.config
.tool_registry
.turn_metadata_includes_tool_info
&& step_context.settings.model_info.use_responses_lite
{
step_context.tool_router.tool_namespaces_info().cloned()
} else {
None
};
self.with_window_and_fork_metadata(
&step_context.turn,
responses_metadata,
window_number,
context_window_id,
)
}
// TODO(CDXENT-454): Build the compaction request and metadata from the captured execution.
// Remote compaction currently attaches only finalized tool inventory because the rest of the
// request remains turn-backed; local compaction does not have a finalized request inventory.
pub(crate) async fn compaction_responses_metadata(
&self,
turn_context: &TurnContext,
compaction_metadata: CompactionTurnMetadata,
) -> CodexResponsesMetadata {
let (window_id, window_number, context_window_id) = self.current_window().await;
let responses_metadata = turn_context.turn_metadata_state.to_responses_metadata(
self.installation_id.clone(),
window_id,
request_kind,
CodexResponsesRequestKind::Compaction(compaction_metadata),
);
self.with_window_and_fork_metadata(
turn_context,
responses_metadata,
window_number,
context_window_id,
)
}
fn with_window_and_fork_metadata(
&self,
turn_context: &TurnContext,
responses_metadata: CodexResponsesMetadata,
window_number: u64,
context_window_id: uuid::Uuid,
) -> CodexResponsesMetadata {
CodexResponsesMetadata {
window_number: Some(window_number),
context_window_id: Some(context_window_id),
+3 -2
View File
@@ -5862,16 +5862,17 @@ pub(crate) async fn build_world_state_from_turn_context(
#[tokio::test]
async fn responses_metadata_uses_selected_harness_analytics_client() {
let (mut session, mut turn_context) = make_session_and_context().await;
for enabled in [true, false] {
let (mut session, mut turn_context) = make_session_and_context().await;
session.services.analytics_events_client = AnalyticsEventsClient::new(
Arc::clone(&session.services.auth_manager),
turn_context.config.chatgpt_base_url.clone(),
Some(enabled),
);
Arc::make_mut(&mut turn_context.config).analytics_enabled = Some(!enabled);
let step_context = StepContext::for_test(Arc::new(turn_context));
let metadata = session
.responses_metadata(&turn_context, CodexResponsesRequestKind::Turn)
.responses_metadata(&step_context, CodexResponsesRequestKind::Turn)
.await;
assert_eq!(metadata.analytics_enabled, Some(enabled));
}
+1 -1
View File
@@ -517,7 +517,7 @@ pub(crate) async fn run_turn(
.await;
let responses_metadata = sess
.responses_metadata(turn_context.as_ref(), CodexResponsesRequestKind::Turn)
.responses_metadata(step_context.as_ref(), CodexResponsesRequestKind::Turn)
.await;
run_sampling_request(
Arc::clone(&sess),