mirror of
https://github.com/openai/codex.git
synced 2026-10-29 19:08:43 +00:00
sandbox
This commit is contained in:
@@ -2032,6 +2032,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"sandbox_policy": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/SandboxPolicyMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"user_message_type": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -2410,6 +2420,14 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"SandboxPolicyMetadata": {
|
||||
"enum": [
|
||||
"read_only",
|
||||
"sandbox",
|
||||
"full_access"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ServiceTier": {
|
||||
"enum": [
|
||||
"fast",
|
||||
|
||||
@@ -10661,6 +10661,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"sandbox_policy": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/SandboxPolicyMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"user_message_type": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -11059,6 +11069,14 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"SandboxPolicyMetadata": {
|
||||
"enum": [
|
||||
"read_only",
|
||||
"sandbox",
|
||||
"full_access"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"SandboxWorkspaceWrite": {
|
||||
"properties": {
|
||||
"exclude_slash_tmp": {
|
||||
|
||||
@@ -7406,6 +7406,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"sandbox_policy": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/SandboxPolicyMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"user_message_type": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -7804,6 +7814,14 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"SandboxPolicyMetadata": {
|
||||
"enum": [
|
||||
"read_only",
|
||||
"sandbox",
|
||||
"full_access"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"SandboxWorkspaceWrite": {
|
||||
"properties": {
|
||||
"exclude_slash_tmp": {
|
||||
|
||||
@@ -873,6 +873,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"sandbox_policy": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/SandboxPolicyMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"user_message_type": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -999,6 +1009,14 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"SandboxPolicyMetadata": {
|
||||
"enum": [
|
||||
"read_only",
|
||||
"sandbox",
|
||||
"full_access"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"UserMessageType": {
|
||||
"enum": [
|
||||
"prompt",
|
||||
|
||||
@@ -939,6 +939,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"sandbox_policy": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/SandboxPolicyMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"user_message_type": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -1073,6 +1083,14 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"SandboxPolicyMetadata": {
|
||||
"enum": [
|
||||
"read_only",
|
||||
"sandbox",
|
||||
"full_access"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ServiceTier": {
|
||||
"enum": [
|
||||
"fast",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
// 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, is_tool_call_escalated?: boolean, review_decision?: ReviewDecisionMetadata, };
|
||||
export type ResponseItemMetadata = { user_message_type?: UserMessageType, sandbox_policy?: SandboxPolicyMetadata, is_tool_call_escalated?: boolean, review_decision?: ReviewDecisionMetadata, };
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// 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";
|
||||
@@ -61,6 +61,7 @@ export type { ResponseItem } from "./ResponseItem";
|
||||
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";
|
||||
|
||||
+62
-12
@@ -323,6 +323,7 @@ use codex_protocol::models::ResponseInputItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::models::ResponseItemMetadata;
|
||||
use codex_protocol::models::ReviewDecisionMetadata;
|
||||
use codex_protocol::models::SandboxPolicyMetadata;
|
||||
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
|
||||
use codex_protocol::protocol::CodexErrorInfo;
|
||||
use codex_protocol::protocol::InitialHistory;
|
||||
@@ -1012,6 +1013,31 @@ fn stamp_user_message_type_on_input_item(item: &mut ResponseInputItem, kind: Use
|
||||
*metadata = Some(metadata_value);
|
||||
}
|
||||
|
||||
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 stamp_sandbox_policy_on_input_item(
|
||||
item: &mut ResponseInputItem,
|
||||
sandbox_policy: SandboxPolicyMetadata,
|
||||
) {
|
||||
let ResponseInputItem::Message { role, metadata, .. } = item else {
|
||||
return;
|
||||
};
|
||||
if role != "user" {
|
||||
return;
|
||||
}
|
||||
let mut metadata_value = metadata.take().unwrap_or_default();
|
||||
metadata_value.sandbox_policy = Some(sandbox_policy);
|
||||
*metadata = Some(metadata_value);
|
||||
}
|
||||
|
||||
fn review_decision_to_metadata(decision: &ReviewDecision) -> ReviewDecisionMetadata {
|
||||
match decision {
|
||||
ReviewDecision::Approved => ReviewDecisionMetadata::Approved,
|
||||
@@ -3383,7 +3409,7 @@ impl Session {
|
||||
|
||||
async fn stamp_tool_approval_metadata(
|
||||
&self,
|
||||
_turn_context: &TurnContext,
|
||||
turn_context: &TurnContext,
|
||||
response_item: ResponseItem,
|
||||
) -> ResponseItem {
|
||||
if !self.enabled(Feature::ItemMetadata) {
|
||||
@@ -3410,6 +3436,9 @@ impl Session {
|
||||
| ResponseItem::CustomToolCall { metadata, .. } => metadata.clone().unwrap_or_default(),
|
||||
_ => return response_item,
|
||||
};
|
||||
metadata.sandbox_policy = Some(sandbox_policy_to_metadata(
|
||||
turn_context.sandbox_policy.get(),
|
||||
));
|
||||
|
||||
match outcome {
|
||||
Some(review_decision) => {
|
||||
@@ -3421,7 +3450,7 @@ impl Session {
|
||||
metadata.review_decision = None;
|
||||
}
|
||||
None => {
|
||||
return response_item;
|
||||
return stamp_tool_metadata_on_response_item(response_item, metadata);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3944,13 +3973,18 @@ impl Session {
|
||||
response_item: ResponseItem,
|
||||
user_message_type: Option<UserMessageType>,
|
||||
) {
|
||||
let user_message_type = if self.enabled(Feature::ItemMetadata) {
|
||||
user_message_type
|
||||
let (user_message_type, sandbox_policy) = if self.enabled(Feature::ItemMetadata) {
|
||||
(
|
||||
user_message_type,
|
||||
Some(sandbox_policy_to_metadata(
|
||||
turn_context.sandbox_policy.get(),
|
||||
)),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let response_item = match (response_item, user_message_type.clone()) {
|
||||
let response_item = match (response_item, user_message_type.clone(), sandbox_policy) {
|
||||
(
|
||||
ResponseItem::Message {
|
||||
id,
|
||||
@@ -3960,10 +3994,12 @@ impl Session {
|
||||
end_turn,
|
||||
phase,
|
||||
},
|
||||
Some(kind),
|
||||
user_message_type,
|
||||
sandbox_policy,
|
||||
) if role == "user" => {
|
||||
let mut metadata = metadata.unwrap_or_default();
|
||||
metadata.user_message_type = Some(kind);
|
||||
metadata.user_message_type = user_message_type;
|
||||
metadata.sandbox_policy = sandbox_policy;
|
||||
ResponseItem::Message {
|
||||
id,
|
||||
role,
|
||||
@@ -3973,7 +4009,7 @@ impl Session {
|
||||
phase,
|
||||
}
|
||||
}
|
||||
(response_item, _) => response_item,
|
||||
(response_item, _, _) => response_item,
|
||||
};
|
||||
|
||||
// Persist the user message to history, but emit the turn item from `UserInput` so
|
||||
@@ -4061,7 +4097,7 @@ impl Session {
|
||||
return Err(SteerInputError::NoActiveTurn(input));
|
||||
};
|
||||
|
||||
let Some((active_turn_id, _)) = active_turn.tasks.first() else {
|
||||
let Some((active_turn_id, active_task)) = active_turn.tasks.first() else {
|
||||
return Err(SteerInputError::NoActiveTurn(input));
|
||||
};
|
||||
|
||||
@@ -4070,18 +4106,22 @@ impl Session {
|
||||
{
|
||||
return Err(SteerInputError::ExpectedTurnMismatch {
|
||||
expected: expected_turn_id.to_string(),
|
||||
actual: active_turn_id.clone(),
|
||||
actual: active_turn_id.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let mut input_item: ResponseInputItem = input.into();
|
||||
if self.enabled(Feature::ItemMetadata) {
|
||||
stamp_user_message_type_on_input_item(&mut input_item, UserMessageType::PromptSteering);
|
||||
stamp_sandbox_policy_on_input_item(
|
||||
&mut input_item,
|
||||
sandbox_policy_to_metadata(active_task.turn_context.sandbox_policy.get()),
|
||||
);
|
||||
}
|
||||
|
||||
let mut turn_state = active_turn.turn_state.lock().await;
|
||||
turn_state.push_pending_input(input_item, Some(UserMessageType::PromptSteering));
|
||||
Ok(active_turn_id.clone())
|
||||
Ok(active_turn_id.to_string())
|
||||
}
|
||||
|
||||
/// Returns the input if there was no task running to inject into
|
||||
@@ -4092,6 +4132,9 @@ 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 {
|
||||
let user_message_type = match &item {
|
||||
@@ -4102,6 +4145,9 @@ impl Session {
|
||||
&& let Some(kind) = user_message_type.clone()
|
||||
{
|
||||
stamp_user_message_type_on_input_item(&mut item, kind);
|
||||
if let Some(sandbox_policy) = sandbox_policy.clone() {
|
||||
stamp_sandbox_policy_on_input_item(&mut item, sandbox_policy);
|
||||
}
|
||||
}
|
||||
ts.push_pending_input(item, user_message_type);
|
||||
}
|
||||
@@ -5887,6 +5933,10 @@ pub(crate) async fn run_turn(
|
||||
let mut initial_input_for_turn: ResponseInputItem = ResponseInputItem::from(input.clone());
|
||||
if sess.enabled(Feature::ItemMetadata) {
|
||||
stamp_user_message_type_on_input_item(&mut initial_input_for_turn, UserMessageType::Prompt);
|
||||
stamp_sandbox_policy_on_input_item(
|
||||
&mut initial_input_for_turn,
|
||||
sandbox_policy_to_metadata(turn_context.sandbox_policy.get()),
|
||||
);
|
||||
}
|
||||
let response_item: ResponseItem = initial_input_for_turn.clone().into();
|
||||
sess.record_user_prompt_and_emit_turn_item(
|
||||
|
||||
@@ -3940,9 +3940,26 @@ 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
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn tool_call_metadata_stamps_escalated_review_decision_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
|
||||
@@ -3993,6 +4010,7 @@ async fn tool_call_metadata_stamps_escalated_review_decision_when_feature_enable
|
||||
} if metadata.is_tool_call_escalated == Some(true)
|
||||
&& metadata.review_decision
|
||||
== Some(codex_protocol::models::ReviewDecisionMetadata::Denied)
|
||||
&& metadata.sandbox_policy == Some(expected_sandbox_policy)
|
||||
)
|
||||
));
|
||||
}
|
||||
@@ -4000,6 +4018,7 @@ 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 (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
|
||||
@@ -4047,6 +4066,7 @@ async fn tool_call_metadata_stamps_non_escalated_false_when_feature_enabled() {
|
||||
..
|
||||
} if metadata.is_tool_call_escalated == Some(false)
|
||||
&& metadata.review_decision.is_none()
|
||||
&& metadata.sandbox_policy == Some(expected_sandbox_policy)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
@@ -344,6 +344,13 @@ async fn user_message_type_prompt_steering_metadata_is_emitted_when_feature_enab
|
||||
.and_then(Value::as_str),
|
||||
Some("prompt_steering")
|
||||
);
|
||||
assert_eq!(
|
||||
steered_message
|
||||
.get("metadata")
|
||||
.and_then(|metadata| metadata.get("sandbox_policy"))
|
||||
.and_then(Value::as_str),
|
||||
Some("full_access")
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -476,6 +483,13 @@ async fn user_message_type_prompt_queued_metadata_is_emitted_when_feature_enable
|
||||
.and_then(Value::as_str),
|
||||
Some("prompt_queued")
|
||||
);
|
||||
assert_eq!(
|
||||
queued_message
|
||||
.get("metadata")
|
||||
.and_then(|metadata| metadata.get("sandbox_policy"))
|
||||
.and_then(Value::as_str),
|
||||
Some("full_access")
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -275,6 +275,15 @@ 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")]
|
||||
@@ -282,6 +291,9 @@ pub struct ResponseItemMetadata {
|
||||
pub user_message_type: Option<UserMessageType>,
|
||||
#[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)]
|
||||
|
||||
Reference in New Issue
Block a user