mirror of
https://github.com/openai/codex.git
synced 2026-04-27 16:15:09 +00:00
proto
This commit is contained in:
@@ -13,8 +13,10 @@ use tokio_util::task::AbortOnDropHandle;
|
||||
use tracing::trace;
|
||||
use tracing::warn;
|
||||
|
||||
use crate::AuthManager;
|
||||
use crate::codex::Session;
|
||||
use crate::codex::TurnContext;
|
||||
use crate::config::Config;
|
||||
use crate::protocol::EventMsg;
|
||||
use crate::protocol::TaskCompleteEvent;
|
||||
use crate::protocol::TurnAbortReason;
|
||||
@@ -44,6 +46,14 @@ impl SessionTaskContext {
|
||||
pub(crate) fn clone_session(&self) -> Arc<Session> {
|
||||
Arc::clone(&self.session)
|
||||
}
|
||||
|
||||
pub(crate) fn auth_manager(&self) -> Arc<AuthManager> {
|
||||
Arc::clone(&self.session.services.auth_manager)
|
||||
}
|
||||
|
||||
pub(crate) async fn base_config(&self) -> Arc<Config> {
|
||||
self.session.base_config().await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::ReviewOutputEvent;
|
||||
use codex_protocol::protocol::TaskCompleteEvent;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::codex::Session;
|
||||
use crate::codex::TurnContext;
|
||||
use crate::codex::exit_review_mode;
|
||||
use crate::codex::run_task;
|
||||
use crate::codex_delegate::run_codex_conversation;
|
||||
// use crate::config::Config; // no longer needed directly; use session.base_config()
|
||||
use crate::review_format::format_review_findings_block;
|
||||
use crate::state::TaskKind;
|
||||
use codex_protocol::user_input::UserInput;
|
||||
|
||||
@@ -28,11 +35,108 @@ impl SessionTask for ReviewTask {
|
||||
input: Vec<UserInput>,
|
||||
cancellation_token: CancellationToken,
|
||||
) -> Option<String> {
|
||||
let sess = session.clone_session();
|
||||
run_task(sess, ctx, input, TaskKind::Review, cancellation_token).await
|
||||
// let sess = session.clone_session();
|
||||
// run_task(sess, ctx, input, TaskKind::Review, cancellation_token).await
|
||||
|
||||
let config = session.base_config().await.as_ref().clone();
|
||||
let receiver =
|
||||
match run_codex_conversation(config, session.auth_manager(), input, cancellation_token)
|
||||
.await
|
||||
{
|
||||
Ok(r) => r,
|
||||
Err(_) => return None,
|
||||
};
|
||||
while let Ok(event) = receiver.recv().await {
|
||||
session
|
||||
.clone_session()
|
||||
.send_event(ctx.as_ref(), event.clone())
|
||||
.await;
|
||||
if let EventMsg::TaskComplete(TaskCompleteEvent { last_agent_message }) = event {
|
||||
exit_review_mode(
|
||||
session.clone_session(),
|
||||
last_agent_message.as_deref().map(parse_review_output_event),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
Some("".to_string())
|
||||
}
|
||||
|
||||
async fn abort(&self, session: Arc<SessionTaskContext>, ctx: Arc<TurnContext>) {
|
||||
exit_review_mode(session.clone_session(), ctx, None).await;
|
||||
async fn abort(&self, session: Arc<SessionTaskContext>, _ctx: Arc<TurnContext>) {
|
||||
exit_review_mode(session.clone_session(), None).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Emits an ExitedReviewMode Event with optional ReviewOutput,
|
||||
/// and records a developer message with the review output.
|
||||
pub(crate) async fn exit_review_mode(
|
||||
session: Arc<Session>,
|
||||
review_output: Option<ReviewOutputEvent>,
|
||||
) {
|
||||
// ExitedReviewMode event can be emitted by the caller if needed.
|
||||
|
||||
let mut user_message = String::new();
|
||||
if let Some(out) = review_output {
|
||||
let mut findings_str = String::new();
|
||||
let text = out.overall_explanation.trim();
|
||||
if !text.is_empty() {
|
||||
findings_str.push_str(text);
|
||||
}
|
||||
if !out.findings.is_empty() {
|
||||
let block = format_review_findings_block(&out.findings, None);
|
||||
findings_str.push_str(&format!("\n{block}"));
|
||||
}
|
||||
user_message.push_str(&format!(
|
||||
r#"<user_action>
|
||||
<context>User initiated a review task. Here's the full review output from reviewer model. User may select one or more comments to resolve.</context>
|
||||
<action>review</action>
|
||||
<results>
|
||||
{findings_str}
|
||||
</results>
|
||||
</user_action>
|
||||
"#));
|
||||
} else {
|
||||
user_message.push_str(r#"<user_action>
|
||||
<context>User initiated a review task, but was interrupted. If user asks about this, tell them to re-initiate a review with `/review` and wait for it to complete.</context>
|
||||
<action>review</action>
|
||||
<results>
|
||||
None.
|
||||
</results>
|
||||
</user_action>
|
||||
"#);
|
||||
}
|
||||
|
||||
session
|
||||
.record_conversation_items(&[ResponseItem::Message {
|
||||
id: None,
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentItem::InputText { text: user_message }],
|
||||
}])
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Parse the review output; when not valid JSON, build a structured
|
||||
/// fallback that carries the plain text as the overall explanation.
|
||||
///
|
||||
/// Returns: a ReviewOutputEvent parsed from JSON or a fallback populated from text.
|
||||
fn parse_review_output_event(text: &str) -> ReviewOutputEvent {
|
||||
// Try direct parse first
|
||||
if let Ok(ev) = serde_json::from_str::<ReviewOutputEvent>(text) {
|
||||
return ev;
|
||||
}
|
||||
// If wrapped in markdown fences or extra prose, attempt to extract the first JSON object
|
||||
if let (Some(start), Some(end)) = (text.find('{'), text.rfind('}'))
|
||||
&& start < end
|
||||
&& let Some(slice) = text.get(start..=end)
|
||||
&& let Ok(ev) = serde_json::from_str::<ReviewOutputEvent>(slice)
|
||||
{
|
||||
return ev;
|
||||
}
|
||||
// Not JSON – return a structured ReviewOutputEvent that carries
|
||||
// the plain text as the overall explanation.
|
||||
ReviewOutputEvent {
|
||||
overall_explanation: text.to_string(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user