Compare commits

..

3 Commits

Author SHA1 Message Date
Ahmed Ibrahim
200f07f1ec remove/docs/reference 2026-01-26 17:54:11 -08:00
Ahmed Ibrahim
737654923f remove/docs/reference 2026-01-26 17:51:23 -08:00
Ahmed Ibrahim
a9a56081d0 remove/docs/reference 2026-01-26 17:47:30 -08:00
53 changed files with 694 additions and 854 deletions

View File

@@ -15,8 +15,8 @@ You can also install via Homebrew (`brew install --cask codex`) or download a pl
## Documentation quickstart
- First run with Codex? Start with [`docs/getting-started.md`](../docs/getting-started.md) (links to the walkthrough for prompts, keyboard shortcuts, and session management).
- Want deeper control? See [`docs/config.md`](../docs/config.md) and [`docs/install.md`](../docs/install.md).
- First run with Codex? Start with the [Getting Started guide](https://developers.openai.com/codex) (links to the walkthrough for prompts, keyboard shortcuts, and session management).
- Want deeper control? See [Configuration documentation](https://developers.openai.com/codex/config-advanced/).
## What's new in the Rust CLI
@@ -24,13 +24,13 @@ The Rust implementation is now the maintained Codex CLI and serves as the defaul
### Config
Codex supports a rich set of configuration options. Note that the Rust CLI uses `config.toml` instead of `config.json`. See [`docs/config.md`](../docs/config.md) for details.
Codex supports a rich set of configuration options. Note that the Rust CLI uses `config.toml` instead of `config.json`. See [Configuration documentation](https://developers.openai.com/codex/config-advanced/) for details.
### Model Context Protocol Support
#### MCP client
Codex CLI functions as an MCP client that allows the Codex CLI and IDE extension to connect to MCP servers on startup. See the [`configuration documentation`](../docs/config.md#connecting-to-mcp-servers) for details.
Codex CLI functions as an MCP client that allows the Codex CLI and IDE extension to connect to MCP servers on startup. See the [configuration documentation](https://developers.openai.com/codex/config-advanced/) for details.
#### MCP server (experimental)
@@ -46,7 +46,7 @@ Use `codex mcp` to add/list/get/remove MCP server launchers defined in `config.t
### Notifications
You can enable notifications by configuring a script that is run whenever the agent finishes a turn. The [notify documentation](../docs/config.md#notify) includes a detailed example that explains how to get desktop notifications via [terminal-notifier](https://github.com/julienXX/terminal-notifier) on macOS. When Codex detects that it is running under WSL 2 inside Windows Terminal (`WT_SESSION` is set), the TUI automatically falls back to native Windows toast notifications so approval prompts and completed turns surface even though Windows Terminal does not implement OSC 9.
You can enable notifications by configuring a script that is run whenever the agent finishes a turn. The [notify documentation](https://developers.openai.com/codex/config-advanced/#notifications) includes a detailed example that explains how to get desktop notifications via [terminal-notifier](https://github.com/julienXX/terminal-notifier) on macOS. When Codex detects that it is running under WSL 2 inside Windows Terminal (`WT_SESSION` is set), the TUI automatically falls back to native Windows toast notifications so approval prompts and completed turns surface even though Windows Terminal does not implement OSC 9.
### `codex exec` to run Codex programmatically/non-interactively

View File

@@ -3605,7 +3605,10 @@ impl CodexMessageProcessor {
// Submit user input to the conversation.
let _ = conversation
.submit_user_turn_with_defaults(mapped_items, None)
.submit(Op::UserInput {
items: mapped_items,
final_output_json_schema: None,
})
.await;
// Acknowledge with an empty result.
@@ -3654,7 +3657,6 @@ impl CodexMessageProcessor {
let _ = conversation
.submit(Op::UserTurn {
use_thread_defaults: false,
items: mapped_items,
cwd,
approval_policy,
@@ -3869,35 +3871,36 @@ impl CodexMessageProcessor {
.map(V2UserInput::into_core)
.collect();
// Start the turn by submitting the user input with the thread's current defaults,
// overridden by any per-turn parameters.
let snapshot = thread.config_snapshot().await;
let cwd = params.cwd.unwrap_or(snapshot.cwd);
let approval_policy = params
.approval_policy
.map(AskForApproval::to_core)
.unwrap_or(snapshot.approval_policy);
let sandbox_policy = params
.sandbox_policy
.map(|policy| policy.to_core())
.unwrap_or(snapshot.sandbox_policy);
let model = params.model.unwrap_or(snapshot.model);
let effort = params.effort.or(snapshot.reasoning_effort);
let summary = params.summary.unwrap_or(snapshot.reasoning_summary);
let personality = params.personality.or(snapshot.personality);
let has_any_overrides = params.cwd.is_some()
|| params.approval_policy.is_some()
|| params.sandbox_policy.is_some()
|| params.model.is_some()
|| params.effort.is_some()
|| params.summary.is_some()
|| params.collaboration_mode.is_some()
|| params.personality.is_some();
// If any overrides are provided, update the session turn context first.
if has_any_overrides {
let _ = thread
.submit(Op::OverrideTurnContext {
cwd: params.cwd,
approval_policy: params.approval_policy.map(AskForApproval::to_core),
sandbox_policy: params.sandbox_policy.map(|p| p.to_core()),
model: params.model,
effort: params.effort.map(Some),
summary: params.summary,
collaboration_mode: params.collaboration_mode,
personality: params.personality,
})
.await;
}
// Start the turn by submitting the user input. Return its submission id as turn_id.
let turn_id = thread
.submit(Op::UserTurn {
use_thread_defaults: false,
.submit(Op::UserInput {
items: mapped_items,
cwd,
approval_policy,
sandbox_policy,
model,
effort,
summary,
final_output_json_schema: params.output_schema,
collaboration_mode: params.collaboration_mode,
personality,
})
.await;

View File

@@ -2,5 +2,4 @@
This file has moved. Please see the latest configuration documentation here:
- Full config docs: [docs/config.md](../docs/config.md)
- MCP servers section: [docs/config.md#connecting-to-mcp-servers](../docs/config.md#connecting-to-mcp-servers)
- Configuration documentation: https://developers.openai.com/codex/config-advanced/

View File

@@ -72,26 +72,19 @@ impl AgentControl {
prompt: String,
) -> CodexResult<String> {
let state = self.upgrade()?;
let thread = state.get_thread(agent_id).await?;
let snapshot = thread.config_snapshot().await;
let op = Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: prompt,
// Agent control prompts are plain text with no UI text elements.
text_elements: Vec::new(),
}],
cwd: snapshot.cwd,
approval_policy: snapshot.approval_policy,
sandbox_policy: snapshot.sandbox_policy,
model: snapshot.model,
effort: snapshot.reasoning_effort,
summary: snapshot.reasoning_summary,
final_output_json_schema: None,
collaboration_mode: None,
personality: snapshot.personality,
};
let result = state.send_op(agent_id, op).await;
let result = state
.send_op(
agent_id,
Op::UserInput {
items: vec![UserInput::Text {
text: prompt,
// Agent control prompts are plain text with no UI text elements.
text_elements: Vec::new(),
}],
final_output_json_schema: None,
},
)
.await;
if matches!(result, Err(CodexErr::InternalAgentDied)) {
let _ = state.remove_thread(&agent_id).await;
self.state.release_spawned_thread(agent_id);
@@ -354,8 +347,7 @@ mod tests {
#[tokio::test]
async fn send_prompt_submits_user_message() {
let harness = AgentControlHarness::new().await;
let (thread_id, thread) = harness.start_thread().await;
let snapshot = thread.config_snapshot().await;
let (thread_id, _thread) = harness.start_thread().await;
let submission_id = harness
.control
@@ -365,21 +357,12 @@ mod tests {
assert!(!submission_id.is_empty());
let expected = (
thread_id,
Op::UserTurn {
use_thread_defaults: false,
Op::UserInput {
items: vec![UserInput::Text {
text: "hello from tests".to_string(),
text_elements: Vec::new(),
}],
cwd: snapshot.cwd,
approval_policy: snapshot.approval_policy,
sandbox_policy: snapshot.sandbox_policy,
model: snapshot.model,
effort: snapshot.reasoning_effort,
summary: snapshot.reasoning_summary,
final_output_json_schema: None,
collaboration_mode: None,
personality: snapshot.personality,
},
);
let captured = harness
@@ -398,29 +381,19 @@ mod tests {
.spawn_agent(harness.config.clone(), "spawned".to_string(), None)
.await
.expect("spawn_agent should succeed");
let thread = harness
let _thread = harness
.manager
.get_thread(thread_id)
.await
.expect("thread should be registered");
let snapshot = thread.config_snapshot().await;
let expected = (
thread_id,
Op::UserTurn {
use_thread_defaults: false,
Op::UserInput {
items: vec![UserInput::Text {
text: "spawned".to_string(),
text_elements: Vec::new(),
}],
cwd: snapshot.cwd,
approval_policy: snapshot.approval_policy,
sandbox_policy: snapshot.sandbox_policy,
model: snapshot.model,
effort: snapshot.reasoning_effort,
summary: snapshot.reasoning_summary,
final_output_json_schema: None,
collaboration_mode: None,
personality: snapshot.personality,
},
);
let captured = harness

View File

@@ -521,7 +521,6 @@ impl SessionConfiguration {
sandbox_policy: self.sandbox_policy.get().clone(),
cwd: self.cwd.clone(),
reasoning_effort: self.collaboration_mode.reasoning_effort(),
reasoning_summary: self.model_reasoning_summary,
personality: self.personality,
session_source: self.session_source.clone(),
}
@@ -733,7 +732,7 @@ impl Session {
None
} else {
Some(format!(
"Enable it with `--enable {canonical}` or `[features].{canonical}` in config.toml. See https://github.com/openai/codex/blob/main/docs/config.md#feature-flags for details."
"Enable it with `--enable {canonical}` or `[features].{canonical}` in config.toml. See https://developers.openai.com/codex/config-advanced/ for details."
))
};
post_session_configured_events.push(Event {
@@ -2176,8 +2175,9 @@ async fn submission_loop(sess: Arc<Session>, config: Arc<Config>, rx_sub: Receiv
)
.await;
}
Op::UserTurn { .. } => {
handlers::user_turn(&sess, sub.id.clone(), sub.op, &mut previous_context).await;
Op::UserInput { .. } | Op::UserTurn { .. } => {
handlers::user_input_or_turn(&sess, sub.id.clone(), sub.op, &mut previous_context)
.await;
}
Op::ExecApproval { id, decision } => {
handlers::exec_approval(&sess, id, decision).await;
@@ -2343,83 +2343,59 @@ mod handlers {
}
}
pub async fn user_turn(
pub async fn user_input_or_turn(
sess: &Arc<Session>,
sub_id: String,
op: Op,
previous_context: &mut Option<Arc<TurnContext>>,
) {
let Op::UserTurn {
use_thread_defaults,
cwd,
approval_policy,
sandbox_policy,
model,
effort,
summary,
final_output_json_schema,
items,
collaboration_mode,
personality,
} = op
else {
unreachable!();
};
let snapshot = if use_thread_defaults {
let state = sess.state.lock().await;
Some(state.session_configuration.thread_config_snapshot())
} else {
None
};
let (cwd, approval_policy, sandbox_policy, model, effort, summary, snapshot_personality) =
if let Some(snapshot) = snapshot {
let (items, updates) = match op {
Op::UserTurn {
cwd,
approval_policy,
sandbox_policy,
model,
effort,
summary,
final_output_json_schema,
items,
collaboration_mode,
personality,
} => {
let collaboration_mode = collaboration_mode.or_else(|| {
Some(CollaborationMode {
mode: ModeKind::Custom,
settings: Settings {
model: model.clone(),
reasoning_effort: effort,
developer_instructions: None,
},
})
});
(
snapshot.cwd,
snapshot.approval_policy,
snapshot.sandbox_policy,
snapshot.model,
snapshot.reasoning_effort,
snapshot.reasoning_summary,
snapshot.personality,
)
} else {
(
cwd,
approval_policy,
sandbox_policy,
model,
effort,
summary,
None,
)
};
let personality = if use_thread_defaults {
personality.or(snapshot_personality)
} else {
personality
};
let collaboration_mode = if use_thread_defaults {
collaboration_mode
} else {
collaboration_mode.or_else(|| {
Some(CollaborationMode {
mode: ModeKind::Custom,
settings: Settings {
model: model.clone(),
reasoning_effort: effort,
developer_instructions: None,
items,
SessionSettingsUpdate {
cwd: Some(cwd),
approval_policy: Some(approval_policy),
sandbox_policy: Some(sandbox_policy),
collaboration_mode,
reasoning_summary: Some(summary),
final_output_json_schema: Some(final_output_json_schema),
personality,
},
})
})
};
let updates = SessionSettingsUpdate {
cwd: Some(cwd),
approval_policy: Some(approval_policy),
sandbox_policy: Some(sandbox_policy),
collaboration_mode,
reasoning_summary: Some(summary),
final_output_json_schema: Some(final_output_json_schema),
personality,
)
}
Op::UserInput {
items,
final_output_json_schema,
} => (
items,
SessionSettingsUpdate {
final_output_json_schema: Some(final_output_json_schema),
..Default::default()
},
),
_ => unreachable!(),
};
let previous_collaboration_mode = sess

View File

@@ -126,19 +126,9 @@ pub(crate) async fn run_codex_thread_one_shot(
.await?;
// Send the initial input to kick off the one-shot turn.
let snapshot = io.thread_config_snapshot().await;
io.submit(Op::UserTurn {
use_thread_defaults: false,
io.submit(Op::UserInput {
items: input,
cwd: snapshot.cwd,
approval_policy: snapshot.approval_policy,
sandbox_policy: snapshot.sandbox_policy,
model: snapshot.model,
effort: snapshot.reasoning_effort,
summary: snapshot.reasoning_summary,
final_output_json_schema: None,
collaboration_mode: None,
personality: snapshot.personality,
})
.await?;

View File

@@ -5,13 +5,10 @@ use crate::protocol::Event;
use crate::protocol::Op;
use crate::protocol::Submission;
use codex_protocol::config_types::Personality;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::openai_models::ReasoningEffort;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::protocol::SessionSource;
use codex_protocol::user_input::UserInput;
use serde_json::Value;
use std::path::PathBuf;
use tokio::sync::watch;
@@ -23,7 +20,6 @@ pub struct ThreadConfigSnapshot {
pub sandbox_policy: SandboxPolicy,
pub cwd: PathBuf,
pub reasoning_effort: Option<ReasoningEffort>,
pub reasoning_summary: ReasoningSummary,
pub personality: Option<Personality>,
pub session_source: SessionSource,
}
@@ -71,38 +67,4 @@ impl CodexThread {
pub async fn config_snapshot(&self) -> ThreadConfigSnapshot {
self.codex.thread_config_snapshot().await
}
/// Build a user turn using the thread's current default settings.
pub async fn user_turn_with_defaults(
&self,
items: Vec<UserInput>,
final_output_json_schema: Option<Value>,
) -> Op {
let snapshot = self.config_snapshot().await;
Op::UserTurn {
use_thread_defaults: true,
items,
cwd: snapshot.cwd,
approval_policy: snapshot.approval_policy,
sandbox_policy: snapshot.sandbox_policy,
model: snapshot.model,
effort: snapshot.reasoning_effort,
summary: snapshot.reasoning_summary,
final_output_json_schema,
collaboration_mode: None,
personality: snapshot.personality,
}
}
/// Submit a user turn using the thread's current default settings.
pub async fn submit_user_turn_with_defaults(
&self,
items: Vec<UserInput>,
final_output_json_schema: Option<Value>,
) -> CodexResult<String> {
let op = self
.user_turn_with_defaults(items, final_output_json_schema)
.await;
self.submit(op).await
}
}

View File

@@ -889,7 +889,7 @@ mod tests {
.collect();
assert_eq!(ops_for_agent.len(), 2);
assert!(matches!(ops_for_agent[0], Op::Interrupt));
assert!(matches!(ops_for_agent[1], Op::UserTurn { .. }));
assert!(matches!(ops_for_agent[1], Op::UserInput { .. }));
let _ = thread
.thread

View File

@@ -268,7 +268,6 @@ impl TestCodex {
let session_model = self.session_configured.model.clone();
self.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: prompt.into(),
text_elements: Vec::new(),

View File

@@ -45,13 +45,13 @@ async fn interrupt_long_running_tool_emits_turn_aborted() {
// Kick off a turn that triggers the function call.
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "start sleep".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -99,13 +99,13 @@ async fn interrupt_tool_records_history_entries() {
let codex = Arc::clone(&fixture.codex);
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "start history recording".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -117,13 +117,13 @@ async fn interrupt_tool_records_history_entries() {
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnAborted(_))).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "follow up".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -197,13 +197,13 @@ async fn interrupt_persists_turn_aborted_marker_in_next_request() {
let codex = Arc::clone(&fixture.codex);
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "start interrupt marker".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -215,13 +215,13 @@ async fn interrupt_persists_turn_aborted_marker_in_next_request() {
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnAborted(_))).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "follow up".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();

View File

@@ -300,7 +300,6 @@ async fn apply_patch_cli_move_without_content_change_has_no_turn_diff(
let model = test.session_configured.model.clone();
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "rename without content change".into(),
text_elements: Vec::new(),
@@ -890,7 +889,6 @@ async fn apply_patch_shell_command_heredoc_with_cd_emits_turn_diff() -> Result<(
let model = test.session_configured.model.clone();
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "apply via shell heredoc with cd".into(),
text_elements: Vec::new(),
@@ -971,7 +969,6 @@ async fn apply_patch_shell_command_failure_propagates_error_and_skips_diff() ->
let model = test.session_configured.model.clone();
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "apply patch via shell".into(),
text_elements: Vec::new(),
@@ -1122,7 +1119,6 @@ async fn apply_patch_emits_turn_diff_event_with_unified_diff(
let model = test.session_configured.model.clone();
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "emit diff".into(),
text_elements: Vec::new(),
@@ -1186,7 +1182,6 @@ async fn apply_patch_turn_diff_for_rename_with_content_change(
let model = test.session_configured.model.clone();
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "rename with change".into(),
text_elements: Vec::new(),
@@ -1258,7 +1253,6 @@ async fn apply_patch_aggregates_diff_across_multiple_tool_calls() -> Result<()>
let model = test.session_configured.model.clone();
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "aggregate diffs".into(),
text_elements: Vec::new(),
@@ -1330,7 +1324,6 @@ async fn apply_patch_aggregates_diff_preserves_success_after_failure() -> Result
let model = test.session_configured.model.clone();
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "apply patch twice with failure".into(),
text_elements: Vec::new(),

View File

@@ -490,7 +490,6 @@ async fn submit_turn(
test.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: prompt.into(),
text_elements: Vec::new(),

View File

@@ -294,13 +294,13 @@ async fn resume_includes_initial_messages_and_sends_prior_items() {
// 2) Submit new input; the request body must include the prior items, then initial context, then new user input.
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -393,13 +393,13 @@ async fn includes_conversation_id_and_model_headers_in_request() {
.expect("create new conversation");
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -447,13 +447,13 @@ async fn includes_base_instructions_override_in_request() {
.thread;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -504,13 +504,13 @@ async fn chatgpt_auth_sends_correct_request() {
.expect("create new conversation");
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -597,13 +597,13 @@ async fn prefers_apikey_when_config_prefers_apikey_even_with_chatgpt_tokens() {
.expect("create new conversation");
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -639,13 +639,13 @@ async fn includes_user_instructions_message_in_request() {
.thread;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -719,13 +719,13 @@ async fn skills_append_to_instructions() {
.thread;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -772,13 +772,13 @@ async fn includes_configured_effort_in_request() -> anyhow::Result<()> {
.await?;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -810,13 +810,13 @@ async fn includes_no_effort_in_request() -> anyhow::Result<()> {
.await?;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -846,13 +846,13 @@ async fn includes_default_reasoning_effort_in_request_when_defined_by_model_info
let TestCodex { codex, .. } = test_codex().with_model("gpt-5.1").build(&server).await?;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -899,7 +899,6 @@ async fn user_turn_collaboration_mode_overrides_model_and_effort() -> anyhow::Re
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
@@ -945,13 +944,13 @@ async fn configured_reasoning_summary_is_sent() -> anyhow::Result<()> {
.await?;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -985,13 +984,13 @@ async fn reasoning_summary_is_omitted_when_disabled() -> anyhow::Result<()> {
.await?;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1019,13 +1018,13 @@ async fn includes_default_verbosity_in_request() -> anyhow::Result<()> {
let TestCodex { codex, .. } = test_codex().with_model("gpt-5.1").build(&server).await?;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1060,13 +1059,13 @@ async fn configured_verbosity_not_sent_for_models_without_support() -> anyhow::R
.await?;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1100,13 +1099,13 @@ async fn configured_verbosity_is_sent() -> anyhow::Result<()> {
.await?;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1156,13 +1155,13 @@ async fn includes_developer_instructions_message_in_request() {
.thread;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1407,13 +1406,13 @@ async fn token_count_includes_rate_limits_snapshot() {
.thread;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1566,13 +1565,13 @@ async fn usage_limit_error_emits_rate_limit_event() -> anyhow::Result<()> {
});
let submission_id = codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.expect("submission should succeed while emitting usage limit error events");
@@ -1637,25 +1636,25 @@ async fn context_window_error_sets_total_tokens_to_model_window() -> anyhow::Res
.await?;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "seed turn".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "trigger context window".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
let token_event = wait_for_event(&codex, |event| {
@@ -1770,13 +1769,13 @@ async fn azure_overrides_assign_properties_used_for_responses_url() {
.thread;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1854,13 +1853,13 @@ async fn env_var_overrides_loaded_auth() {
.thread;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1929,39 +1928,39 @@ async fn history_dedupes_streamed_and_final_messages_across_turns() {
// Turn 1: user sends U1; wait for completion.
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "U1".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
// Turn 2: user sends U2; wait for completion.
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "U2".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
// Turn 3: user sends U3; wait for completion.
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "U3".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;

View File

@@ -70,13 +70,13 @@ async fn no_collaboration_instructions_by_default() -> Result<()> {
let test = test_codex().build(&server).await?;
test.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -113,13 +113,13 @@ async fn user_input_includes_collaboration_instructions_after_override() -> Resu
.await?;
test.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -144,7 +144,6 @@ async fn collaboration_instructions_added_on_user_turn() -> Result<()> {
test.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
@@ -196,7 +195,6 @@ async fn override_then_user_turn_uses_updated_collaboration_instructions() -> Re
test.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
@@ -250,7 +248,6 @@ async fn user_turn_overrides_collaboration_instructions_after_override() -> Resu
test.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
@@ -304,13 +301,13 @@ async fn collaboration_mode_update_emits_new_instruction_message() -> Result<()>
.await?;
test.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 1".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -328,13 +325,13 @@ async fn collaboration_mode_update_emits_new_instruction_message() -> Result<()>
.await?;
test.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 2".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -373,13 +370,13 @@ async fn collaboration_mode_update_noop_does_not_append() -> Result<()> {
.await?;
test.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 1".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -397,13 +394,13 @@ async fn collaboration_mode_update_noop_does_not_append() -> Result<()> {
.await?;
test.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 2".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -449,26 +446,26 @@ async fn resume_replays_collaboration_instructions() -> Result<()> {
initial
.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&initial.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
let resumed = builder.resume(&server, home, rollout_path).await?;
resumed
.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "after resume".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&resumed.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -503,13 +500,13 @@ async fn empty_collaboration_instructions_are_ignored() -> Result<()> {
.await?;
test.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;

View File

@@ -158,13 +158,13 @@ async fn summarize_context_three_requests_and_instructions() {
// 1) Normal user input should hit server once.
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello world".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -180,13 +180,13 @@ async fn summarize_context_three_requests_and_instructions() {
// 3) Next user input third hit; history should include only the summary.
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: THIRD_USER_MSG.into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -574,13 +574,13 @@ async fn multiple_auto_compact_per_task_runs_after_token_limit_hit() {
// Start the conversation with the user message
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: user_message.into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.expect("submit user input");
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -1051,39 +1051,39 @@ async fn auto_compact_runs_after_token_limit_hit() {
let codex = thread_manager.start_thread(config).await.unwrap().thread;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: FIRST_AUTO_MSG.into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: SECOND_AUTO_MSG.into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: POST_AUTO_USER_MSG.into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1284,7 +1284,6 @@ async fn auto_compact_runs_after_resume_when_token_usage_is_over_limit() {
resumed
.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: follow_up_user.into(),
text_elements: Vec::new(),
@@ -1396,37 +1395,37 @@ async fn auto_compact_persists_rollout_entries() {
} = thread_manager.start_thread(config).await.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: FIRST_AUTO_MSG.into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: SECOND_AUTO_MSG.into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: POST_AUTO_USER_MSG.into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -1513,13 +1512,13 @@ async fn manual_compact_retries_after_context_window_error() {
.thread;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "first turn".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -1647,13 +1646,13 @@ async fn manual_compact_twice_preserves_latest_user_messages() {
.thread;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: first_user_message.into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -1662,13 +1661,13 @@ async fn manual_compact_twice_preserves_latest_user_messages() {
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: second_user_message.into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -1677,13 +1676,13 @@ async fn manual_compact_twice_preserves_latest_user_messages() {
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: final_user_message.into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -1858,13 +1857,13 @@ async fn auto_compact_allows_multiple_attempts_when_interleaved_with_other_turn_
let mut auto_compact_lifecycle_events = Vec::new();
for user in [MULTI_AUTO_MSG, follow_up_user, final_user] {
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: user.into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1972,26 +1971,26 @@ async fn auto_compact_triggers_after_function_call_over_95_percent_usage() {
.thread;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: FUNCTION_CALL_LIMIT_MSG.into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
wait_for_event(&codex, |msg| matches!(msg, EventMsg::TurnComplete(_))).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: follow_up_user.into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -2104,13 +2103,13 @@ async fn auto_compact_counts_encrypted_reasoning_before_last_user() {
.enumerate()
{
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: user.into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -2221,13 +2220,13 @@ async fn auto_compact_runs_when_reasoning_header_clears_between_turns() {
for user in [first_user, second_user, third_user] {
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: user.into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;

View File

@@ -71,13 +71,13 @@ async fn remote_compact_replaces_history_for_followups() -> Result<()> {
.await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello remote compact".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -85,13 +85,13 @@ async fn remote_compact_replaces_history_for_followups() -> Result<()> {
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "after compact".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -193,13 +193,13 @@ async fn remote_compact_runs_automatically() -> Result<()> {
.await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello remote compact".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
let message = wait_for_event_match(&codex, |ev| match ev {
EventMsg::ContextCompacted(_) => Some(true),
@@ -274,13 +274,13 @@ async fn remote_compact_persists_replacement_history_in_rollout() -> Result<()>
.await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "needs compaction".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;

View File

@@ -987,13 +987,13 @@ async fn start_test_conversation(
async fn user_turn(conversation: &Arc<CodexThread>, text: &str) {
conversation
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: text.into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.expect("submit user turn");
wait_for_event(conversation, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;

View File

@@ -49,7 +49,7 @@ async fn emits_deprecation_notice_for_legacy_feature_flag() -> anyhow::Result<()
assert_eq!(
details.as_deref(),
Some(
"Enable it with `--enable unified_exec` or `[features].unified_exec` in config.toml. See https://github.com/openai/codex/blob/main/docs/config.md#feature-flags for details."
"Enable it with `--enable unified_exec` or `[features].unified_exec` in config.toml. See https://developers.openai.com/codex/config-advanced/ for details."
),
);

View File

@@ -70,7 +70,6 @@ async fn execpolicy_blocks_shell_invocation() -> Result<()> {
let session_model = test.session_configured.model.clone();
test.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "run shell command".into(),
text_elements: Vec::new(),

View File

@@ -5,6 +5,7 @@ use codex_core::ThreadManager;
use codex_core::built_in_model_providers;
use codex_core::parse_turn_item;
use codex_core::protocol::EventMsg;
use codex_core::protocol::Op;
use codex_core::protocol::RolloutItem;
use codex_core::protocol::RolloutLine;
use codex_protocol::items::TurnItem;
@@ -66,13 +67,13 @@ async fn fork_thread_twice_drops_to_first_message() {
// Send three user messages; wait for three completed turns.
for text in ["first", "second", "third"] {
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: text.to_string(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
let _ = wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;

View File

@@ -111,7 +111,6 @@ async fn copy_paste_local_image_persists_rollout_request_shape() -> anyhow::Resu
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![
UserInput::LocalImage {
path: abs_path.clone(),
@@ -193,7 +192,6 @@ async fn drag_drop_image_persists_rollout_request_shape() -> anyhow::Result<()>
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![
UserInput::Image {
image_url: image_url.clone(),

View File

@@ -4,6 +4,7 @@ use anyhow::Ok;
use codex_core::protocol::EventMsg;
use codex_core::protocol::ItemCompletedEvent;
use codex_core::protocol::ItemStartedEvent;
use codex_core::protocol::Op;
use codex_protocol::items::TurnItem;
use codex_protocol::user_input::ByteRange;
use codex_protocol::user_input::TextElement;
@@ -49,7 +50,10 @@ async fn user_message_item_is_emitted() -> anyhow::Result<()> {
};
codex
.submit_user_turn_with_defaults(vec![expected_input.clone()], None)
.submit(Op::UserInput {
items: vec![expected_input.clone()],
final_output_json_schema: None,
})
.await?;
let started_item = wait_for_event_match(&codex, |ev| match ev {
@@ -99,13 +103,13 @@ async fn assistant_message_item_is_emitted() -> anyhow::Result<()> {
mount_sse_once(&server, first_response).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "please summarize results".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
let started = wait_for_event_match(&codex, |ev| match ev {
@@ -157,13 +161,13 @@ async fn reasoning_item_is_emitted() -> anyhow::Result<()> {
mount_sse_once(&server, first_response).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "explain your reasoning".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
let started = wait_for_event_match(&codex, |ev| match ev {
@@ -217,13 +221,13 @@ async fn web_search_item_is_emitted() -> anyhow::Result<()> {
mount_sse_once(&server, first_response).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "find the weather".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
let started = wait_for_event_match(&codex, |ev| match ev {
@@ -271,13 +275,13 @@ async fn agent_message_content_delta_has_item_metadata() -> anyhow::Result<()> {
mount_sse_once(&server, stream).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "please stream text".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
let (started_turn_id, started_item) = wait_for_event_match(&codex, |ev| match ev {
@@ -338,13 +342,13 @@ async fn reasoning_content_delta_has_item_metadata() -> anyhow::Result<()> {
mount_sse_once(&server, stream).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "reason through it".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
let reasoning_item = wait_for_event_match(&codex, |ev| match ev {
@@ -397,13 +401,13 @@ async fn reasoning_raw_content_delta_respects_flag() -> anyhow::Result<()> {
mount_sse_once(&server, stream).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "show raw reasoning".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
let reasoning_item = wait_for_event_match(&codex, |ev| match ev {

View File

@@ -74,7 +74,6 @@ async fn codex_returns_json_result(model: String) -> anyhow::Result<()> {
// 1) Normal user input should hit server once.
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "hello world".into(),
text_elements: Vec::new(),

View File

@@ -87,7 +87,6 @@ async fn renews_cache_ttl_on_matching_models_etag() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "hi".into(),
text_elements: Vec::new(),

View File

@@ -98,7 +98,6 @@ async fn refresh_models_on_models_etag_mismatch_and_avoid_duplicate_models_fetch
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "please run a tool".into(),
text_elements: Vec::new(),

View File

@@ -42,13 +42,13 @@ async fn responses_api_emits_api_request_event() {
let TestCodex { codex, .. } = test_codex().build(&server).await.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -85,13 +85,13 @@ async fn process_sse_emits_tracing_for_output_item() {
let TestCodex { codex, .. } = test_codex().build(&server).await.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -125,13 +125,13 @@ async fn process_sse_emits_failed_event_on_parse_error() {
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -166,13 +166,13 @@ async fn process_sse_records_failed_event_when_stream_closes_without_completed()
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -227,13 +227,13 @@ async fn process_sse_failed_event_records_response_error_message() {
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -286,13 +286,13 @@ async fn process_sse_failed_event_logs_parse_error() {
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -332,13 +332,13 @@ async fn process_sse_failed_event_logs_missing_error() {
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -387,13 +387,13 @@ async fn process_sse_failed_event_logs_response_completed_parse_error() {
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -439,13 +439,13 @@ async fn process_sse_emits_completed_telemetry() {
let TestCodex { codex, .. } = test_codex().build(&server).await.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -508,13 +508,13 @@ async fn handle_responses_span_records_response_kind_and_tool_name() {
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -574,13 +574,13 @@ async fn record_responses_sets_span_fields_for_response_events() {
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -655,13 +655,13 @@ async fn handle_response_item_records_tool_result_for_custom_tool_call() {
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -724,13 +724,13 @@ async fn handle_response_item_records_tool_result_for_function_call() {
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -803,13 +803,13 @@ async fn handle_response_item_records_tool_result_for_local_shell_missing_ids()
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -866,13 +866,13 @@ async fn handle_response_item_records_tool_result_for_local_shell_call() {
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -972,13 +972,13 @@ async fn handle_container_exec_autoapprove_from_config_records_tool_decision() {
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1022,13 +1022,13 @@ async fn handle_container_exec_user_approved_records_tool_decision() {
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "approved".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1082,13 +1082,13 @@ async fn handle_container_exec_user_approved_for_session_records_tool_decision()
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "persist".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1142,13 +1142,13 @@ async fn handle_sandbox_error_user_approves_retry_records_tool_decision() {
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "retry".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1202,13 +1202,13 @@ async fn handle_container_exec_user_denies_records_tool_decision() {
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "deny".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1262,13 +1262,13 @@ async fn handle_sandbox_error_user_approves_for_session_records_tool_decision()
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "persist".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1323,13 +1323,13 @@ async fn handle_sandbox_error_user_denies_records_tool_decision() {
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "deny".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();

View File

@@ -1,4 +1,5 @@
use codex_core::protocol::EventMsg;
use codex_core::protocol::Op;
use codex_protocol::user_input::UserInput;
use core_test_support::responses;
use core_test_support::responses::ev_completed;
@@ -96,13 +97,13 @@ async fn injected_user_input_triggers_follow_up_request_with_deltas() {
.codex;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "first prompt".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -112,13 +113,13 @@ async fn injected_user_input_triggers_follow_up_request_with_deltas() {
.await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "second prompt".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();

View File

@@ -58,13 +58,13 @@ async fn permissions_message_sent_once_on_start() -> Result<()> {
let test = builder.build(&server).await?;
test.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -91,13 +91,13 @@ async fn permissions_message_added_on_override_change() -> Result<()> {
let test = builder.build(&server).await?;
test.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 1".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -115,13 +115,13 @@ async fn permissions_message_added_on_override_change() -> Result<()> {
.await?;
test.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 2".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -154,24 +154,24 @@ async fn permissions_message_not_added_when_no_change() -> Result<()> {
let test = builder.build(&server).await?;
test.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 1".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
test.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 2".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -211,13 +211,13 @@ async fn resume_replays_permissions_messages() -> Result<()> {
initial
.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 1".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&initial.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -237,26 +237,26 @@ async fn resume_replays_permissions_messages() -> Result<()> {
initial
.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 2".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&initial.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
let resumed = builder.resume(&server, home, rollout_path).await?;
resumed
.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "after resume".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&resumed.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -293,13 +293,13 @@ async fn resume_and_fork_append_permissions_messages() -> Result<()> {
initial
.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 1".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&initial.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -319,13 +319,13 @@ async fn resume_and_fork_append_permissions_messages() -> Result<()> {
initial
.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 2".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&initial.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -340,13 +340,13 @@ async fn resume_and_fork_append_permissions_messages() -> Result<()> {
let resumed = builder.resume(&server, home, rollout_path.clone()).await?;
resumed
.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "after resume".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&resumed.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -368,13 +368,13 @@ async fn resume_and_fork_append_permissions_messages() -> Result<()> {
.await?;
forked
.thread
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "after fork".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&forked.thread, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -416,13 +416,13 @@ async fn permissions_message_includes_writable_roots() -> Result<()> {
let test = builder.build(&server).await?;
test.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;

View File

@@ -85,7 +85,6 @@ async fn user_turn_personality_none_does_not_add_update_message() -> anyhow::Res
test.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
@@ -132,7 +131,6 @@ async fn config_personality_some_sets_instructions_template() -> anyhow::Result<
test.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
@@ -189,7 +187,6 @@ async fn user_turn_personality_some_adds_update_message() -> anyhow::Result<()>
test.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
@@ -223,7 +220,6 @@ async fn user_turn_personality_some_adds_update_message() -> anyhow::Result<()>
test.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
@@ -343,7 +339,6 @@ async fn user_turn_personality_remote_model_template_includes_update_message() -
test.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
@@ -377,7 +372,6 @@ async fn user_turn_personality_remote_model_template_includes_update_message() -
test.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),

View File

@@ -110,24 +110,24 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> {
.base_instructions;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 1".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 2".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -186,24 +186,24 @@ async fn codex_mini_latest_tools() -> anyhow::Result<()> {
.await?;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 1".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 2".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -250,24 +250,24 @@ async fn prefixes_context_and_instructions_once_and_consistently_across_requests
.await?;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 1".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 2".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -328,13 +328,13 @@ async fn overrides_turn_context_but_keeps_cached_prefix_and_key_constant() -> an
// First turn
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 1".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -360,13 +360,13 @@ async fn overrides_turn_context_but_keeps_cached_prefix_and_key_constant() -> an
// Second turn after overrides
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 2".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -436,13 +436,13 @@ async fn override_before_first_turn_emits_environment_context() -> anyhow::Resul
.await?;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "first message".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -556,13 +556,13 @@ async fn per_turn_overrides_keep_cached_prefix_and_key_constant() -> anyhow::Res
// First turn
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello 1".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
@@ -577,7 +577,6 @@ async fn per_turn_overrides_keep_cached_prefix_and_key_constant() -> anyhow::Res
};
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "hello 2".into(),
text_elements: Vec::new(),
@@ -673,7 +672,6 @@ async fn send_user_turn_with_no_changes_does_not_send_environment_context() -> a
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "hello 1".into(),
text_elements: Vec::new(),
@@ -693,7 +691,6 @@ async fn send_user_turn_with_no_changes_does_not_send_environment_context() -> a
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "hello 2".into(),
text_elements: Vec::new(),
@@ -775,7 +772,6 @@ async fn send_user_turn_with_changes_sends_environment_context() -> anyhow::Resu
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "hello 1".into(),
text_elements: Vec::new(),
@@ -795,7 +791,6 @@ async fn send_user_turn_with_changes_sends_environment_context() -> anyhow::Resu
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "hello 2".into(),
text_elements: Vec::new(),

View File

@@ -1,5 +1,6 @@
use anyhow::Result;
use codex_core::protocol::EventMsg;
use codex_core::protocol::Op;
use codex_protocol::user_input::UserInput;
use core_test_support::responses::ev_response_created;
use core_test_support::responses::mount_sse_once;
@@ -39,13 +40,13 @@ async fn quota_exceeded_emits_single_error_event() -> Result<()> {
let test = builder.build(&server).await?;
test.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "quota?".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();

View File

@@ -167,7 +167,6 @@ async fn remote_models_remote_model_uses_unified_exec() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "run call".into(),
text_elements: Vec::new(),
@@ -378,7 +377,6 @@ async fn remote_models_apply_remote_base_instructions() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "hello remote".into(),
text_elements: Vec::new(),

View File

@@ -3,6 +3,7 @@
use codex_core::CodexAuth;
use codex_core::features::Feature;
use codex_core::protocol::EventMsg;
use codex_core::protocol::Op;
use codex_protocol::user_input::UserInput;
use core_test_support::responses::ev_completed;
use core_test_support::responses::ev_response_created;
@@ -35,13 +36,13 @@ async fn request_body_is_zstd_compressed_for_codex_backend_when_enabled() -> any
let codex = builder.build(&server).await?.codex;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "compress me".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
// Wait until the task completes so the request definitely hit the server.
@@ -79,13 +80,13 @@ async fn request_body_is_not_compressed_for_api_key_auth_even_when_enabled() ->
let codex = builder.build(&server).await?.codex;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "do not compress".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;

View File

@@ -123,7 +123,6 @@ async fn request_user_input_round_trip_resolves_pending() -> anyhow::Result<()>
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "please confirm".into(),
text_elements: Vec::new(),
@@ -245,7 +244,6 @@ where
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "please confirm".into(),
text_elements: Vec::new(),

View File

@@ -1,5 +1,6 @@
use anyhow::Result;
use codex_core::protocol::EventMsg;
use codex_core::protocol::Op;
use codex_protocol::user_input::ByteRange;
use codex_protocol::user_input::TextElement;
use codex_protocol::user_input::UserInput;
@@ -44,13 +45,13 @@ async fn resume_includes_initial_messages_from_rollout_events() -> Result<()> {
)];
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "Record some messages".into(),
text_elements: text_elements.clone(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |event| matches!(event, EventMsg::TurnComplete(_))).await;
@@ -103,13 +104,13 @@ async fn resume_includes_initial_messages_from_reasoning_events() -> Result<()>
mount_sse_once(&server, initial_sse).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "Record reasoning messages".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |event| matches!(event, EventMsg::TurnComplete(_))).await;
@@ -164,13 +165,13 @@ async fn resume_switches_models_preserves_base_instructions() -> Result<()> {
let initial_mock = mount_sse_once(&server, initial_sse).await;
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "Record initial instructions".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |event| matches!(event, EventMsg::TurnComplete(_))).await;
@@ -194,13 +195,13 @@ async fn resume_switches_models_preserves_base_instructions() -> Result<()> {
let resumed = resume_builder.resume(&server, home, rollout_path).await?;
resumed
.codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "Resume with different model".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&resumed.codex, |event| {
matches!(event, EventMsg::TurnComplete(_))

View File

@@ -705,13 +705,13 @@ async fn review_history_surfaces_in_parent_session() {
// 2) Continue in the parent session; request input must not include any review items.
let followup = "back to parent".to_string();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: followup.clone(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
let _complete = wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;

View File

@@ -108,7 +108,6 @@ async fn stdio_server_round_trip() -> anyhow::Result<()> {
fixture
.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "call the rmcp echo tool".into(),
text_elements: Vec::new(),
@@ -250,7 +249,6 @@ async fn stdio_image_responses_round_trip() -> anyhow::Result<()> {
fixture
.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "call the rmcp image tool".into(),
text_elements: Vec::new(),
@@ -450,7 +448,6 @@ async fn stdio_image_completions_round_trip() -> anyhow::Result<()> {
fixture
.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "call the rmcp image tool".into(),
text_elements: Vec::new(),
@@ -598,7 +595,6 @@ async fn stdio_server_propagates_whitelisted_env_vars() -> anyhow::Result<()> {
fixture
.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "call the rmcp echo tool".into(),
text_elements: Vec::new(),
@@ -757,7 +753,6 @@ async fn streamable_http_tool_call_round_trip() -> anyhow::Result<()> {
fixture
.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "call the rmcp streamable http echo tool".into(),
text_elements: Vec::new(),
@@ -948,7 +943,6 @@ async fn streamable_http_with_oauth_round_trip() -> anyhow::Result<()> {
fixture
.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "call the rmcp streamable http oauth echo tool".into(),
text_elements: Vec::new(),

View File

@@ -89,7 +89,6 @@ async fn run_snapshot_command(command: &str) -> Result<SnapshotRun> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "run unified exec with shell snapshot".into(),
text_elements: Vec::new(),
@@ -164,7 +163,6 @@ async fn run_shell_command_snapshot(command: &str) -> Result<SnapshotRun> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "run shell_command with shell snapshot".into(),
text_elements: Vec::new(),
@@ -300,7 +298,6 @@ async fn shell_command_snapshot_still_intercepts_apply_patch() -> Result<()> {
let model = test.session_configured.model.clone();
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "apply patch via shell_command with snapshot".into(),
text_elements: Vec::new(),

View File

@@ -61,7 +61,6 @@ async fn user_turn_includes_skill_instructions() -> Result<()> {
let session_model = test.session_configured.model.clone();
test.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![
UserInput::Text {
text: "please use $demo".to_string(),

View File

@@ -1,6 +1,7 @@
use codex_core::ModelProviderInfo;
use codex_core::WireApi;
use codex_core::protocol::EventMsg;
use codex_core::protocol::Op;
use codex_protocol::user_input::UserInput;
use core_test_support::load_sse_fixture_with_id;
use core_test_support::skip_if_no_network;
@@ -84,13 +85,13 @@ async fn continue_after_stream_error() {
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "first message".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();
@@ -103,13 +104,13 @@ async fn continue_after_stream_error() {
// mock server SSE stream. If the agent failed to clear the running task on
// error above, this submission would be rejected/queued indefinitely.
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "follow up".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();

View File

@@ -4,6 +4,7 @@
use codex_core::ModelProviderInfo;
use codex_core::WireApi;
use codex_core::protocol::EventMsg;
use codex_core::protocol::Op;
use codex_protocol::user_input::UserInput;
use core_test_support::load_sse_fixture;
use core_test_support::load_sse_fixture_with_id;
@@ -91,13 +92,13 @@ async fn retries_on_early_close() {
.unwrap();
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await
.unwrap();

View File

@@ -79,7 +79,6 @@ async fn shell_tool_executes_command_and_streams_output() -> anyhow::Result<()>
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "please run the shell command".into(),
text_elements: Vec::new(),
@@ -149,7 +148,6 @@ async fn update_plan_tool_emits_plan_update_event() -> anyhow::Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "please update the plan".into(),
text_elements: Vec::new(),
@@ -229,7 +227,6 @@ async fn update_plan_tool_rejects_malformed_payload() -> anyhow::Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "please update the plan".into(),
text_elements: Vec::new(),
@@ -321,7 +318,6 @@ async fn apply_patch_tool_executes_and_emits_patch_events() -> anyhow::Result<()
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "please apply a patch".into(),
text_elements: Vec::new(),
@@ -421,7 +417,6 @@ async fn apply_patch_reports_parse_diagnostics() -> anyhow::Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "please apply a patch".into(),
text_elements: Vec::new(),

View File

@@ -36,7 +36,6 @@ async fn run_turn(test: &TestCodex, prompt: &str) -> anyhow::Result<()> {
test.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: prompt.into(),
text_elements: Vec::new(),
@@ -355,7 +354,6 @@ async fn shell_tools_start_before_response_completed_when_stream_delayed() -> an
let session_model = test.session_configured.model.clone();
test.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "stream delayed completion".into(),
text_elements: Vec::new(),

View File

@@ -538,7 +538,6 @@ async fn mcp_image_output_preserves_image_and_no_text_summary() -> Result<()> {
fixture
.codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "call the rmcp image tool".into(),
text_elements: Vec::new(),

View File

@@ -198,7 +198,6 @@ async fn unified_exec_intercepts_apply_patch_exec_command() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "apply patch via unified exec".into(),
text_elements: Vec::new(),
@@ -328,7 +327,6 @@ async fn unified_exec_emits_exec_command_begin_event() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "emit begin event".into(),
text_elements: Vec::new(),
@@ -407,7 +405,6 @@ async fn unified_exec_resolves_relative_workdir() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "run relative workdir test".into(),
text_elements: Vec::new(),
@@ -489,7 +486,6 @@ async fn unified_exec_respects_workdir_override() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "run workdir test".into(),
text_elements: Vec::new(),
@@ -583,7 +579,6 @@ async fn unified_exec_emits_exec_command_end_event() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "emit end event".into(),
text_elements: Vec::new(),
@@ -659,7 +654,6 @@ async fn unified_exec_emits_output_delta_for_exec_command() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "emit delta".into(),
text_elements: Vec::new(),
@@ -736,7 +730,6 @@ async fn unified_exec_full_lifecycle_with_background_end_event() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "exercise full unified exec lifecycle".into(),
text_elements: Vec::new(),
@@ -867,7 +860,6 @@ async fn unified_exec_emits_terminal_interaction_for_write_stdin() -> Result<()>
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "stdin delta".into(),
text_elements: Vec::new(),
@@ -1005,7 +997,6 @@ async fn unified_exec_terminal_interaction_captures_delayed_output() -> Result<(
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "delayed terminal interaction output".into(),
text_elements: Vec::new(),
@@ -1166,7 +1157,6 @@ async fn unified_exec_emits_one_begin_and_one_end_event() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "check poll event behavior".into(),
text_elements: Vec::new(),
@@ -1265,7 +1255,6 @@ async fn exec_command_reports_chunk_and_exit_metadata() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "run metadata test".into(),
text_elements: Vec::new(),
@@ -1384,7 +1373,6 @@ async fn unified_exec_defaults_to_pipe() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "check default pipe mode".into(),
text_elements: Vec::new(),
@@ -1475,7 +1463,6 @@ async fn unified_exec_can_enable_tty() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "check tty enabled".into(),
text_elements: Vec::new(),
@@ -1557,7 +1544,6 @@ async fn unified_exec_respects_early_exit_notifications() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "watch early exit timing".into(),
text_elements: Vec::new(),
@@ -1689,7 +1675,6 @@ async fn write_stdin_returns_exit_metadata_and_clears_session() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "test write_stdin exit behavior".into(),
text_elements: Vec::new(),
@@ -1858,7 +1843,6 @@ async fn unified_exec_emits_end_event_when_session_dies_via_stdin() -> Result<()
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "end on exit".into(),
text_elements: Vec::new(),
@@ -1936,7 +1920,6 @@ async fn unified_exec_closes_long_running_session_at_turn_end() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "close unified exec processes on turn end".into(),
text_elements: Vec::new(),
@@ -2059,7 +2042,6 @@ async fn unified_exec_reuses_session_via_stdin() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "run unified exec".into(),
text_elements: Vec::new(),
@@ -2195,7 +2177,6 @@ PY
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "exercise lag handling".into(),
text_elements: Vec::new(),
@@ -2310,7 +2291,6 @@ async fn unified_exec_timeout_and_followup_poll() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "check timeout".into(),
text_elements: Vec::new(),
@@ -2407,7 +2387,6 @@ PY
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "summarize large output".into(),
text_elements: Vec::new(),
@@ -2489,7 +2468,6 @@ async fn unified_exec_runs_under_sandbox() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "summarize large output".into(),
text_elements: Vec::new(),
@@ -2595,7 +2573,6 @@ async fn unified_exec_python_prompt_under_seatbelt() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "start python under seatbelt".into(),
text_elements: Vec::new(),
@@ -2691,7 +2668,6 @@ async fn unified_exec_runs_on_all_platforms() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "summarize large output".into(),
text_elements: Vec::new(),
@@ -2827,7 +2803,6 @@ async fn unified_exec_prunes_exited_sessions_first() -> Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "fill session cache".into(),
text_elements: Vec::new(),

View File

@@ -3,6 +3,7 @@
use std::os::unix::fs::PermissionsExt;
use codex_core::protocol::EventMsg;
use codex_core::protocol::Op;
use codex_protocol::user_input::UserInput;
use core_test_support::fs_wait;
use core_test_support::responses;
@@ -56,13 +57,13 @@ echo -n "${@: -1}" > $(dirname "${0}")/notify.txt"#,
// 1) Normal user input should hit server once.
codex
.submit_user_turn_with_defaults(
vec![UserInput::Text {
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "hello world".into(),
text_elements: Vec::new(),
}],
None,
)
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;

View File

@@ -78,7 +78,6 @@ async fn user_turn_with_local_image_attaches_image() -> anyhow::Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::LocalImage {
path: abs_path.clone(),
}],
@@ -172,7 +171,6 @@ async fn view_image_tool_attaches_local_image() -> anyhow::Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "please add the screenshot".into(),
text_elements: Vec::new(),
@@ -306,7 +304,6 @@ async fn view_image_tool_errors_when_path_is_directory() -> anyhow::Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "please attach the folder".into(),
text_elements: Vec::new(),
@@ -382,7 +379,6 @@ async fn view_image_tool_placeholder_for_non_image_files() -> anyhow::Result<()>
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "please use the view_image tool to read the json file".into(),
text_elements: Vec::new(),
@@ -477,7 +473,6 @@ async fn view_image_tool_errors_when_file_missing() -> anyhow::Result<()> {
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::Text {
text: "please attach the missing image".into(),
text_elements: Vec::new(),
@@ -562,7 +557,6 @@ async fn replaces_invalid_local_image_after_bad_request() -> anyhow::Result<()>
codex
.submit(Op::UserTurn {
use_thread_defaults: false,
items: vec![UserInput::LocalImage {
path: abs_path.clone(),
}],

View File

@@ -23,7 +23,7 @@ These are entities exit on the codex backend. The intent of this section is to e
3. `Task`
- A `Task` is `Codex` executing work in response to user input.
- `Session` has at most one `Task` running at a time.
- Receiving `Op::UserTurn` starts a `Task`
- Receiving `Op::UserTurn` starts a `Task` (`Op::UserInput` is legacy)
- Consists of a series of `Turn`s
- The `Task` executes to until:
- The `Model` completes the task and there is no output to feed into an additional `Turn`
@@ -66,6 +66,7 @@ For complete documentation of the `Op` and `EventMsg` variants, refer to [protoc
- `Op`
- `Op::UserTurn` Any input from the user to kick off a `Turn`
- `Op::UserInput` Legacy form of user input
- `Op::Interrupt` Interrupts a running turn
- `Op::ExecApproval` Approve or deny code execution
- `Op::UserInputAnswer` Provide answers for a `request_user_input` tool call

View File

@@ -466,7 +466,6 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
} => {
let task_id = thread
.submit(Op::UserTurn {
use_thread_defaults: false,
items,
cwd: default_cwd,
approval_policy: default_approval_policy,

View File

@@ -18,6 +18,7 @@ use codex_core::protocol::ApplyPatchApprovalRequestEvent;
use codex_core::protocol::Event;
use codex_core::protocol::EventMsg;
use codex_core::protocol::ExecApprovalRequestEvent;
use codex_core::protocol::Op;
use codex_core::protocol::Submission;
use codex_core::protocol::TurnCompleteEvent;
use codex_protocol::ThreadId;
@@ -116,19 +117,16 @@ pub async fn run_codex_tool_session(
.lock()
.await
.insert(id.clone(), thread_id);
let op = thread
.user_turn_with_defaults(
vec![UserInput::Text {
let submission = Submission {
id: sub_id.clone(),
op: Op::UserInput {
items: vec![UserInput::Text {
text: initial_prompt.clone(),
// MCP tool prompts are plain text with no UI element ranges.
text_elements: Vec::new(),
}],
None,
)
.await;
let submission = Submission {
id: sub_id.clone(),
op,
final_output_json_schema: None,
},
};
if let Err(e) = thread.submit_with_id(submission).await {
@@ -166,17 +164,17 @@ pub async fn run_codex_tool_session_reply(
.lock()
.await
.insert(request_id.clone(), thread_id);
let op = thread
.user_turn_with_defaults(
vec![UserInput::Text {
if let Err(e) = thread
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: prompt,
// MCP tool prompts are plain text with no UI element ranges.
text_elements: Vec::new(),
}],
None,
)
.await;
if let Err(e) = thread.submit(op).await {
final_output_json_schema: None,
})
.await
{
tracing::error!("Failed to submit user input: {e}");
let result = create_call_tool_result_with_thread_id(
thread_id,

View File

@@ -61,10 +61,6 @@ pub const COLLABORATION_MODE_OPEN_TAG: &str = "<collaboration_mode>";
pub const COLLABORATION_MODE_CLOSE_TAG: &str = "</collaboration_mode>";
pub const USER_MESSAGE_BEGIN: &str = "## My request for Codex:";
const fn is_false(value: &bool) -> bool {
!*value
}
/// Submission Queue Entry - requests from user
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
pub struct Submission {
@@ -91,13 +87,21 @@ pub enum Op {
/// This server sends [`EventMsg::TurnAborted`] in response.
Interrupt,
/// User input with full turn context.
UserTurn {
/// If true, defaults are computed at handling time from the session.
/// When set, collaboration mode is only updated if explicitly provided.
#[serde(default, skip_serializing_if = "is_false")]
use_thread_defaults: bool,
/// Legacy user input.
///
/// Prefer [`Op::UserTurn`] so the caller provides full turn context
/// (cwd/approval/sandbox/model/etc.) for each turn.
UserInput {
/// User input items, see `InputItem`
items: Vec<UserInput>,
/// Optional JSON Schema used to constrain the final assistant message for this turn.
#[serde(skip_serializing_if = "Option::is_none")]
final_output_json_schema: Option<Value>,
},
/// Similar to [`Op::UserInput`], but contains additional context required
/// for a turn of a [`crate::codex_thread::CodexThread`].
UserTurn {
/// User input items, see `InputItem`
items: Vec<UserInput>,
@@ -139,7 +143,7 @@ pub enum Op {
/// All fields are optional; when omitted, the existing value is preserved.
/// This does not enqueue any input it only updates defaults used for
/// turns that rely on persistent session-level context (for example,
/// [`Op::UserTurn`]).
/// [`Op::UserInput`]).
OverrideTurnContext {
/// Updated `cwd` for sandbox/tool calls.
#[serde(skip_serializing_if = "Option::is_none")]
@@ -2394,7 +2398,35 @@ mod tests {
}
#[test]
fn user_turn_serialization_includes_final_output_json_schema_when_some() -> Result<()> {
fn user_input_serialization_omits_final_output_json_schema_when_none() -> Result<()> {
let op = Op::UserInput {
items: Vec::new(),
final_output_json_schema: None,
};
let json_op = serde_json::to_value(op)?;
assert_eq!(json_op, json!({ "type": "user_input", "items": [] }));
Ok(())
}
#[test]
fn user_input_deserializes_without_final_output_json_schema_field() -> Result<()> {
let op: Op = serde_json::from_value(json!({ "type": "user_input", "items": [] }))?;
assert_eq!(
op,
Op::UserInput {
items: Vec::new(),
final_output_json_schema: None,
}
);
Ok(())
}
#[test]
fn user_input_serialization_includes_final_output_json_schema_when_some() -> Result<()> {
let schema = json!({
"type": "object",
"properties": {
@@ -2403,32 +2435,17 @@ mod tests {
"required": ["answer"],
"additionalProperties": false
});
let cwd = PathBuf::from("/tmp");
let op = Op::UserTurn {
use_thread_defaults: false,
let op = Op::UserInput {
items: Vec::new(),
cwd: cwd.clone(),
approval_policy: AskForApproval::Never,
sandbox_policy: SandboxPolicy::ReadOnly,
model: "test-model".to_string(),
effort: None,
summary: ReasoningSummaryConfig::Auto,
final_output_json_schema: Some(schema.clone()),
collaboration_mode: None,
personality: None,
};
let json_op = serde_json::to_value(op)?;
assert_eq!(
json_op,
json!({
"type": "user_turn",
"type": "user_input",
"items": [],
"cwd": cwd,
"approval_policy": "never",
"sandbox_policy": { "type": "read-only" },
"model": "test-model",
"summary": "auto",
"final_output_json_schema": schema,
})
);

View File

@@ -2843,7 +2843,6 @@ impl ChatWidget {
.model_personality
.filter(|_| self.current_model_supports_personality());
let op = Op::UserTurn {
use_thread_defaults: false,
items,
cwd: self.config.cwd.clone(),
approval_policy: self.config.approval_policy.value(),

View File

@@ -1220,7 +1220,6 @@ async fn submit_user_message_with_mode_sets_coding_collaboration_mode() {
match next_submit_op(&mut op_rx) {
Op::UserTurn {
use_thread_defaults: false,
collaboration_mode:
Some(CollaborationMode {
mode: ModeKind::Code,
@@ -2244,7 +2243,6 @@ async fn collab_slash_command_opens_picker_and_updates_mode() {
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
match next_submit_op(&mut op_rx) {
Op::UserTurn {
use_thread_defaults: false,
collaboration_mode:
Some(CollaborationMode {
mode: ModeKind::Code,
@@ -2263,7 +2261,6 @@ async fn collab_slash_command_opens_picker_and_updates_mode() {
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
match next_submit_op(&mut op_rx) {
Op::UserTurn {
use_thread_defaults: false,
collaboration_mode:
Some(CollaborationMode {
mode: ModeKind::Code,
@@ -2401,7 +2398,6 @@ async fn collab_mode_is_not_sent_until_selected() {
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
match next_submit_op(&mut op_rx) {
Op::UserTurn {
use_thread_defaults: false,
collaboration_mode,
personality: None,
..
@@ -2434,7 +2430,6 @@ async fn user_turn_includes_personality_from_config() {
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
match next_submit_op(&mut op_rx) {
Op::UserTurn {
use_thread_defaults: false,
personality: Some(Personality::Friendly),
..
} => {}