Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
408 changes: 408 additions & 0 deletions docs/en/wework/developer-guide/wework-plugin-interactive-forms.md

Large diffs are not rendered by default.

408 changes: 408 additions & 0 deletions docs/zh/wework/developer-guide/wework-plugin-interactive-forms.md

Large diffs are not rendered by default.

44 changes: 26 additions & 18 deletions executor/src/agents/codex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ const CODEX_APPLY_PATCH_STREAMING_EVENTS_OVERRIDE: &str =
const CODEX_APPLY_PATCH_FREEFORM_OVERRIDE: &str = "features.apply_patch_freeform=true";
const CODEX_SUPPRESS_UNSTABLE_FEATURES_WARNING_OVERRIDE: &str =
"suppress_unstable_features_warning=true";
const CODEX_DISABLE_TOOL_CALL_MCP_ELICITATION_OVERRIDE: &str =
"features.tool_call_mcp_elicitation=false";
const DEFAULT_EXECUTOR_SERVER_PORT: u16 = 10001;
const SIDE_BOUNDARY_PROMPT: &str = r#"Side conversation boundary.

Expand Down Expand Up @@ -712,7 +714,7 @@ fn persistent_codex_app_server_launch_config(
]);
launch_config
.config_overrides
.extend(codex_streaming_patch_config_overrides());
.extend(codex_runtime_default_config_overrides());
launch_config
}

Expand Down Expand Up @@ -1952,7 +1954,7 @@ fn build_codex_launch_config(request: &ExecutionRequest) -> CodexLaunchConfig {
.push(shell_path_config_override());
launch_config
.config_overrides
.extend(codex_streaming_patch_config_overrides());
.extend(codex_runtime_default_config_overrides());
launch_config
.config_overrides
.extend(codex_model_config_overrides(&request.model_config));
Expand Down Expand Up @@ -2081,6 +2083,12 @@ fn codex_streaming_patch_config_overrides() -> Vec<String> {
]
}

fn codex_runtime_default_config_overrides() -> Vec<String> {
let mut overrides = codex_streaming_patch_config_overrides();
overrides.push(CODEX_DISABLE_TOOL_CALL_MCP_ELICITATION_OVERRIDE.to_owned());
overrides
}

fn codex_model_config_overrides(model_config: &Value) -> Vec<String> {
const DEFAULT_CODEX_MODEL_CONTEXT_WINDOW: i64 = 262_144;

Expand Down Expand Up @@ -3315,6 +3323,18 @@ fn resolve_codex_binary(value: &str) -> String {

const CODEX_DANGER_FULL_ACCESS_PERMISSION_PROFILE: &str = ":danger-full-access";

pub(crate) fn codex_runtime_approval_policy() -> Value {
json!({
"granular": {
"sandbox_approval": false,
"rules": false,
"skill_approval": false,
"request_permissions": false,
"mcp_elicitations": true,
}
})
}

fn insert_codex_runtime_permissions(params: &mut serde_json::Map<String, Value>) {
params.insert(
"permissions".to_owned(),
Expand Down Expand Up @@ -3406,10 +3426,7 @@ fn thread_start_params(request: &ExecutionRequest, launch_config: &CodexLaunchCo
params.insert("cwd".to_owned(), Value::String(cwd.to_owned()));
}
insert_runtime_workspace_roots(&mut params, request);
params.insert(
"approvalPolicy".to_owned(),
Value::String("never".to_owned()),
);
params.insert("approvalPolicy".to_owned(), codex_runtime_approval_policy());
insert_codex_runtime_permissions(&mut params);
if request.ephemeral {
params.insert("ephemeral".to_owned(), Value::Bool(true));
Expand Down Expand Up @@ -3437,10 +3454,7 @@ fn thread_fork_params(
params.insert("cwd".to_owned(), Value::String(cwd.to_owned()));
}
insert_runtime_workspace_roots(&mut params, request);
params.insert(
"approvalPolicy".to_owned(),
Value::String("never".to_owned()),
);
params.insert("approvalPolicy".to_owned(), codex_runtime_approval_policy());
insert_codex_runtime_permissions(&mut params);
if request.ephemeral {
params.insert("ephemeral".to_owned(), Value::Bool(true));
Expand Down Expand Up @@ -3501,10 +3515,7 @@ fn thread_resume_params(
params.insert("cwd".to_owned(), Value::String(cwd.to_owned()));
}
insert_runtime_workspace_roots(&mut params, request);
params.insert(
"approvalPolicy".to_owned(),
Value::String("never".to_owned()),
);
params.insert("approvalPolicy".to_owned(), codex_runtime_approval_policy());
insert_codex_runtime_permissions(&mut params);
Value::Object(params)
}
Expand Down Expand Up @@ -3552,10 +3563,7 @@ fn turn_start_params(
Value::String(client_user_message_id.to_owned()),
);
}
params.insert(
"approvalPolicy".to_owned(),
Value::String("never".to_owned()),
);
params.insert("approvalPolicy".to_owned(), codex_runtime_approval_policy());
insert_codex_runtime_permissions(&mut params);
if let Some(cwd) = request.cwd() {
params.insert("cwd".to_owned(), Value::String(cwd.to_owned()));
Expand Down
78 changes: 78 additions & 0 deletions executor/src/agents/codex/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,56 @@ fn persistent_app_server_enables_deferred_mcp_tool_search() {
assert!(!config
.config_overrides
.contains(&"features.tool_search_always_defer_mcp_tools=false".to_owned()));
assert!(config
.config_overrides
.contains(&CODEX_DISABLE_TOOL_CALL_MCP_ELICITATION_OVERRIDE.to_owned()));
}

#[test]
fn initialize_params_does_not_advertise_openai_form_elicitation_extension() {
let params = initialize_params();

assert_eq!(params["capabilities"]["experimentalApi"], true);
assert!(params["capabilities"]
.get("mcpServerOpenaiFormElicitation")
.is_none());
}

#[test]
fn mcp_form_elicitation_maps_enum_names_to_request_user_input_options() {
let params = json!({
"serverName": "wegent-sites",
"mode": "form",
"message": "请选择内网访问范围。",
"requestedSchema": {
"type": "object",
"properties": {
"audience": {
"type": "string",
"title": "访问范围",
"description": "请选择站点发布到内网后的访问范围。",
"enum": ["all", "owner", "custom"],
"enumNames": ["所有人", "仅自己", "指定人"]
}
},
"required": ["audience"]
}
});

let payload = mcp_server_elicitation_request_user_input_params(&params)
.expect("enum + enumNames form should map to request_user_input payload");

assert_eq!(payload["itemId"], "mcp_server_elicitation");
assert_eq!(payload["questions"][0]["id"], "audience");
assert_eq!(payload["questions"][0]["header"], "访问范围");
assert_eq!(
payload["questions"][0]["options"],
json!([
{"label": "所有人", "description": "all"},
{"label": "仅自己", "description": "owner"},
{"label": "指定人", "description": "custom"}
])
);
}

#[test]
Expand Down Expand Up @@ -274,6 +324,9 @@ fn codex_launch_config_enables_streaming_patch_updates() {
assert!(launch_config
.config_overrides
.contains(&CODEX_SUPPRESS_UNSTABLE_FEATURES_WARNING_OVERRIDE.to_owned()));
assert!(launch_config
.config_overrides
.contains(&CODEX_DISABLE_TOOL_CALL_MCP_ELICITATION_OVERRIDE.to_owned()));
}

#[test]
Expand Down Expand Up @@ -1254,11 +1307,36 @@ fn codex_permission_profile_is_applied_to_thread_and_turn_requests() {
params["permissions"],
CODEX_DANGER_FULL_ACCESS_PERMISSION_PROFILE
);
assert_eq!(params["approvalPolicy"], codex_runtime_approval_policy());
assert!(params.get("sandboxPolicy").is_none());
assert!(params.get("sandbox").is_none());
}
}

#[test]
fn codex_thread_launch_disables_tool_call_mcp_elicitation() {
let request = ExecutionRequest::default();
let mut launch_config = CodexLaunchConfig::default();
launch_config
.config_overrides
.push(CODEX_DISABLE_TOOL_CALL_MCP_ELICITATION_OVERRIDE.to_owned());

let thread_start = thread_start_params(&request, &launch_config);
let thread_resume = thread_resume_params("thread-1", &request, &launch_config);
let thread_fork = thread_fork_params("thread-1", None, &request, &launch_config);

for params in [thread_start, thread_resume, thread_fork] {
assert_eq!(
params["config"]["features.tool_call_mcp_elicitation"],
false
);
assert_eq!(
params["approvalPolicy"]["granular"]["mcp_elicitations"],
true
);
}
}

#[test]
fn codex_runtime_workspace_roots_are_applied_to_thread_and_turn_requests() {
let request = ExecutionRequest {
Expand Down
5 changes: 3 additions & 2 deletions executor/src/agents/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ use claude_code::{
};
pub use claude_options::{extract_claude_options, ClaudeOptions};
pub(crate) use codex::{
combined_codex_developer_instructions, configured_inference_model_provider,
mcp_server_elicitation_request_user_input_params, strip_wework_browser_instructions,
codex_runtime_approval_policy, combined_codex_developer_instructions,
configured_inference_model_provider, mcp_server_elicitation_request_user_input_params,
strip_wework_browser_instructions,
};
pub use codex::{
run_codex_app_server_turn, run_codex_app_server_turn_with_cancel, CodexActiveTurnCallback,
Expand Down
90 changes: 89 additions & 1 deletion executor/src/agents/runtime_capabilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1511,7 +1511,75 @@ fn ensure_object<'a>(object: &'a mut Map<String, Value>, key: &str) -> &'a mut M
}

fn collect_request_mcp_servers(request: &ExecutionRequest) -> BTreeMap<String, Value> {
extract_claude_options(request, &BTreeMap::new()).mcp_servers
let mut servers = extract_claude_options(request, &BTreeMap::new()).mcp_servers;
preserve_explicit_mcp_approval_modes(request, &mut servers);
servers
}

fn preserve_explicit_mcp_approval_modes(
request: &ExecutionRequest,
servers: &mut BTreeMap<String, Value>,
) {
if request_mode(request).as_deref() == Some("coordinate") {
if let Some(bots) = request.bot.as_array() {
for bot in bots {
preserve_explicit_mcp_approval_modes_from_value(
bot_mcp_servers_value(bot),
servers,
);
}
}
} else if let Some(bot) = primary_bot(request) {
preserve_explicit_mcp_approval_modes_from_value(bot_mcp_servers_value(bot), servers);
}

preserve_explicit_mcp_approval_modes_from_value(
Some(&Value::Array(request.mcp_servers.clone())),
servers,
);
}

fn bot_mcp_servers_value(bot: &Value) -> Option<&Value> {
bot.get("mcp_servers").or_else(|| bot.get("mcpServers"))
}

fn preserve_explicit_mcp_approval_modes_from_value(
value: Option<&Value>,
servers: &mut BTreeMap<String, Value>,
) {
match value {
Some(Value::Object(object)) => {
for (name, server) in object {
preserve_explicit_mcp_approval_mode(name, server, servers);
}
}
Some(Value::Array(values)) => {
for server in values {
if let Some(name) = server.get("name").and_then(Value::as_str) {
preserve_explicit_mcp_approval_mode(name, server, servers);
}
}
}
_ => {}
}
}

fn preserve_explicit_mcp_approval_mode(
name: &str,
source: &Value,
servers: &mut BTreeMap<String, Value>,
) {
let Some(mode) = source
.get("default_tools_approval_mode")
.or_else(|| source.get("defaultToolsApprovalMode"))
.cloned()
else {
return;
};
let Some(server) = servers.get_mut(name).and_then(Value::as_object_mut) else {
return;
};
server.insert("default_tools_approval_mode".to_owned(), mode);
}

fn mcp_server_headers_summary(servers: &BTreeMap<String, Value>) -> String {
Expand Down Expand Up @@ -1652,6 +1720,7 @@ fn codex_mcp_server_overrides(name: &str, server: &Value) -> Vec<String> {
}
}
}
append_codex_mcp_server_approval_override(&key, object, &mut overrides);
return overrides;
}
let Some(url) = object
Expand Down Expand Up @@ -1687,9 +1756,28 @@ fn codex_mcp_server_overrides(name: &str, server: &Value) -> Vec<String> {
overrides.push(format!("{key}.{target_key}={}", toml_value(value)));
}
}
append_codex_mcp_server_approval_override(&key, object, &mut overrides);
overrides
}

fn append_codex_mcp_server_approval_override(
key: &str,
object: &Map<String, Value>,
overrides: &mut Vec<String>,
) {
let approval_mode = object
.get("default_tools_approval_mode")
.or_else(|| object.get("defaultToolsApprovalMode"))
.and_then(Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty())
.unwrap_or("approve");
overrides.push(format!(
"{key}.default_tools_approval_mode={}",
toml_value(approval_mode)
));
}

fn load_global_mcp_records() -> BTreeMap<String, Value> {
let path = executor_home().join("capabilities/manifest.json");
let Ok(content) = fs::read_to_string(path) else {
Expand Down
Loading
Loading