separate out sandbox

This commit is contained in:
Roy Han
2026-03-23 11:31:44 -07:00
parent 7b496c78de
commit 35f1f605a6
13 changed files with 18 additions and 262 deletions
@@ -1996,16 +1996,6 @@
"description": "Client-visible metadata ID generated by Codex for this item.",
"type": "string"
},
"sandbox_policy": {
"anyOf": [
{
"$ref": "#/definitions/SandboxPolicyMetadata"
},
{
"type": "null"
}
]
},
"user_message_type": {
"anyOf": [
{
@@ -2040,16 +2030,6 @@
}
]
},
"sandbox_policy": {
"anyOf": [
{
"$ref": "#/definitions/SandboxPolicyMetadata"
},
{
"type": "null"
}
]
},
"user_message_type": {
"anyOf": [
{
@@ -2435,14 +2415,6 @@
}
]
},
"SandboxPolicyMetadata": {
"enum": [
"read_only",
"sandbox",
"full_access"
],
"type": "string"
},
"ServiceTier": {
"enum": [
"fast",
@@ -10719,16 +10719,6 @@
"description": "Client-visible metadata ID generated by Codex for this item.",
"type": "string"
},
"sandbox_policy": {
"anyOf": [
{
"$ref": "#/definitions/v2/SandboxPolicyMetadata"
},
{
"type": "null"
}
]
},
"user_message_type": {
"anyOf": [
{
@@ -10763,16 +10753,6 @@
}
]
},
"sandbox_policy": {
"anyOf": [
{
"$ref": "#/definitions/v2/SandboxPolicyMetadata"
},
{
"type": "null"
}
]
},
"user_message_type": {
"anyOf": [
{
@@ -11178,14 +11158,6 @@
}
]
},
"SandboxPolicyMetadata": {
"enum": [
"read_only",
"sandbox",
"full_access"
],
"type": "string"
},
"SandboxWorkspaceWrite": {
"properties": {
"exclude_slash_tmp": {
@@ -7487,16 +7487,6 @@
"description": "Client-visible metadata ID generated by Codex for this item.",
"type": "string"
},
"sandbox_policy": {
"anyOf": [
{
"$ref": "#/definitions/SandboxPolicyMetadata"
},
{
"type": "null"
}
]
},
"user_message_type": {
"anyOf": [
{
@@ -7531,16 +7521,6 @@
}
]
},
"sandbox_policy": {
"anyOf": [
{
"$ref": "#/definitions/SandboxPolicyMetadata"
},
{
"type": "null"
}
]
},
"user_message_type": {
"anyOf": [
{
@@ -7946,14 +7926,6 @@
}
]
},
"SandboxPolicyMetadata": {
"enum": [
"read_only",
"sandbox",
"full_access"
],
"type": "string"
},
"SandboxWorkspaceWrite": {
"properties": {
"exclude_slash_tmp": {
@@ -844,16 +844,6 @@
"description": "Client-visible metadata ID generated by Codex for this item.",
"type": "string"
},
"sandbox_policy": {
"anyOf": [
{
"$ref": "#/definitions/SandboxPolicyMetadata"
},
{
"type": "null"
}
]
},
"user_message_type": {
"anyOf": [
{
@@ -888,16 +878,6 @@
}
]
},
"sandbox_policy": {
"anyOf": [
{
"$ref": "#/definitions/SandboxPolicyMetadata"
},
{
"type": "null"
}
]
},
"user_message_type": {
"anyOf": [
{
@@ -1031,14 +1011,6 @@
],
"type": "string"
},
"SandboxPolicyMetadata": {
"enum": [
"read_only",
"sandbox",
"full_access"
],
"type": "string"
},
"UserMessageType": {
"enum": [
"prompt",
@@ -910,16 +910,6 @@
"description": "Client-visible metadata ID generated by Codex for this item.",
"type": "string"
},
"sandbox_policy": {
"anyOf": [
{
"$ref": "#/definitions/SandboxPolicyMetadata"
},
{
"type": "null"
}
]
},
"user_message_type": {
"anyOf": [
{
@@ -954,16 +944,6 @@
}
]
},
"sandbox_policy": {
"anyOf": [
{
"$ref": "#/definitions/SandboxPolicyMetadata"
},
{
"type": "null"
}
]
},
"user_message_type": {
"anyOf": [
{
@@ -1105,14 +1085,6 @@
],
"type": "string"
},
"SandboxPolicyMetadata": {
"enum": [
"read_only",
"sandbox",
"full_access"
],
"type": "string"
},
"ServiceTier": {
"enum": [
"fast",
@@ -1,10 +1,9 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { SandboxPolicyMetadata } from "./SandboxPolicyMetadata";
import type { UserMessageType } from "./UserMessageType";
export type ResponseItemMessageMetadata = { user_message_type?: UserMessageType, sandbox_policy?: SandboxPolicyMetadata,
export type ResponseItemMessageMetadata = { user_message_type?: UserMessageType,
/**
* Client-visible metadata ID generated by Codex for this item.
*/
@@ -2,11 +2,10 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { ReviewDecisionMetadata } from "./ReviewDecisionMetadata";
import type { SandboxPolicyMetadata } from "./SandboxPolicyMetadata";
import type { UserMessageType } from "./UserMessageType";
export type ResponseItemMetadata = { user_message_type?: UserMessageType,
/**
* Client-visible UUID generated by Codex for this item.
*/
uuid?: string, sandbox_policy?: SandboxPolicyMetadata, is_tool_call_escalated?: boolean, review_decision?: ReviewDecisionMetadata, };
uuid?: string, is_tool_call_escalated?: boolean, review_decision?: ReviewDecisionMetadata, };
@@ -1,5 +0,0 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type SandboxPolicyMetadata = "read_only" | "sandbox" | "full_access";
@@ -63,7 +63,6 @@ export type { ResponseItemMessageMetadata } from "./ResponseItemMessageMetadata"
export type { ResponseItemMetadata } from "./ResponseItemMetadata";
export type { ReviewDecision } from "./ReviewDecision";
export type { ReviewDecisionMetadata } from "./ReviewDecisionMetadata";
export type { SandboxPolicyMetadata } from "./SandboxPolicyMetadata";
export type { ServerNotification } from "./ServerNotification";
export type { ServerRequest } from "./ServerRequest";
export type { ServiceTier } from "./ServiceTier";
+9 -54
View File
@@ -335,7 +335,6 @@ use codex_protocol::models::DeveloperInstructions;
use codex_protocol::models::ResponseInputItem;
use codex_protocol::models::ResponseItem;
use codex_protocol::models::ResponseItemMetadata;
use codex_protocol::models::SandboxPolicyMetadata;
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
use codex_protocol::protocol::CodexErrorInfo;
use codex_protocol::protocol::InitialHistory;
@@ -1021,9 +1020,6 @@ fn stamp_message_metadata_on_input_item(
if patch.user_message_type.is_some() {
existing.user_message_type = patch.user_message_type.clone();
}
if patch.sandbox_policy.is_some() {
existing.sandbox_policy = patch.sandbox_policy.clone();
}
}
None => {
*metadata = Some(patch.clone());
@@ -1031,23 +1027,9 @@ fn stamp_message_metadata_on_input_item(
}
}
fn sandbox_policy_to_metadata(policy: &SandboxPolicy) -> SandboxPolicyMetadata {
match policy {
SandboxPolicy::ReadOnly { .. } => SandboxPolicyMetadata::ReadOnly,
SandboxPolicy::WorkspaceWrite { .. } | SandboxPolicy::ExternalSandbox { .. } => {
SandboxPolicyMetadata::Sandbox
}
SandboxPolicy::DangerFullAccess => SandboxPolicyMetadata::FullAccess,
}
}
fn user_message_metadata_patch(
kind: UserMessageType,
sandbox_policy: Option<SandboxPolicyMetadata>,
) -> ResponseItemMessageMetadata {
fn user_message_metadata_patch(kind: UserMessageType) -> ResponseItemMessageMetadata {
ResponseItemMessageMetadata {
user_message_type: Some(kind),
sandbox_policy,
..ResponseItemMessageMetadata::new(/*user_message_type*/ None)
}
}
@@ -1109,7 +1091,6 @@ struct ToolApprovalMetadataSnapshot {
}
fn stamp_tool_approval_metadata_with_snapshot(
turn_context: &TurnContext,
response_item: ResponseItem,
snapshot: Option<&ToolApprovalMetadataSnapshot>,
) -> ResponseItem {
@@ -1127,9 +1108,6 @@ fn stamp_tool_approval_metadata_with_snapshot(
Some(metadata) => metadata,
None => return response_item,
};
metadata.sandbox_policy = Some(sandbox_policy_to_metadata(
turn_context.sandbox_policy.get(),
));
match outcome {
Some(outcome) => {
@@ -3435,7 +3413,6 @@ impl Session {
pub(crate) async fn stamp_tool_approval_metadata(
&self,
turn_context: &TurnContext,
response_item: ResponseItem,
) -> ResponseItem {
if !self.enabled(Feature::ItemMetadata) {
@@ -3454,12 +3431,11 @@ impl Session {
pending_approval_call_ids,
}
};
stamp_tool_approval_metadata_with_snapshot(turn_context, response_item, Some(&snapshot))
stamp_tool_approval_metadata_with_snapshot(response_item, Some(&snapshot))
}
pub(crate) async fn stamp_tool_approval_metadata_on_items(
&self,
turn_context: &TurnContext,
response_items: Vec<ResponseItem>,
) -> Vec<ResponseItem> {
if !self.enabled(Feature::ItemMetadata) {
@@ -3481,9 +3457,7 @@ impl Session {
response_items
.into_iter()
.map(|item| {
stamp_tool_approval_metadata_with_snapshot(turn_context, item, Some(&snapshot))
})
.map(|item| stamp_tool_approval_metadata_with_snapshot(item, Some(&snapshot)))
.collect()
}
@@ -4039,9 +4013,7 @@ impl Session {
turn_context: &TurnContext,
response_item: ResponseItem,
) {
let response_item = self
.stamp_tool_approval_metadata(turn_context, response_item)
.await;
let response_item = self.stamp_tool_approval_metadata(response_item).await;
// Add to conversation history and persist response item to rollout.
self.record_conversation_items(turn_context, std::slice::from_ref(&response_item))
@@ -4145,7 +4117,7 @@ impl Session {
return Err(SteerInputError::NoActiveTurn(input));
};
let Some((active_turn_id, active_task)) = active_turn.tasks.first() else {
let Some((active_turn_id, _active_task)) = active_turn.tasks.first() else {
return Err(SteerInputError::NoActiveTurn(input));
};
@@ -4159,11 +4131,7 @@ impl Session {
}
let mut input_item: ResponseInputItem = input.into();
let metadata = user_message_metadata_patch(
UserMessageType::PromptSteering,
self.enabled(Feature::ItemMetadata)
.then(|| sandbox_policy_to_metadata(active_task.turn_context.sandbox_policy.get())),
);
let metadata = user_message_metadata_patch(UserMessageType::PromptSteering);
if self.enabled(Feature::ItemMetadata) {
stamp_message_metadata_on_input_item(&mut input_item, &metadata);
}
@@ -4181,18 +4149,12 @@ impl Session {
let mut active = self.active_turn.lock().await;
match active.as_mut() {
Some(at) => {
let sandbox_policy = at.tasks.first().map(|(_, turn_context)| {
sandbox_policy_to_metadata(turn_context.turn_context.sandbox_policy.get())
});
let mut ts = at.turn_state.lock().await;
for mut item in input {
if self.enabled(Feature::ItemMetadata)
&& matches!(&item, ResponseInputItem::Message { .. })
{
let metadata = user_message_metadata_patch(
UserMessageType::PromptQueued,
sandbox_policy.clone(),
);
let metadata = user_message_metadata_patch(UserMessageType::PromptQueued);
stamp_message_metadata_on_input_item(&mut item, &metadata);
}
ts.push_pending_input(item);
@@ -5892,11 +5854,7 @@ pub(crate) async fn run_turn(
.await;
let mut initial_input_for_turn: ResponseInputItem = ResponseInputItem::from(input.clone());
let initial_message_metadata = user_message_metadata_patch(
UserMessageType::Prompt,
sess.enabled(Feature::ItemMetadata)
.then(|| sandbox_policy_to_metadata(turn_context.sandbox_policy.get())),
);
let initial_message_metadata = user_message_metadata_patch(UserMessageType::Prompt);
if sess.enabled(Feature::ItemMetadata) {
stamp_message_metadata_on_input_item(
&mut initial_input_for_turn,
@@ -5994,10 +5952,7 @@ pub(crate) async fn run_turn(
.await
.for_prompt(&turn_context.model_info.input_modalities);
let sampling_request_input = sess
.stamp_tool_approval_metadata_on_items(
turn_context.as_ref(),
sampling_request_input_items,
)
.stamp_tool_approval_metadata_on_items(sampling_request_input_items)
.await;
let sampling_request_input_messages = sampling_request_input
+5 -32
View File
@@ -4464,30 +4464,12 @@ fn review_decision_metadata_mapping_is_stable() {
);
}
#[test]
fn sandbox_policy_metadata_mapping_is_stable() {
assert_eq!(
sandbox_policy_to_metadata(&SandboxPolicy::DangerFullAccess),
codex_protocol::models::SandboxPolicyMetadata::FullAccess
);
assert_eq!(
sandbox_policy_to_metadata(&SandboxPolicy::new_read_only_policy()),
codex_protocol::models::SandboxPolicyMetadata::ReadOnly
);
assert_eq!(
sandbox_policy_to_metadata(&SandboxPolicy::new_workspace_write_policy()),
codex_protocol::models::SandboxPolicyMetadata::Sandbox
);
}
async fn setup_tool_call_metadata_runtime_test() -> (
Arc<Session>,
Arc<TurnContext>,
async_channel::Receiver<Event>,
codex_protocol::models::SandboxPolicyMetadata,
) {
let (mut sess, tc, rx) = make_session_and_context_with_rx().await;
let expected_sandbox_policy = sandbox_policy_to_metadata(tc.sandbox_policy.get());
Arc::get_mut(&mut sess)
.expect("session should be uniquely owned in this test")
.features
@@ -4508,7 +4490,7 @@ async fn setup_tool_call_metadata_runtime_test() -> (
.await;
while rx.try_recv().is_ok() {}
(sess, tc, rx, expected_sandbox_policy)
(sess, tc, rx)
}
fn function_call_item(call_id: &str) -> ResponseItem {
@@ -4524,7 +4506,6 @@ fn function_call_item(call_id: &str) -> ResponseItem {
async fn assert_next_emitted_function_call_metadata(
rx: &async_channel::Receiver<Event>,
expected_sandbox_policy: codex_protocol::models::SandboxPolicyMetadata,
expected_escalated: bool,
expected_review_decision: Option<codex_protocol::models::ReviewDecisionMetadata>,
) {
@@ -4542,14 +4523,13 @@ async fn assert_next_emitted_function_call_metadata(
..
} if metadata.is_tool_call_escalated == Some(expected_escalated)
&& metadata.review_decision == expected_review_decision
&& metadata.sandbox_policy == Some(expected_sandbox_policy)
)
));
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn tool_call_metadata_stamps_escalated_review_decision_when_feature_enabled() {
let (sess, tc, rx, expected_sandbox_policy) = setup_tool_call_metadata_runtime_test().await;
let (sess, tc, rx) = setup_tool_call_metadata_runtime_test().await;
sess.record_call_approval_outcome(
"call-1".to_string(),
@@ -4560,7 +4540,6 @@ async fn tool_call_metadata_stamps_escalated_review_decision_when_feature_enable
.await;
assert_next_emitted_function_call_metadata(
&rx,
expected_sandbox_policy,
true,
Some(codex_protocol::models::ReviewDecisionMetadata::Denied),
)
@@ -4569,17 +4548,16 @@ async fn tool_call_metadata_stamps_escalated_review_decision_when_feature_enable
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn tool_call_metadata_stamps_non_escalated_false_when_feature_enabled() {
let (sess, tc, rx, expected_sandbox_policy) = setup_tool_call_metadata_runtime_test().await;
let (sess, tc, rx) = setup_tool_call_metadata_runtime_test().await;
sess.record_response_item_and_emit_turn_item(tc.as_ref(), function_call_item("call-2"))
.await;
assert_next_emitted_function_call_metadata(&rx, expected_sandbox_policy, false, None).await;
assert_next_emitted_function_call_metadata(&rx, false, None).await;
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn handle_output_item_done_stamps_tool_call_metadata_when_feature_enabled() {
let (mut sess, tc, _rx) = make_session_and_context_with_rx().await;
let expected_sandbox_policy = sandbox_policy_to_metadata(tc.sandbox_policy.get());
Arc::get_mut(&mut sess)
.expect("session should be uniquely owned in this test")
.features
@@ -4629,7 +4607,6 @@ async fn handle_output_item_done_stamps_tool_call_metadata_when_feature_enabled(
} if call_id == "call-stream-1"
&& metadata.is_tool_call_escalated == Some(false)
&& metadata.review_decision.is_none()
&& metadata.sandbox_policy == Some(expected_sandbox_policy.clone())
)
}));
}
@@ -4637,7 +4614,6 @@ async fn handle_output_item_done_stamps_tool_call_metadata_when_feature_enabled(
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn tool_call_metadata_can_be_restamped_after_approval_outcome() {
let (mut sess, tc, _rx) = make_session_and_context_with_rx().await;
let expected_sandbox_policy = sandbox_policy_to_metadata(tc.sandbox_policy.get());
Arc::get_mut(&mut sess)
.expect("session should be uniquely owned in this test")
.features
@@ -4689,9 +4665,7 @@ async fn tool_call_metadata_can_be_restamped_after_approval_outcome() {
.cloned()
.expect("function call should exist in history");
let restamped_item = sess
.stamp_tool_approval_metadata(tc.as_ref(), original_item)
.await;
let restamped_item = sess.stamp_tool_approval_metadata(original_item).await;
assert!(matches!(
restamped_item,
@@ -4701,7 +4675,6 @@ async fn tool_call_metadata_can_be_restamped_after_approval_outcome() {
} if metadata.is_tool_call_escalated == Some(true)
&& metadata.review_decision
== Some(codex_protocol::models::ReviewDecisionMetadata::Denied)
&& metadata.sandbox_policy == Some(expected_sandbox_policy)
));
}
+1 -4
View File
@@ -213,10 +213,7 @@ pub(crate) async fn handle_output_item_done(
payload_preview
);
let item = ctx
.sess
.stamp_tool_approval_metadata(ctx.turn_context.as_ref(), item)
.await;
let item = ctx.sess.stamp_tool_approval_metadata(item).await;
record_completed_response_item(ctx.sess.as_ref(), ctx.turn_context.as_ref(), &item)
.await;
+1 -22
View File
@@ -282,15 +282,6 @@ pub enum ReviewDecisionMetadata {
DeniedWithNetworkPolicyDeny,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "snake_case")]
#[ts(rename_all = "snake_case")]
pub enum SandboxPolicyMetadata {
ReadOnly,
Sandbox,
FullAccess,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, TS)]
pub struct ResponseItemMetadata {
#[serde(default, skip_serializing_if = "Option::is_none")]
@@ -302,9 +293,6 @@ pub struct ResponseItemMetadata {
pub uuid: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub sandbox_policy: Option<SandboxPolicyMetadata>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub is_tool_call_escalated: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
@@ -313,15 +301,10 @@ pub struct ResponseItemMetadata {
impl ResponseItemMetadata {
pub fn is_empty(&self) -> bool {
self.sandbox_policy.is_none()
&& self.is_tool_call_escalated.is_none()
&& self.review_decision.is_none()
self.is_tool_call_escalated.is_none() && self.review_decision.is_none()
}
pub fn merge_from(&mut self, other: Self) {
if other.sandbox_policy.is_some() {
self.sandbox_policy = other.sandbox_policy;
}
if other.is_tool_call_escalated.is_some() {
self.is_tool_call_escalated = other.is_tool_call_escalated;
}
@@ -336,9 +319,6 @@ pub struct ResponseItemMessageMetadata {
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub user_message_type: Option<UserMessageType>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub sandbox_policy: Option<SandboxPolicyMetadata>,
/// Client-visible metadata ID generated by Codex for this item.
pub metadata_id: String,
}
@@ -347,7 +327,6 @@ impl ResponseItemMessageMetadata {
pub fn new(user_message_type: Option<UserMessageType>) -> Self {
Self {
user_message_type,
sandbox_policy: None,
metadata_id: uuid::Uuid::new_v4().to_string(),
}
}