Record sandbox backends in command execution analytics (#47568)

## Why

Command execution analytics lack the observed sandbox backend. Older execution servers may omit this information, so an unknown backend must remain distinguishable from an explicitly unsandboxed process.

## What changed

- Carry optional `sandbox_type` metadata through command execution items and emit its metric tag as `sandbox_backend` in analytics.
- Propagate the observed backend through unified execution completion and failure events, including asynchronous exits.
- Preserve missing backend information as `None` and mark executed user shell commands as `Some(SandboxType::None)`.
- Exclude the metadata from serialized command items and generated schemas and TypeScript types.

## Testing

Extend tests for missing and explicit executor backends, backend propagation on failed execution, and analytics payloads, including platform-specific backend tags in app-server integration coverage.

GitOrigin-RevId: 178fe46824a6194910802ec02168ed40ee0b2019
This commit is contained in:
iceweasel-oai
2026-09-23 15:05:59 +00:00
committed by copyberry
parent d6c4c6aea4
commit 6f6af0fce4
29 changed files with 113 additions and 21 deletions
@@ -289,6 +289,10 @@ pub enum ThreadItem {
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase")]
CommandExecution {
#[serde(skip)]
#[schemars(skip)]
#[ts(skip)]
sandbox_type: Option<codex_protocol::sandbox::SandboxType>,
#[serde(skip)]
#[schemars(skip)]
#[ts(skip)]
@@ -919,6 +923,7 @@ impl From<CoreTurnItem> for ThreadItem {
ThreadItem::CommandExecution {
id: command.id,
model_context: command.model_context,
sandbox_type: command.sandbox_type,
plugin_id: command.plugin_id,
script_path: command.script_path,
command: presentation.command,
@@ -3272,6 +3272,7 @@ fn core_turn_item_into_thread_item_converts_supported_variants() {
let command_item = TurnItem::CommandExecution(CommandExecutionItem {
model_context: None,
sandbox_type: None,
id: "exec-1".to_string(),
plugin_id: Some("sample@openai-curated".to_string()),
script_path: Some("scripts/run.py".to_string()),
@@ -3305,6 +3306,7 @@ fn core_turn_item_into_thread_item_converts_supported_variants() {
ThreadItem::from(command_item),
ThreadItem::CommandExecution {
model_context: None,
sandbox_type: None,
id: "exec-1".to_string(),
plugin_id: Some("sample@openai-curated".to_string()),
script_path: Some("scripts/run.py".to_string()),