Compare commits

...

3 Commits

Author SHA1 Message Date
viyatb-oai
851bec01fd docs: clarify proxy timeout guidance
Co-authored-by: Codex noreply@openai.com
2026-05-01 10:12:06 -07:00
viyatb-oai
8cd5eec700 fix: tighten network proxy prompt plumbing
Co-authored-by: Codex noreply@openai.com
2026-05-01 09:57:00 -07:00
viyatb-oai
ba681f1296 feat: add active network proxy guidance
Co-authored-by: Codex <noreply@openai.com>
2026-05-01 09:57:00 -07:00
7 changed files with 116 additions and 22 deletions

View File

@@ -41,6 +41,7 @@ pub(crate) use hook_additional_context::HookAdditionalContext;
pub(crate) use image_generation_instructions::ImageGenerationInstructions;
pub(crate) use model_switch_instructions::ModelSwitchInstructions;
pub(crate) use network_rule_saved::NetworkRuleSaved;
pub use permissions_instructions::PermissionsInstructionOptions;
pub use permissions_instructions::PermissionsInstructions;
pub(crate) use personality_spec_instructions::PersonalitySpecInstructions;
pub(crate) use plugin_instructions::PluginInstructions;

View File

@@ -24,6 +24,7 @@ const APPROVAL_POLICY_ON_REQUEST_RULE: &str =
const APPROVAL_POLICY_ON_REQUEST_RULE_REQUEST_PERMISSION: &str =
include_str!("prompts/permissions/approval_policy/on_request_rule_request_permission.md");
const AUTO_REVIEW_APPROVAL_SUFFIX: &str = "`approvals_reviewer` is `auto_review`: Sandbox escalations with require_escalated will be reviewed for compliance with the policy. If a rejection happens, you should proceed only with a materially safer alternative, or inform the user of the risk and send a final message to ask for approval.";
const NETWORK_PROXY: &str = include_str!("prompts/permissions/network_proxy.md");
const SANDBOX_MODE_DANGER_FULL_ACCESS: &str =
include_str!("prompts/permissions/sandbox_mode/danger_full_access.md");
@@ -50,6 +51,15 @@ struct PermissionsPromptConfig<'a> {
exec_policy: &'a Policy,
exec_permission_approvals_enabled: bool,
request_permissions_tool_enabled: bool,
network_proxy_active: bool,
}
#[derive(Debug, Clone, Copy)]
/// Feature-dependent knobs used while rendering permissions instructions.
pub struct PermissionsInstructionOptions {
pub exec_permission_approvals_enabled: bool,
pub request_permissions_tool_enabled: bool,
pub network_proxy_active: bool,
}
#[derive(Debug, Clone, PartialEq)]
@@ -66,8 +76,7 @@ impl PermissionsInstructions {
approvals_reviewer: ApprovalsReviewer,
exec_policy: &Policy,
cwd: &Path,
exec_permission_approvals_enabled: bool,
request_permissions_tool_enabled: bool,
options: PermissionsInstructionOptions,
) -> Self {
let (sandbox_mode, writable_roots) = sandbox_prompt_from_profile(permission_profile, cwd);
@@ -78,8 +87,9 @@ impl PermissionsInstructions {
approval_policy,
approvals_reviewer,
exec_policy,
exec_permission_approvals_enabled,
request_permissions_tool_enabled,
exec_permission_approvals_enabled: options.exec_permission_approvals_enabled,
request_permissions_tool_enabled: options.request_permissions_tool_enabled,
network_proxy_active: options.network_proxy_active,
},
writable_roots,
)
@@ -92,8 +102,7 @@ impl PermissionsInstructions {
approvals_reviewer: ApprovalsReviewer,
exec_policy: &Policy,
cwd: &Path,
exec_permission_approvals_enabled: bool,
request_permissions_tool_enabled: bool,
options: PermissionsInstructionOptions,
) -> Self {
Self::from_permission_profile(
&PermissionProfile::from_legacy_sandbox_policy(sandbox_policy),
@@ -101,8 +110,7 @@ impl PermissionsInstructions {
approvals_reviewer,
exec_policy,
cwd,
exec_permission_approvals_enabled,
request_permissions_tool_enabled,
options,
)
}
@@ -114,6 +122,9 @@ impl PermissionsInstructions {
) -> Self {
let mut text = String::new();
append_section(&mut text, &sandbox_text(sandbox_mode, network_access));
if config.network_proxy_active {
append_section(&mut text, NETWORK_PROXY.trim_end());
}
append_section(
&mut text,
&approval_text(

View File

@@ -38,6 +38,7 @@ fn builds_permissions_with_network_access_override() {
exec_policy: &Policy::empty(),
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: false,
network_proxy_active: false,
},
/*writable_roots*/ None,
);
@@ -68,8 +69,11 @@ fn builds_permissions_from_policy() {
ApprovalsReviewer::User,
&Policy::empty(),
&PathBuf::from("/tmp"),
/*exec_permission_approvals_enabled*/ false,
/*request_permissions_tool_enabled*/ false,
PermissionsInstructionOptions {
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: false,
network_proxy_active: false,
},
);
let text = instructions.body();
assert!(text.contains("Network access is enabled."));
@@ -97,8 +101,11 @@ fn builds_permissions_from_profile() {
ApprovalsReviewer::User,
&Policy::empty(),
&cwd,
/*exec_permission_approvals_enabled*/ false,
/*request_permissions_tool_enabled*/ false,
PermissionsInstructionOptions {
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: false,
network_proxy_active: false,
},
);
let text = instructions.body();
assert!(text.contains("`sandbox_mode` is `workspace-write`"));
@@ -106,6 +113,47 @@ fn builds_permissions_from_profile() {
assert!(text.contains(writable_root.to_string_lossy().as_ref()));
}
#[test]
fn includes_network_proxy_guidance_when_proxy_is_active() {
let instructions = PermissionsInstructions::from_permissions_with_network(
SandboxMode::WorkspaceWrite,
NetworkAccess::Restricted,
PermissionsPromptConfig {
approval_policy: AskForApproval::Never,
approvals_reviewer: ApprovalsReviewer::User,
exec_policy: &Policy::empty(),
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: false,
network_proxy_active: true,
},
/*writable_roots*/ None,
);
let text = instructions.body();
assert!(text.contains("# Network Proxy"));
assert!(text.contains("blocked-by-allowlist"));
assert!(text.contains("proxy-specific headers or messages"));
}
#[test]
fn omits_network_proxy_guidance_when_proxy_is_inactive() {
let instructions = PermissionsInstructions::from_permissions_with_network(
SandboxMode::WorkspaceWrite,
NetworkAccess::Restricted,
PermissionsPromptConfig {
approval_policy: AskForApproval::Never,
approvals_reviewer: ApprovalsReviewer::User,
exec_policy: &Policy::empty(),
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: false,
network_proxy_active: false,
},
/*writable_roots*/ None,
);
assert!(!instructions.body().contains("# Network Proxy"));
}
#[test]
fn includes_request_rule_instructions_for_on_request() {
let mut exec_policy = Policy::empty();
@@ -121,6 +169,7 @@ fn includes_request_rule_instructions_for_on_request() {
exec_policy: &exec_policy,
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: false,
network_proxy_active: false,
},
/*writable_roots*/ None,
);
@@ -142,6 +191,7 @@ fn includes_request_permissions_tool_instructions_for_unless_trusted_when_enable
exec_policy: &Policy::empty(),
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: true,
network_proxy_active: false,
},
/*writable_roots*/ None,
);
@@ -162,6 +212,7 @@ fn includes_request_permissions_tool_instructions_for_on_failure_when_enabled()
exec_policy: &Policy::empty(),
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: true,
network_proxy_active: false,
},
/*writable_roots*/ None,
);
@@ -182,6 +233,7 @@ fn includes_request_permission_rule_instructions_for_on_request_when_enabled() {
exec_policy: &Policy::empty(),
exec_permission_approvals_enabled: true,
request_permissions_tool_enabled: false,
network_proxy_active: false,
},
/*writable_roots*/ None,
);
@@ -202,6 +254,7 @@ fn includes_request_permissions_tool_instructions_for_on_request_when_tool_is_en
exec_policy: &Policy::empty(),
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: true,
network_proxy_active: false,
},
/*writable_roots*/ None,
);
@@ -222,6 +275,7 @@ fn on_request_includes_tool_guidance_alongside_inline_permission_guidance_when_b
exec_policy: &Policy::empty(),
exec_permission_approvals_enabled: true,
request_permissions_tool_enabled: true,
network_proxy_active: false,
},
/*writable_roots*/ None,
);

View File

@@ -0,0 +1,12 @@
# Network Proxy
A network proxy is active for model-initiated shell commands. Codex applies proxy environment variables automatically so outbound traffic is checked against the configured network policy.
Honor any `<network>` allow/deny entries in the environment context. Use normal network tools without clearing or overriding proxy-related environment variables. If a required host is not allowed, request additional network permissions instead of working around the proxy.
Interpret proxy failures precisely:
- `blocked-by-allowlist` means the host is not allowed by the current network policy.
- `blocked-by-denylist` means the host is explicitly denied by policy.
- A message about local/private network addresses means the sandbox is blocking local or private targets.
Do not infer a proxy denial from a generic network failure alone. Proxy-mediated requests can themselves time out or hang. Treat timeouts, hangs, DNS errors, TLS errors, and connection failures as evidence of proxy policy only when they also include proxy-specific headers or messages.

View File

@@ -2,6 +2,7 @@ use crate::context::CollaborationModeInstructions;
use crate::context::ContextualUserFragment;
use crate::context::EnvironmentContext;
use crate::context::ModelSwitchInstructions;
use crate::context::PermissionsInstructionOptions;
use crate::context::PermissionsInstructions;
use crate::context::PersonalitySpecInstructions;
use crate::context::RealtimeEndInstructions;
@@ -62,8 +63,15 @@ fn build_permissions_update_item(
next.config.approvals_reviewer,
exec_policy,
&next.cwd,
next.features.enabled(Feature::ExecPermissionApprovals),
next.features.enabled(Feature::RequestPermissionsTool),
PermissionsInstructionOptions {
exec_permission_approvals_enabled: next
.features
.enabled(Feature::ExecPermissionApprovals),
request_permissions_tool_enabled: next
.features
.enabled(Feature::RequestPermissionsTool),
network_proxy_active: next.network.is_some(),
},
)
.render(),
)

View File

@@ -27,6 +27,7 @@ use crate::context::AvailableSkillsInstructions;
use crate::context::CollaborationModeInstructions;
use crate::context::ContextualUserFragment;
use crate::context::NetworkRuleSaved;
use crate::context::PermissionsInstructionOptions;
use crate::context::PermissionsInstructions;
use crate::context::PersonalitySpecInstructions;
use crate::default_skill_metadata_budget;
@@ -2573,12 +2574,15 @@ impl Session {
turn_context.config.approvals_reviewer,
self.services.exec_policy.current().as_ref(),
&turn_context.cwd,
turn_context
.features
.enabled(Feature::ExecPermissionApprovals),
turn_context
.features
.enabled(Feature::RequestPermissionsTool),
PermissionsInstructionOptions {
exec_permission_approvals_enabled: turn_context
.features
.enabled(Feature::ExecPermissionApprovals),
request_permissions_tool_enabled: turn_context
.features
.enabled(Feature::RequestPermissionsTool),
network_proxy_active: turn_context.network.is_some(),
},
)
.render(),
);

View File

@@ -3,6 +3,7 @@ use codex_config::ConfigLayerStack;
use codex_core::ForkSnapshot;
use codex_core::config::Constrained;
use codex_core::context::ContextualUserFragment;
use codex_core::context::PermissionsInstructionOptions;
use codex_core::context::PermissionsInstructions;
use codex_core::load_exec_policy;
use codex_protocol::models::PermissionProfile;
@@ -581,8 +582,11 @@ async fn permissions_message_includes_writable_roots() -> Result<()> {
test.config.approvals_reviewer,
&exec_policy,
test.config.cwd.as_path(),
/*exec_permission_approvals_enabled*/ false,
/*request_permissions_tool_enabled*/ false,
PermissionsInstructionOptions {
exec_permission_approvals_enabled: false,
request_permissions_tool_enabled: false,
network_proxy_active: test.session_configured.network_proxy.is_some(),
},
)
.render();
let expected_normalized = normalize_line_endings(&expected);