mirror of
https://github.com/openai/codex.git
synced 2026-09-11 20:36:49 +00:00
## What changed - Represent MCP tool calls as approval actions and route them through the session-level approval flow for permission hooks, reviewer selection, rejection handling, and resolution telemetry. - Preserve MCP-specific user prompts, session and persistent approval choices, and the approval policy and reviewer captured for the tool call. ## Testing - Add coverage for permission hooks resolving MCP calls before user or automatic review, persistence choices, captured policy routing, and MCP turn metadata with strict automatic review enabled. GitOrigin-RevId: 8e134b3d17a19dc62b3b56076d2cd05a54c9f638
147 lines
5.1 KiB
Rust
147 lines
5.1 KiB
Rust
mod approvals;
|
|
pub(crate) mod code_mode;
|
|
pub(crate) mod context;
|
|
pub(crate) mod events;
|
|
mod executed_tool_calls;
|
|
pub(crate) mod handlers;
|
|
pub(crate) mod hook_names;
|
|
pub(crate) mod hosted_spec;
|
|
pub(crate) mod lifecycle;
|
|
pub(crate) mod network_approval;
|
|
pub(crate) mod orchestrator;
|
|
pub(crate) mod parallel;
|
|
pub(crate) mod registry;
|
|
pub(crate) mod router;
|
|
pub(crate) mod runtimes;
|
|
pub(crate) mod sandboxing;
|
|
pub(crate) mod spec_plan;
|
|
pub(crate) mod tool_dispatch_trace;
|
|
mod tool_namespaces_info;
|
|
|
|
use std::borrow::Cow;
|
|
|
|
use crate::session::turn_context::TurnContext;
|
|
pub(crate) use approvals::ApprovalContext;
|
|
use codex_features::Feature;
|
|
use codex_protocol::exec_output::ExecToolCallOutput;
|
|
use codex_protocol::openai_models::ToolMode;
|
|
use codex_tools::ToolName;
|
|
use codex_utils_output_truncation::TruncationPolicy;
|
|
use codex_utils_output_truncation::formatted_truncate_text;
|
|
use codex_utils_output_truncation::truncate_text;
|
|
pub(crate) use executed_tool_calls::ExecutedToolCallRecorder;
|
|
pub use router::ToolRouter;
|
|
|
|
// Telemetry preview limits: keep log events smaller than model budgets.
|
|
pub(crate) const TELEMETRY_PREVIEW_MAX_BYTES: usize = 2 * 1024; // 2 KiB
|
|
pub(crate) const TELEMETRY_PREVIEW_MAX_LINES: usize = 64; // lines
|
|
pub(crate) const TELEMETRY_PREVIEW_TRUNCATION_NOTICE: &str =
|
|
"[... telemetry preview truncated ...]";
|
|
|
|
/// Legacy boundaries such as hook payloads, telemetry tags, and Responses tool
|
|
/// names still require a single flattened string. Keep comparisons and sorting
|
|
/// on `ToolName` itself; use this only when crossing those boundaries.
|
|
pub(crate) fn flat_tool_name(tool_name: &ToolName) -> Cow<'_, str> {
|
|
if tool_name.is_default_namespace() {
|
|
return Cow::Borrowed(tool_name.name.as_str());
|
|
}
|
|
|
|
match tool_name.namespace.as_deref() {
|
|
Some(namespace) => {
|
|
let mut name = String::with_capacity(namespace.len() + tool_name.name.len());
|
|
name.push_str(namespace);
|
|
name.push_str(&tool_name.name);
|
|
Cow::Owned(name)
|
|
}
|
|
None => Cow::Borrowed(tool_name.name.as_str()),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn tool_user_shell_type(
|
|
user_shell: &crate::shell::Shell,
|
|
) -> codex_tools::ToolUserShellType {
|
|
match user_shell.shell_type {
|
|
crate::shell::ShellType::Zsh => codex_tools::ToolUserShellType::Zsh,
|
|
crate::shell::ShellType::Bash => codex_tools::ToolUserShellType::Bash,
|
|
crate::shell::ShellType::PowerShell => codex_tools::ToolUserShellType::PowerShell,
|
|
crate::shell::ShellType::Sh => codex_tools::ToolUserShellType::Sh,
|
|
crate::shell::ShellType::Cmd => codex_tools::ToolUserShellType::Cmd,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn requested_tool_mode(turn_context: &TurnContext) -> ToolMode {
|
|
turn_context.model_info.tool_mode.unwrap_or_else(|| {
|
|
if turn_context.config.features.enabled(Feature::CodeModeOnly) {
|
|
ToolMode::CodeModeOnly
|
|
} else if turn_context.config.features.enabled(Feature::CodeMode) {
|
|
ToolMode::CodeMode
|
|
} else {
|
|
ToolMode::Direct
|
|
}
|
|
})
|
|
}
|
|
|
|
pub(crate) fn effective_tool_mode(turn_context: &TurnContext) -> ToolMode {
|
|
let requested_tool_mode = requested_tool_mode(turn_context);
|
|
if !turn_context.code_mode_available
|
|
&& requested_tool_mode == ToolMode::CodeMode
|
|
&& !turn_context.config.code_mode.disable_in_process_fallback
|
|
{
|
|
ToolMode::Direct
|
|
} else {
|
|
requested_tool_mode
|
|
}
|
|
}
|
|
|
|
/// Format the combined exec output for sending back to the model.
|
|
/// Includes exit code and duration metadata; truncates large bodies safely.
|
|
pub fn format_exec_output_for_model(
|
|
exec_output: &ExecToolCallOutput,
|
|
truncation_policy: TruncationPolicy,
|
|
) -> String {
|
|
// round to 1 decimal place
|
|
let duration_seconds = ((exec_output.duration.as_secs_f32()) * 10.0).round() / 10.0;
|
|
|
|
let content = build_content_with_timeout(exec_output);
|
|
|
|
let total_lines = content.lines().count();
|
|
|
|
let formatted_output = truncate_text(&content, truncation_policy);
|
|
|
|
let mut sections = Vec::new();
|
|
|
|
sections.push(format!("Exit code: {}", exec_output.exit_code));
|
|
sections.push(format!("Wall time: {duration_seconds} seconds"));
|
|
if total_lines != formatted_output.lines().count() {
|
|
sections.push(format!("Total output lines: {total_lines}"));
|
|
}
|
|
|
|
sections.push("Output:".to_string());
|
|
sections.push(formatted_output);
|
|
|
|
sections.join("\n")
|
|
}
|
|
|
|
pub fn format_exec_output_str(
|
|
exec_output: &ExecToolCallOutput,
|
|
truncation_policy: TruncationPolicy,
|
|
) -> String {
|
|
let content = build_content_with_timeout(exec_output);
|
|
|
|
// Truncate for model consumption before serialization.
|
|
formatted_truncate_text(&content, truncation_policy)
|
|
}
|
|
|
|
/// Extracts exec output content and prepends a timeout message if the command timed out.
|
|
fn build_content_with_timeout(exec_output: &ExecToolCallOutput) -> String {
|
|
if exec_output.timed_out {
|
|
format!(
|
|
"command timed out after {} milliseconds\n{}",
|
|
exec_output.duration.as_millis(),
|
|
exec_output.aggregated_output.text
|
|
)
|
|
} else {
|
|
exec_output.aggregated_output.text.clone()
|
|
}
|
|
}
|