feat: expose outputSchema to user_turn/turn_start app_server API (#8377)

What changed
- Added `outputSchema` support to the app-server APIs, mirroring `codex
exec --output-schema` behavior.
- V1 `sendUserTurn` now accepts `outputSchema` and constrains the final
assistant message for that turn.
- V2 `turn/start` now accepts `outputSchema` and constrains the final
assistant message for that turn (explicitly per-turn only).

Core behavior
- `Op::UserTurn` already supported `final_output_json_schema`; now V1
`sendUserTurn` forwards `outputSchema` into that field.
- `Op::UserInput` now carries `final_output_json_schema` for per-turn
settings updates; core maps it into
`SessionSettingsUpdate.final_output_json_schema` so it applies to the
created turn context.
- V2 `turn/start` does NOT persist the schema via `OverrideTurnContext`
(it’s applied only for the current turn). Other overrides
(cwd/model/etc) keep their existing persistent behavior.

API / docs
- `codex-rs/app-server-protocol/src/protocol/v1.rs`: add `output_schema:
Option<serde_json::Value>` to `SendUserTurnParams` (serialized as
`outputSchema`).
- `codex-rs/app-server-protocol/src/protocol/v2.rs`: add `output_schema:
Option<JsonValue>` to `TurnStartParams` (serialized as `outputSchema`).
- `codex-rs/app-server/README.md`: document `outputSchema` for
`turn/start` and clarify it applies only to the current turn.
- `codex-rs/docs/codex_mcp_interface.md`: document `outputSchema` for v1
`sendUserTurn` and v2 `turn/start`.

Tests added/updated
- New app-server integration tests asserting `outputSchema` is forwarded
into outbound `/responses` requests as `text.format`:
  - `codex-rs/app-server/tests/suite/output_schema.rs`
  - `codex-rs/app-server/tests/suite/v2/output_schema.rs`
- Added per-turn semantics tests (schema does not leak to the next
turn):
  - `send_user_turn_output_schema_is_per_turn_v1`
  - `turn_start_output_schema_is_per_turn_v2`
- Added protocol wire-compat tests for the merged op:
  - serialize omits `final_output_json_schema` when `None`
  - deserialize works when field is missing
  - serialize includes `final_output_json_schema` when `Some(schema)`

Call site updates (high level)
- Updated all `Op::UserInput { .. }` constructions to include
`final_output_json_schema`:
  - `codex-rs/app-server/src/codex_message_processor.rs`
  - `codex-rs/core/src/codex_delegate.rs`
  - `codex-rs/mcp-server/src/codex_tool_runner.rs`
  - `codex-rs/tui/src/chatwidget.rs`
  - `codex-rs/tui2/src/chatwidget.rs`
  - plus impacted core tests.

Validation
- `just fmt`
- `cargo test -p codex-core`
- `cargo test -p codex-app-server`
- `cargo test -p codex-mcp-server`
- `cargo test -p codex-tui`
- `cargo test -p codex-tui2`
- `cargo test -p codex-protocol`
- `cargo clippy --all-features --tests --profile dev --fix -- -D
warnings`
This commit is contained in:
Anton Panasenko
2026-01-05 10:27:00 -08:00
committed by GitHub
parent 1d8e2b4da8
commit 807f8a43c2
32 changed files with 722 additions and 8 deletions

View File

@@ -49,6 +49,7 @@ async fn interrupt_long_running_tool_emits_turn_aborted() {
items: vec![UserInput::Text {
text: "start sleep".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -101,6 +102,7 @@ async fn interrupt_tool_records_history_entries() {
items: vec![UserInput::Text {
text: "start history recording".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -117,6 +119,7 @@ async fn interrupt_tool_records_history_entries() {
items: vec![UserInput::Text {
text: "follow up".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();

View File

@@ -290,6 +290,7 @@ async fn resume_includes_initial_messages_and_sends_prior_items() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -365,6 +366,7 @@ async fn includes_conversation_id_and_model_headers_in_request() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -424,6 +426,7 @@ async fn includes_base_instructions_override_in_request() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -488,6 +491,7 @@ async fn chatgpt_auth_sends_correct_request() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -582,6 +586,7 @@ async fn prefers_apikey_when_config_prefers_apikey_even_with_chatgpt_tokens() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -622,6 +627,7 @@ async fn includes_user_instructions_message_in_request() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -692,6 +698,7 @@ async fn skills_append_to_instructions() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -741,6 +748,7 @@ async fn includes_configured_effort_in_request() -> anyhow::Result<()> {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -777,6 +785,7 @@ async fn includes_no_effort_in_request() -> anyhow::Result<()> {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -811,6 +820,7 @@ async fn includes_default_reasoning_effort_in_request_when_defined_by_model_fami
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -849,6 +859,7 @@ async fn configured_reasoning_summary_is_sent() -> anyhow::Result<()> {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -887,6 +898,7 @@ async fn reasoning_summary_is_omitted_when_disabled() -> anyhow::Result<()> {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -919,6 +931,7 @@ async fn includes_default_verbosity_in_request() -> anyhow::Result<()> {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -958,6 +971,7 @@ async fn configured_verbosity_not_sent_for_models_without_support() -> anyhow::R
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -996,6 +1010,7 @@ async fn configured_verbosity_is_sent() -> anyhow::Result<()> {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1050,6 +1065,7 @@ async fn includes_developer_instructions_message_in_request() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1280,6 +1296,7 @@ async fn token_count_includes_rate_limits_snapshot() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1437,6 +1454,7 @@ async fn usage_limit_error_emits_rate_limit_event() -> anyhow::Result<()> {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.expect("submission should succeed while emitting usage limit error events");
@@ -1506,6 +1524,7 @@ async fn context_window_error_sets_total_tokens_to_model_window() -> anyhow::Res
items: vec![UserInput::Text {
text: "seed turn".into(),
}],
final_output_json_schema: None,
})
.await?;
@@ -1516,6 +1535,7 @@ async fn context_window_error_sets_total_tokens_to_model_window() -> anyhow::Res
items: vec![UserInput::Text {
text: "trigger context window".into(),
}],
final_output_json_schema: None,
})
.await?;
@@ -1635,6 +1655,7 @@ async fn azure_overrides_assign_properties_used_for_responses_url() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1717,6 +1738,7 @@ async fn env_var_overrides_loaded_auth() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1800,6 +1822,7 @@ async fn history_dedupes_streamed_and_final_messages_across_turns() {
codex
.submit(Op::UserInput {
items: vec![UserInput::Text { text: "U1".into() }],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1809,6 +1832,7 @@ async fn history_dedupes_streamed_and_final_messages_across_turns() {
codex
.submit(Op::UserInput {
items: vec![UserInput::Text { text: "U2".into() }],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1818,6 +1842,7 @@ async fn history_dedupes_streamed_and_final_messages_across_turns() {
codex
.submit(Op::UserInput {
items: vec![UserInput::Text { text: "U3".into() }],
final_output_json_schema: None,
})
.await
.unwrap();

View File

@@ -161,6 +161,7 @@ async fn summarize_context_three_requests_and_instructions() {
items: vec![UserInput::Text {
text: "hello world".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -181,6 +182,7 @@ async fn summarize_context_three_requests_and_instructions() {
items: vec![UserInput::Text {
text: THIRD_USER_MSG.into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -580,6 +582,7 @@ async fn multiple_auto_compact_per_task_runs_after_token_limit_hit() {
items: vec![UserInput::Text {
text: user_message.into(),
}],
final_output_json_schema: None,
})
.await
.expect("submit user input");
@@ -1084,6 +1087,7 @@ async fn auto_compact_runs_after_token_limit_hit() {
items: vec![UserInput::Text {
text: FIRST_AUTO_MSG.into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1095,6 +1099,7 @@ async fn auto_compact_runs_after_token_limit_hit() {
items: vec![UserInput::Text {
text: SECOND_AUTO_MSG.into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1106,6 +1111,7 @@ async fn auto_compact_runs_after_token_limit_hit() {
items: vec![UserInput::Text {
text: POST_AUTO_USER_MSG.into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1418,6 +1424,7 @@ async fn auto_compact_persists_rollout_entries() {
items: vec![UserInput::Text {
text: FIRST_AUTO_MSG.into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1428,6 +1435,7 @@ async fn auto_compact_persists_rollout_entries() {
items: vec![UserInput::Text {
text: SECOND_AUTO_MSG.into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1438,6 +1446,7 @@ async fn auto_compact_persists_rollout_entries() {
items: vec![UserInput::Text {
text: POST_AUTO_USER_MSG.into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1529,6 +1538,7 @@ async fn manual_compact_retries_after_context_window_error() {
items: vec![UserInput::Text {
text: "first turn".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1661,6 +1671,7 @@ async fn manual_compact_twice_preserves_latest_user_messages() {
items: vec![UserInput::Text {
text: first_user_message.into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1674,6 +1685,7 @@ async fn manual_compact_twice_preserves_latest_user_messages() {
items: vec![UserInput::Text {
text: second_user_message.into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1687,6 +1699,7 @@ async fn manual_compact_twice_preserves_latest_user_messages() {
items: vec![UserInput::Text {
text: final_user_message.into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1866,6 +1879,7 @@ async fn auto_compact_allows_multiple_attempts_when_interleaved_with_other_turn_
codex
.submit(Op::UserInput {
items: vec![UserInput::Text { text: user.into() }],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1978,6 +1992,7 @@ async fn auto_compact_triggers_after_function_call_over_95_percent_usage() {
items: vec![UserInput::Text {
text: FUNCTION_CALL_LIMIT_MSG.into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1989,6 +2004,7 @@ async fn auto_compact_triggers_after_function_call_over_95_percent_usage() {
items: vec![UserInput::Text {
text: follow_up_user.into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -2103,6 +2119,7 @@ async fn auto_compact_counts_encrypted_reasoning_before_last_user() {
codex
.submit(Op::UserInput {
items: vec![UserInput::Text { text: user.into() }],
final_output_json_schema: None,
})
.await
.unwrap();

View File

@@ -74,6 +74,7 @@ async fn remote_compact_replaces_history_for_followups() -> Result<()> {
items: vec![UserInput::Text {
text: "hello remote compact".into(),
}],
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await;
@@ -86,6 +87,7 @@ async fn remote_compact_replaces_history_for_followups() -> Result<()> {
items: vec![UserInput::Text {
text: "after compact".into(),
}],
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await;
@@ -191,6 +193,7 @@ async fn remote_compact_runs_automatically() -> Result<()> {
items: vec![UserInput::Text {
text: "hello remote compact".into(),
}],
final_output_json_schema: None,
})
.await?;
let message = wait_for_event_match(&codex, |ev| match ev {
@@ -263,6 +266,7 @@ async fn remote_compact_persists_replacement_history_in_rollout() -> Result<()>
items: vec![UserInput::Text {
text: "needs compaction".into(),
}],
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await;

View File

@@ -884,6 +884,7 @@ async fn user_turn(conversation: &Arc<CodexConversation>, text: &str) {
conversation
.submit(Op::UserInput {
items: vec![UserInput::Text { text: text.into() }],
final_output_json_schema: None,
})
.await
.expect("submit user turn");

View File

@@ -74,6 +74,7 @@ async fn fork_conversation_twice_drops_to_first_message() {
items: vec![UserInput::Text {
text: text.to_string(),
}],
final_output_json_schema: None,
})
.await
.unwrap();

View File

@@ -43,6 +43,7 @@ async fn user_message_item_is_emitted() -> anyhow::Result<()> {
items: (vec![UserInput::Text {
text: "please inspect sample.txt".into(),
}]),
final_output_json_schema: None,
})
.await?;
@@ -99,6 +100,7 @@ async fn assistant_message_item_is_emitted() -> anyhow::Result<()> {
items: vec![UserInput::Text {
text: "please summarize results".into(),
}],
final_output_json_schema: None,
})
.await?;
@@ -155,6 +157,7 @@ async fn reasoning_item_is_emitted() -> anyhow::Result<()> {
items: vec![UserInput::Text {
text: "explain your reasoning".into(),
}],
final_output_json_schema: None,
})
.await?;
@@ -213,6 +216,7 @@ async fn web_search_item_is_emitted() -> anyhow::Result<()> {
items: vec![UserInput::Text {
text: "find the weather".into(),
}],
final_output_json_schema: None,
})
.await?;
@@ -265,6 +269,7 @@ async fn agent_message_content_delta_has_item_metadata() -> anyhow::Result<()> {
items: vec![UserInput::Text {
text: "please stream text".into(),
}],
final_output_json_schema: None,
})
.await?;
@@ -330,6 +335,7 @@ async fn reasoning_content_delta_has_item_metadata() -> anyhow::Result<()> {
items: vec![UserInput::Text {
text: "reason through it".into(),
}],
final_output_json_schema: None,
})
.await?;
@@ -387,6 +393,7 @@ async fn reasoning_raw_content_delta_respects_flag() -> anyhow::Result<()> {
items: vec![UserInput::Text {
text: "show raw reasoning".into(),
}],
final_output_json_schema: None,
})
.await?;

View File

@@ -46,6 +46,7 @@ async fn responses_api_emits_api_request_event() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -87,6 +88,7 @@ async fn process_sse_emits_tracing_for_output_item() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -125,6 +127,7 @@ async fn process_sse_emits_failed_event_on_parse_error() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -164,6 +167,7 @@ async fn process_sse_records_failed_event_when_stream_closes_without_completed()
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -223,6 +227,7 @@ async fn process_sse_failed_event_records_response_error_message() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -280,6 +285,7 @@ async fn process_sse_failed_event_logs_parse_error() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -324,6 +330,7 @@ async fn process_sse_failed_event_logs_missing_error() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -377,6 +384,7 @@ async fn process_sse_failed_event_logs_response_completed_parse_error() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -427,6 +435,7 @@ async fn process_sse_emits_completed_telemetry() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -494,6 +503,7 @@ async fn handle_responses_span_records_response_kind_and_tool_name() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -558,6 +568,7 @@ async fn record_responses_sets_span_fields_for_response_events() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -637,6 +648,7 @@ async fn handle_response_item_records_tool_result_for_custom_tool_call() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -704,6 +716,7 @@ async fn handle_response_item_records_tool_result_for_function_call() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -781,6 +794,7 @@ async fn handle_response_item_records_tool_result_for_local_shell_missing_ids()
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -842,6 +856,7 @@ async fn handle_response_item_records_tool_result_for_local_shell_call() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -946,6 +961,7 @@ async fn handle_container_exec_autoapprove_from_config_records_tool_decision() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -994,6 +1010,7 @@ async fn handle_container_exec_user_approved_records_tool_decision() {
items: vec![UserInput::Text {
text: "approved".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1052,6 +1069,7 @@ async fn handle_container_exec_user_approved_for_session_records_tool_decision()
items: vec![UserInput::Text {
text: "persist".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1110,6 +1128,7 @@ async fn handle_sandbox_error_user_approves_retry_records_tool_decision() {
items: vec![UserInput::Text {
text: "retry".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1168,6 +1187,7 @@ async fn handle_container_exec_user_denies_records_tool_decision() {
items: vec![UserInput::Text {
text: "deny".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1226,6 +1246,7 @@ async fn handle_sandbox_error_user_approves_for_session_records_tool_decision()
items: vec![UserInput::Text {
text: "persist".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -1285,6 +1306,7 @@ async fn handle_sandbox_error_user_denies_records_tool_decision() {
items: vec![UserInput::Text {
text: "deny".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();

View File

@@ -102,6 +102,7 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> {
items: vec![UserInput::Text {
text: "hello 1".into(),
}],
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await;
@@ -111,6 +112,7 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> {
items: vec![UserInput::Text {
text: "hello 2".into(),
}],
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await;
@@ -175,6 +177,7 @@ async fn codex_mini_latest_tools() -> anyhow::Result<()> {
items: vec![UserInput::Text {
text: "hello 1".into(),
}],
final_output_json_schema: None,
})
.await?;
@@ -184,6 +187,7 @@ async fn codex_mini_latest_tools() -> anyhow::Result<()> {
items: vec![UserInput::Text {
text: "hello 2".into(),
}],
final_output_json_schema: None,
})
.await?;
@@ -238,6 +242,7 @@ async fn prefixes_context_and_instructions_once_and_consistently_across_requests
items: vec![UserInput::Text {
text: "hello 1".into(),
}],
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await;
@@ -247,6 +252,7 @@ async fn prefixes_context_and_instructions_once_and_consistently_across_requests
items: vec![UserInput::Text {
text: "hello 2".into(),
}],
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await;
@@ -307,6 +313,7 @@ async fn overrides_turn_context_but_keeps_cached_prefix_and_key_constant() -> an
items: vec![UserInput::Text {
text: "hello 1".into(),
}],
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await;
@@ -334,6 +341,7 @@ async fn overrides_turn_context_but_keeps_cached_prefix_and_key_constant() -> an
items: vec![UserInput::Text {
text: "hello 2".into(),
}],
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await;
@@ -412,6 +420,7 @@ async fn override_before_first_turn_emits_environment_context() -> anyhow::Resul
items: vec![UserInput::Text {
text: "first message".into(),
}],
final_output_json_schema: None,
})
.await?;
@@ -504,6 +513,7 @@ async fn per_turn_overrides_keep_cached_prefix_and_key_constant() -> anyhow::Res
items: vec![UserInput::Text {
text: "hello 1".into(),
}],
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await;

View File

@@ -44,6 +44,7 @@ async fn quota_exceeded_emits_single_error_event() -> Result<()> {
items: vec![UserInput::Text {
text: "quota?".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();

View File

@@ -37,6 +37,7 @@ async fn resume_includes_initial_messages_from_rollout_events() -> Result<()> {
items: vec![UserInput::Text {
text: "Record some messages".into(),
}],
final_output_json_schema: None,
})
.await?;
@@ -89,6 +90,7 @@ async fn resume_includes_initial_messages_from_reasoning_events() -> Result<()>
items: vec![UserInput::Text {
text: "Record reasoning messages".into(),
}],
final_output_json_schema: None,
})
.await?;

View File

@@ -665,6 +665,7 @@ async fn review_history_surfaces_in_parent_session() {
items: vec![UserInput::Text {
text: followup.clone(),
}],
final_output_json_schema: None,
})
.await
.unwrap();

View File

@@ -89,6 +89,7 @@ async fn continue_after_stream_error() {
items: vec![UserInput::Text {
text: "first message".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();
@@ -106,6 +107,7 @@ async fn continue_after_stream_error() {
items: vec![UserInput::Text {
text: "follow up".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();

View File

@@ -96,6 +96,7 @@ async fn retries_on_early_close() {
items: vec![UserInput::Text {
text: "hello".into(),
}],
final_output_json_schema: None,
})
.await
.unwrap();

View File

@@ -61,6 +61,7 @@ echo -n "${@: -1}" > $(dirname "${0}")/notify.txt"#,
items: vec![UserInput::Text {
text: "hello world".into(),
}],
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await;