mirror of
https://github.com/openai/codex.git
synced 2026-11-10 21:00:47 +00:00
Include preceding assistant context in Guardian sender reviews (#49951)
## Why
Short sender replies such as `👍` need preceding assistant context to be understood during a delegation review. User messages alone omit that context.
## What changed
- Capture preceding assistant context alongside up to three recent sender user messages at delivery, for Guardian only. Label assistant messages as untrusted context rather than authorization or verified questions.
- Share the existing 900-byte budget per exchange, prioritize user evidence, and report unavailable assistant context.
- Recognize `cloud_threads.send_message` deliveries and accept both compact and indented delegation envelopes.
## Testing
Expand integration coverage for app, TUI, and cloud-thread deliveries, contextual replies, and budget-driven omissions. Assert that sender user and assistant context stays out of the delegated agent's prompt.
GitOrigin-RevId: 4eca369cbb30241635273f729dec81b78ea57faf
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
//! Captures genuine sender instructions for host-delivered task messages.
|
||||
//! Captures genuine sender instructions and assistant context for host-delivered task messages.
|
||||
//! Only turn-input admission calls this, before queueing; ordinary tool results and
|
||||
//! quoted delegation text cannot establish sender provenance. Lookup stays in this host.
|
||||
|
||||
use crate::context::ContextualUserFragment;
|
||||
use crate::context::GuardianSenderExchange;
|
||||
use crate::context::GuardianSenderMessages;
|
||||
use codex_guardian_context::GuardianRootMessage;
|
||||
use codex_history::RetainedContextEntry;
|
||||
use codex_history::RetainedContextOrder;
|
||||
use codex_history::SenderUserMessages;
|
||||
@@ -30,17 +32,25 @@ impl LocalAgentRuntime {
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
if !matches!(namespace.as_str(), "codex_app" | "codex_tui")
|
||||
|| name != "send_message_to_thread"
|
||||
{
|
||||
if !matches!(
|
||||
(namespace.as_str(), name.as_str()),
|
||||
("codex_app" | "codex_tui", "send_message_to_thread")
|
||||
| ("cloud_threads", "send_message")
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
// Recognized deliveries always get their own snapshot, even without usable provenance.
|
||||
let source_thread_id = output.body.to_text().and_then(|text| {
|
||||
let (source, input) = text
|
||||
.strip_prefix("<codex_delegation>\n <source_thread_id>")?
|
||||
.split_once("</source_thread_id>\n <input>")?;
|
||||
input.strip_suffix("</input>\n</codex_delegation>")?;
|
||||
.strip_prefix("<codex_delegation>")?
|
||||
.strip_suffix("</codex_delegation>")?
|
||||
.trim()
|
||||
.strip_prefix("<source_thread_id>")?
|
||||
.split_once("</source_thread_id>")?;
|
||||
input
|
||||
.trim()
|
||||
.strip_prefix("<input>")?
|
||||
.strip_suffix("</input>")?;
|
||||
ThreadId::from_string(source)
|
||||
.ok()
|
||||
.filter(|source| *source != receiver_thread_id)
|
||||
@@ -56,19 +66,36 @@ impl LocalAgentRuntime {
|
||||
{
|
||||
let history = sender.conversation_history_snapshot().await;
|
||||
if let Some(context) = history.retained_context() {
|
||||
fragment.messages = context
|
||||
.ordered_entries()
|
||||
.filter_map(|(order, entry)| match (order, entry) {
|
||||
let mut exchanges = Vec::new();
|
||||
let mut assistant = None;
|
||||
for (order, entry) in context.ordered_entries() {
|
||||
match (order, entry) {
|
||||
(
|
||||
RetainedContextOrder::Local(_),
|
||||
RetainedContextEntry::UserMessage(message),
|
||||
) => Some(message.complete.then(|| message.text.clone())),
|
||||
) => exchanges.push((message, assistant.take())),
|
||||
(
|
||||
RetainedContextOrder::Local(_),
|
||||
RetainedContextEntry::AssistantMessage(message),
|
||||
) => assistant = Some(message),
|
||||
(RetainedContextOrder::Inherited(_), _)
|
||||
| (_, RetainedContextEntry::AssistantMessage(_))
|
||||
| (_, RetainedContextEntry::VerifiedAnswer(_)) => None,
|
||||
})
|
||||
| (_, RetainedContextEntry::VerifiedAnswer(_)) => {}
|
||||
}
|
||||
}
|
||||
fragment.messages = exchanges
|
||||
.into_iter()
|
||||
.rev()
|
||||
.take(/*n*/ 3)
|
||||
.map(|(user, assistant)| GuardianSenderExchange {
|
||||
user: user.complete.then(|| user.text.clone()),
|
||||
assistant: assistant.map(|message| {
|
||||
if message.complete {
|
||||
GuardianRootMessage::Assistant(message.text.clone())
|
||||
} else {
|
||||
GuardianRootMessage::IncompleteAssistantContext
|
||||
}
|
||||
}),
|
||||
})
|
||||
.collect();
|
||||
fragment.messages.reverse();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user