Move guardian prompt composition into the reviewer crate (#57488)

## What changed

Export `GuardianPromptItems` and `compose_guardian_prompt` from `codex-guardian-reviewer`, and delegate prompt composition from core to the new helper. Core continues to collect context sections and choose the history version.

Preserve full and delta transcript selection, empty-transcript placeholders, cursor accounting, and Node REPL evidence sequencing.

GitOrigin-RevId: e3e2b2a87a42709ddb5441f47cbb7461b0130579
This commit is contained in:
jif
2026-10-27 15:39:10 +00:00
committed by copyberry
parent dddc3c0a79
commit 3b600d0566
3 changed files with 67 additions and 42 deletions
+9 -42
View File
@@ -1,9 +1,8 @@
use codex_extension_api::ConversationHistorySnapshot;
use codex_features::Feature;
#[cfg(test)]
use codex_guardian_context::Budgeted;
use codex_guardian_context::CollectedContext;
use codex_guardian_context::ComposedContext;
use codex_guardian_context::ContextPresentation;
use codex_guardian_context::ContextProfile;
#[cfg(test)]
use codex_guardian_context::ConversationTranscriptEntry;
@@ -13,10 +12,11 @@ use codex_guardian_context::PlannedActionKind;
use codex_guardian_context::SectionError;
use codex_guardian_context::SectionHistory;
use codex_guardian_context::SectionInput;
#[cfg(test)]
pub(crate) use codex_guardian_context::TranscriptCursor as GuardianTranscriptCursor;
pub(crate) use codex_guardian_context::TranscriptMode as GuardianPromptMode;
use codex_guardian_context::TranscriptSelection;
use codex_guardian_context::default_registry;
use codex_guardian_reviewer::GuardianPromptItems;
use codex_protocol::models::ResponseItem;
use crate::context::GuardianPermissionContext;
@@ -45,12 +45,6 @@ pub(crate) enum GuardianTranscriptHistory<'a> {
LoadedParentCheckpoint(&'a dyn ConversationHistorySnapshot),
}
pub(crate) struct GuardianPromptItems {
pub(crate) context: ComposedContext,
pub(crate) transcript_cursor: GuardianTranscriptCursor,
pub(crate) node_repl_evidence_sequence: u64,
}
/// Builds the guardian user content items from:
/// - a compact transcript for authorization and local context
/// - the exact action JSON being proposed for approval
@@ -210,45 +204,18 @@ pub(crate) async fn build_guardian_prompt_items_with_parent_turn(
permissions.as_ref(),
node_repl_context.as_ref(),
)?;
let (selection, transcript_cursor) = mode.select(
sections.transcript_entries(),
codex_guardian_reviewer::compose_guardian_prompt(
sections,
&profile,
mode,
if use_parent_checkpoint {
history.history_version()
} else {
history.review_history_version()
},
);
let session_id = session.thread_id.to_string();
let (transcript_entries, offset, placeholder, presentation) = match selection {
TranscriptSelection::Full(entries) => (
entries,
0,
"<no retained transcript entries>",
ContextPresentation::SyncFull {
session_id: &session_id,
},
),
TranscriptSelection::Delta { entries, offset } => (
entries,
offset,
"<no retained transcript delta entries>",
ContextPresentation::SyncDelta {
session_id: &session_id,
},
),
};
let mut transcript = profile.prepare_transcript(transcript_entries, offset);
if transcript_entries.is_empty() {
transcript.items.push(Budgeted::required(
codex_guardian_context::TranscriptContent::Text(placeholder.to_owned()),
));
}
let context = sections.compose(presentation, transcript)?;
Ok(GuardianPromptItems {
context,
transcript_cursor,
&session.thread_id.to_string(),
node_repl_evidence_sequence,
})
)
}
/// Exercises the sync profile through the host's existing transcript tests.
@@ -19,6 +19,7 @@ mod metrics;
mod model;
mod outcome;
mod pool;
mod prompt;
mod reporting;
mod retry;
mod review;
@@ -58,6 +59,8 @@ pub use pool::ReviewerRequest;
pub use pool::ReviewerSession;
pub use pool::ReviewerTasks;
pub use pool::SessionDisposition;
pub use prompt::GuardianPromptItems;
pub use prompt::compose_guardian_prompt;
pub use review::ReviewHost;
@@ -0,0 +1,55 @@
//! Composes synchronous review prompts from the host's captured context sections.
//! Full/delta selection, empty-transcript text, and cursor accounting preserve the existing layout.
use codex_guardian_context::Budgeted;
use codex_guardian_context::CollectedContext;
use codex_guardian_context::ComposedContext;
use codex_guardian_context::ContextPresentation;
use codex_guardian_context::ContextProfile;
use codex_guardian_context::TranscriptCursor as GuardianTranscriptCursor;
use codex_guardian_context::TranscriptMode as GuardianPromptMode;
use codex_guardian_context::TranscriptSelection;
pub struct GuardianPromptItems {
pub context: ComposedContext,
pub transcript_cursor: GuardianTranscriptCursor,
pub node_repl_evidence_sequence: u64,
}
pub fn compose_guardian_prompt(
sections: CollectedContext,
profile: &ContextProfile,
mode: GuardianPromptMode,
history_version: u64,
session_id: &str,
node_repl_evidence_sequence: u64,
) -> anyhow::Result<GuardianPromptItems> {
let (selection, transcript_cursor) =
mode.select(sections.transcript_entries(), history_version);
let (transcript_entries, offset, placeholder, presentation) = match selection {
TranscriptSelection::Full(entries) => (
entries,
0,
"<no retained transcript entries>",
ContextPresentation::SyncFull { session_id },
),
TranscriptSelection::Delta { entries, offset } => (
entries,
offset,
"<no retained transcript delta entries>",
ContextPresentation::SyncDelta { session_id },
),
};
let mut transcript = profile.prepare_transcript(transcript_entries, offset);
if transcript_entries.is_empty() {
transcript.items.push(Budgeted::required(
codex_guardian_context::TranscriptContent::Text(placeholder.to_owned()),
));
}
let context = sections.compose(presentation, transcript)?;
Ok(GuardianPromptItems {
context,
transcript_cursor,
node_repl_evidence_sequence,
})
}