Unify turn input submission and routing (#38275)

## What changed

- Add `TurnInputRequest` and typed submission results for atomically starting a turn, steering the active turn, or declining input with a specific reason.
- Expose `start_or_steer_turn`, `start_turn_if_idle`, and `steer_turn` on `CodexThread`, and migrate Core consumers to these APIs.
- Make app-server `turn/start` steer an active regular turn and return that turn's ID. Reject incompatible output schemas and non-steerable turns without applying settings or enqueueing input.

## Testing

- Cover concurrent start-or-steer submissions, accepted and rejected settings updates, output-schema compatibility, idle-start rejection, and app-server steering.

GitOrigin-RevId: dd9b5528d76ec650c019e97af420bc13190ea86a
This commit is contained in:
Owen Lin
2026-08-12 23:44:58 +00:00
committed by copyberry
parent 4b07886d59
commit cbb7e82a8b
137 changed files with 5217 additions and 6812 deletions

View File

@@ -36,7 +36,6 @@ use codex_core_api::NewThread;
use codex_core_api::Notice;
use codex_core_api::OAuthCredentialsStoreMode;
use codex_core_api::OPENAI_PROVIDER_ID;
use codex_core_api::Op;
use codex_core_api::OtelConfig;
use codex_core_api::PermissionProfile;
use codex_core_api::Permissions;
@@ -46,6 +45,7 @@ use codex_core_api::RealtimeConfig;
use codex_core_api::SessionPickerViewMode;
use codex_core_api::SessionSource;
use codex_core_api::SqliteConfig;
use codex_core_api::StartIfIdleSubmission;
use codex_core_api::StartThreadOptions;
use codex_core_api::TerminalResizeReflowConfig;
use codex_core_api::ThreadManager;
@@ -54,6 +54,7 @@ use codex_core_api::ToolSuggestConfig;
use codex_core_api::TuiKeymap;
use codex_core_api::TuiNotificationSettings;
use codex_core_api::TuiPetAnchor;
use codex_core_api::TurnInputRequest;
use codex_core_api::UriBasedFileOpener;
use codex_core_api::UserInput;
use codex_core_api::WebSearchMode;
@@ -322,19 +323,16 @@ fn new_config(model: Option<String>, arg0_paths: Arg0DispatchPaths) -> anyhow::R
}
async fn run_turn(thread: &CodexThread, thread_id: &str, prompt: String) -> anyhow::Result<()> {
thread
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: prompt,
text_elements: Vec::new(),
}],
final_output_json_schema: None,
responsesapi_client_metadata: None,
additional_context: Default::default(),
thread_settings: Default::default(),
})
let submission = thread
.start_turn_if_idle(TurnInputRequest::user_input(vec![UserInput::Text {
text: prompt,
text_elements: Vec::new(),
}]))
.await
.context("submit user input")?;
if let StartIfIdleSubmission::NotSubmitted { reason } = submission {
bail!("turn input was not submitted: {reason:?}");
}
let mut current_turn_id: Option<String> = None;
let mut stdout = std::io::stdout().lock();