mirror of
https://github.com/openai/codex.git
synced 2026-09-17 12:23:33 +00:00
## 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
101 lines
3.0 KiB
Rust
101 lines
3.0 KiB
Rust
use codex_core::CodexThread;
|
|
use codex_core::NewThread;
|
|
use codex_core::StartIfIdleSubmission;
|
|
use codex_core::StartThreadOptions;
|
|
use codex_core::ThreadManager;
|
|
use codex_core::TurnInputRequest;
|
|
use codex_core::config::Config;
|
|
use codex_protocol::ThreadId;
|
|
use codex_protocol::error::CodexErr;
|
|
use codex_protocol::error::Result as CodexResult;
|
|
use codex_protocol::protocol::W3cTraceContext;
|
|
use codex_protocol::user_input::UserInput;
|
|
use std::sync::Arc;
|
|
use std::sync::Weak;
|
|
|
|
/// A fully resolved agent invocation.
|
|
///
|
|
/// Agent discovery owns rendering `prompt`, including any selected skill
|
|
/// references. The runtime only starts that prompt in isolated forked context.
|
|
pub struct AgentInvocation {
|
|
pub config: Config,
|
|
pub prompt: String,
|
|
pub parent_trace: Option<W3cTraceContext>,
|
|
}
|
|
|
|
/// A spawned agent whose initial turn has been submitted.
|
|
pub struct AgentRun {
|
|
pub thread_id: ThreadId,
|
|
pub turn_id: String,
|
|
pub thread: Arc<CodexThread>,
|
|
}
|
|
|
|
/// Runs resolved agents in threads forked by the owning [`ThreadManager`].
|
|
#[derive(Clone)]
|
|
pub struct AgentRunner {
|
|
thread_manager: Weak<ThreadManager>,
|
|
}
|
|
|
|
impl AgentRunner {
|
|
pub fn new(thread_manager: Weak<ThreadManager>) -> Self {
|
|
Self { thread_manager }
|
|
}
|
|
|
|
/// Starts a resolved agent in a fork of `parent_thread_id`.
|
|
pub async fn start(
|
|
&self,
|
|
parent_thread_id: ThreadId,
|
|
invocation: AgentInvocation,
|
|
) -> CodexResult<AgentRun> {
|
|
let AgentInvocation {
|
|
config,
|
|
prompt,
|
|
parent_trace,
|
|
} = invocation;
|
|
if prompt.trim().is_empty() {
|
|
return Err(CodexErr::InvalidRequest(
|
|
"agent prompt must not be empty".to_string(),
|
|
));
|
|
}
|
|
|
|
let thread_manager = self
|
|
.thread_manager
|
|
.upgrade()
|
|
.ok_or_else(|| CodexErr::UnsupportedOperation("thread manager dropped".to_string()))?;
|
|
let NewThread {
|
|
thread_id, thread, ..
|
|
} = thread_manager
|
|
.spawn_subagent(
|
|
parent_thread_id,
|
|
StartThreadOptions {
|
|
parent_trace: parent_trace.clone(),
|
|
..StartThreadOptions::new(config)
|
|
},
|
|
)
|
|
.await?;
|
|
let turn_id = match thread
|
|
.start_turn_if_idle(
|
|
TurnInputRequest::user_input(vec![UserInput::Text {
|
|
text: prompt,
|
|
text_elements: Vec::new(),
|
|
}])
|
|
.with_trace(parent_trace),
|
|
)
|
|
.await?
|
|
{
|
|
StartIfIdleSubmission::Started { turn_id } => turn_id,
|
|
StartIfIdleSubmission::NotSubmitted { reason } => {
|
|
return Err(CodexErr::InvalidRequest(format!(
|
|
"agent prompt was not submitted: {reason:?}"
|
|
)));
|
|
}
|
|
};
|
|
|
|
Ok(AgentRun {
|
|
thread_id,
|
|
turn_id,
|
|
thread,
|
|
})
|
|
}
|
|
}
|