mirror of
https://github.com/openai/codex.git
synced 2026-10-31 19:28:49 +00:00
Attribute command and plugin analytics to the invoking model (#45445)
## Why Command execution and plugin measurement events lack model and reasoning-effort labels. Attribution needs to reflect the step that invoked the command, even when model settings change before a background process finishes. ## What changed - Add `model_slug` and `reasoning_effort` to command execution and plugin measurement analytics. - Capture model context from resolved step settings and carry it through execution, approval, Guardian review, and plugin metrics collection. - Preserve the first command-start model context when subsequent start notifications arrive. - Keep the carried context out of serialized protocol items and generated schemas. ## Testing Extend analytics tests to cover model switches before invocation and during background execution, default reasoning effort, Guardian-denied commands, and repeated start notifications retaining the original model context. GitOrigin-RevId: af90e1c0d39bab625f2e89786085b61a9b96c0ce
This commit is contained in:
@@ -10,6 +10,7 @@ fn agent_status_uses_bounded_buffered_activity() {
|
||||
store.push_notification(ServerNotification::ItemCompleted(
|
||||
ItemCompletedNotification {
|
||||
item: ThreadItem::CommandExecution {
|
||||
model_context: None,
|
||||
id: "command-1".to_string(),
|
||||
command: "cargo test -p codex-tui".to_string(),
|
||||
cwd: AbsolutePathBuf::try_from("/workspace")
|
||||
|
||||
@@ -95,6 +95,7 @@ mod tests {
|
||||
fn denied_event(id: usize) -> GuardianAssessmentEvent {
|
||||
GuardianAssessmentEvent {
|
||||
review_reason: None,
|
||||
model_context: None,
|
||||
id: format!("review-{id}"),
|
||||
target_item_id: None,
|
||||
plugin_id: None,
|
||||
|
||||
@@ -194,6 +194,7 @@ mod tests {
|
||||
|
||||
fn command_execution(call_id: &str) -> ThreadItem {
|
||||
ThreadItem::CommandExecution {
|
||||
model_context: None,
|
||||
id: call_id.to_string(),
|
||||
command: "true".to_string(),
|
||||
cwd: AbsolutePathBuf::current_dir().expect("current dir").into(),
|
||||
|
||||
@@ -96,6 +96,7 @@ impl ChatWidget {
|
||||
|
||||
self.on_guardian_assessment(GuardianAssessmentEvent {
|
||||
review_reason: None,
|
||||
model_context: None,
|
||||
id,
|
||||
target_item_id: None,
|
||||
plugin_id: None,
|
||||
|
||||
@@ -1080,6 +1080,7 @@ async fn live_app_server_command_execution_strips_shell_wrapper() {
|
||||
turn_id: "turn-1".to_string(),
|
||||
started_at_ms: 0,
|
||||
item: AppServerThreadItem::CommandExecution {
|
||||
model_context: None,
|
||||
id: "cmd-1".to_string(),
|
||||
command: command.clone(),
|
||||
cwd: test_path_buf("/tmp").abs().into(),
|
||||
@@ -1104,6 +1105,7 @@ async fn live_app_server_command_execution_strips_shell_wrapper() {
|
||||
turn_id: "turn-1".to_string(),
|
||||
completed_at_ms: 0,
|
||||
item: AppServerThreadItem::CommandExecution {
|
||||
model_context: None,
|
||||
id: "cmd-1".to_string(),
|
||||
command,
|
||||
cwd: test_path_buf("/tmp").abs().into(),
|
||||
|
||||
@@ -164,6 +164,7 @@ async fn replayed_commands_preserve_individual_output_and_failure_status() {
|
||||
let cwd = chat.config.cwd.clone();
|
||||
let replayed_command =
|
||||
|id: &str, output: &str, source: ExecCommandSource| AppServerThreadItem::CommandExecution {
|
||||
model_context: None,
|
||||
id: id.to_string(),
|
||||
command: format!("printf {output}"),
|
||||
cwd: cwd.clone().into(),
|
||||
@@ -612,6 +613,7 @@ async fn exec_end_without_begin_uses_event_command() {
|
||||
handle_exec_end(
|
||||
&mut chat,
|
||||
AppServerThreadItem::CommandExecution {
|
||||
model_context: None,
|
||||
id: "call-orphan".to_string(),
|
||||
command: codex_shell_command::parse_command::shlex_join(&command),
|
||||
cwd: cwd.into(),
|
||||
|
||||
@@ -4,6 +4,7 @@ use pretty_assertions::assert_eq;
|
||||
fn auto_review_denial_event() -> GuardianAssessmentEvent {
|
||||
GuardianAssessmentEvent {
|
||||
review_reason: None,
|
||||
model_context: None,
|
||||
id: "auto-review-recent-1".into(),
|
||||
target_item_id: Some("target-auto-review-recent-1".into()),
|
||||
plugin_id: None,
|
||||
@@ -33,6 +34,7 @@ fn guardian_command_event(
|
||||
let terminal = status != GuardianAssessmentStatus::InProgress;
|
||||
GuardianAssessmentEvent {
|
||||
review_reason: None,
|
||||
model_context: None,
|
||||
id: id.to_string(),
|
||||
target_item_id: Some(format!("{id}-target")),
|
||||
plugin_id: None,
|
||||
@@ -247,6 +249,7 @@ async fn guardian_denied_exec_renders_warning_and_denied_request() {
|
||||
|
||||
chat.on_guardian_assessment(GuardianAssessmentEvent {
|
||||
review_reason: None,
|
||||
model_context: None,
|
||||
id: "guardian-1".into(),
|
||||
target_item_id: Some("guardian-target-1".into()),
|
||||
plugin_id: None,
|
||||
@@ -264,6 +267,7 @@ async fn guardian_denied_exec_renders_warning_and_denied_request() {
|
||||
chat.on_warning("Automatic approval review denied (risk: high): The planned action would transmit the full contents of a workspace source file (`core/src/codex.rs`) to `https://example.com`, which is an external and untrusted endpoint.");
|
||||
chat.on_guardian_assessment(GuardianAssessmentEvent {
|
||||
review_reason: None,
|
||||
model_context: None,
|
||||
id: "guardian-1".into(),
|
||||
target_item_id: Some("guardian-target-1".into()),
|
||||
plugin_id: None,
|
||||
@@ -318,6 +322,7 @@ async fn guardian_approved_exec_is_hidden_from_history() {
|
||||
);
|
||||
chat.on_guardian_assessment(GuardianAssessmentEvent {
|
||||
review_reason: None,
|
||||
model_context: None,
|
||||
id: "thread:child-thread:guardian-1".into(),
|
||||
target_item_id: Some("guardian-approved-target".into()),
|
||||
plugin_id: None,
|
||||
@@ -376,6 +381,7 @@ async fn guardian_approved_request_permissions_clears_status_without_history() {
|
||||
|
||||
chat.on_guardian_assessment(GuardianAssessmentEvent {
|
||||
review_reason: None,
|
||||
model_context: None,
|
||||
id: "guardian-request-permissions".into(),
|
||||
target_item_id: None,
|
||||
plugin_id: None,
|
||||
@@ -403,6 +409,7 @@ async fn guardian_approved_request_permissions_clears_status_without_history() {
|
||||
|
||||
chat.on_guardian_assessment(GuardianAssessmentEvent {
|
||||
review_reason: None,
|
||||
model_context: None,
|
||||
id: "guardian-request-permissions".into(),
|
||||
target_item_id: None,
|
||||
plugin_id: None,
|
||||
@@ -456,6 +463,7 @@ async fn guardian_timed_out_exec_renders_warning_and_timed_out_request() {
|
||||
|
||||
chat.on_guardian_assessment(GuardianAssessmentEvent {
|
||||
review_reason: None,
|
||||
model_context: None,
|
||||
id: "guardian-1".into(),
|
||||
target_item_id: Some("guardian-target-1".into()),
|
||||
plugin_id: None,
|
||||
@@ -473,6 +481,7 @@ async fn guardian_timed_out_exec_renders_warning_and_timed_out_request() {
|
||||
chat.on_warning("Automatic approval review timed out while evaluating the requested approval.");
|
||||
chat.on_guardian_assessment(GuardianAssessmentEvent {
|
||||
review_reason: None,
|
||||
model_context: None,
|
||||
id: "guardian-1".into(),
|
||||
target_item_id: Some("guardian-target-1".into()),
|
||||
plugin_id: None,
|
||||
|
||||
@@ -883,6 +883,7 @@ pub(super) fn begin_exec_with_source(
|
||||
.map(|parsed| AppServerCommandAction::from_core_with_cwd(parsed, &chat.config.cwd))
|
||||
.collect();
|
||||
let item = AppServerThreadItem::CommandExecution {
|
||||
model_context: None,
|
||||
id: call_id.to_string(),
|
||||
command: codex_shell_command::parse_command::shlex_join(&command),
|
||||
cwd: chat.config.cwd.clone().into(),
|
||||
@@ -908,6 +909,7 @@ pub(super) fn begin_unified_exec_startup(
|
||||
) -> AppServerThreadItem {
|
||||
let command = vec!["bash".to_string(), "-lc".to_string(), raw_cmd.to_string()];
|
||||
let item = AppServerThreadItem::CommandExecution {
|
||||
model_context: None,
|
||||
id: call_id.to_string(),
|
||||
command: codex_shell_command::parse_command::shlex_join(&command),
|
||||
cwd: chat.config.cwd.clone().into(),
|
||||
@@ -1142,6 +1144,7 @@ pub(super) fn end_exec(
|
||||
handle_exec_end(
|
||||
chat,
|
||||
AppServerThreadItem::CommandExecution {
|
||||
model_context: None,
|
||||
id,
|
||||
command,
|
||||
cwd,
|
||||
|
||||
@@ -535,6 +535,7 @@ async fn mcp_startup_complete_preserves_review_status() {
|
||||
|
||||
chat.on_guardian_assessment(GuardianAssessmentEvent {
|
||||
review_reason: None,
|
||||
model_context: None,
|
||||
id: "guardian-1".to_string(),
|
||||
target_item_id: Some("guardian-target-1".to_string()),
|
||||
plugin_id: None,
|
||||
|
||||
@@ -5668,6 +5668,7 @@ async fn chatwidget_exec_and_status_layout_vt100_snapshot() {
|
||||
handle_exec_begin(
|
||||
&mut chat,
|
||||
AppServerThreadItem::CommandExecution {
|
||||
model_context: None,
|
||||
id: "c1".into(),
|
||||
command: codex_shell_command::parse_command::shlex_join(&command),
|
||||
cwd: cwd.clone().into(),
|
||||
@@ -5685,6 +5686,7 @@ async fn chatwidget_exec_and_status_layout_vt100_snapshot() {
|
||||
handle_exec_end(
|
||||
&mut chat,
|
||||
AppServerThreadItem::CommandExecution {
|
||||
model_context: None,
|
||||
id: "c1".into(),
|
||||
command: codex_shell_command::parse_command::shlex_join(&command),
|
||||
cwd: cwd.into(),
|
||||
|
||||
Reference in New Issue
Block a user