Cache-safe request assembly: append-only turn context and declared cache semantics (#11022)
This commit is contained in:
@@ -224,7 +224,7 @@ impl AdversaryInspector {
|
||||
messages
|
||||
.iter()
|
||||
.rev()
|
||||
.filter(|m| m.role == rmcp::model::Role::User)
|
||||
.filter(|m| m.role == rmcp::model::Role::User && !m.is_turn_context())
|
||||
.filter_map(|m| {
|
||||
let text: String = m
|
||||
.content
|
||||
@@ -250,7 +250,7 @@ impl AdversaryInspector {
|
||||
|
||||
fn extract_original_task(messages: &[Message]) -> String {
|
||||
for msg in messages {
|
||||
if msg.role == rmcp::model::Role::User {
|
||||
if msg.role == rmcp::model::Role::User && !msg.is_turn_context() {
|
||||
let text: String = msg
|
||||
.content
|
||||
.iter()
|
||||
@@ -720,4 +720,39 @@ mod tests {
|
||||
.unwrap();
|
||||
assert!(results.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_context_extraction_skips_turn_context_events() {
|
||||
use crate::conversation::message::MessageMetadata;
|
||||
|
||||
let turn_context = |text: &str| {
|
||||
Message::user()
|
||||
.with_text(text)
|
||||
.with_metadata(MessageMetadata::agent_only().with_turn_context())
|
||||
};
|
||||
let messages = vec![
|
||||
turn_context("turn context before any prompt"),
|
||||
Message::user().with_text("never delete files outside the repo"),
|
||||
turn_context("turn context for turn one"),
|
||||
Message::assistant().with_text("understood"),
|
||||
Message::user().with_text("first task"),
|
||||
turn_context("turn context for turn two"),
|
||||
Message::assistant().with_text("done"),
|
||||
Message::user().with_text("second task"),
|
||||
turn_context("turn context for turn three"),
|
||||
];
|
||||
|
||||
let recent = AdversaryInspector::extract_recent_user_messages(&messages, 4);
|
||||
assert_eq!(
|
||||
recent,
|
||||
vec![
|
||||
"never delete files outside the repo",
|
||||
"first task",
|
||||
"second task"
|
||||
]
|
||||
);
|
||||
|
||||
let original = AdversaryInspector::extract_original_task(&messages);
|
||||
assert_eq!(original, "never delete files outside the repo");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,6 +354,7 @@ impl PromptInjectionScanner {
|
||||
.rev()
|
||||
.filter(|m| {
|
||||
crate::conversation::effective_role(m) == crate::conversation::EffectiveRole::User
|
||||
&& !m.is_turn_context()
|
||||
})
|
||||
.take(limit)
|
||||
.map(|m| {
|
||||
@@ -460,4 +461,33 @@ mod tests {
|
||||
|
||||
assert!(result.is_malicious);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_user_messages_skips_turn_context_events() {
|
||||
use crate::conversation::message::MessageMetadata;
|
||||
|
||||
let scanner = PromptInjectionScanner::new();
|
||||
let turn_context = |text: &str| {
|
||||
Message::user()
|
||||
.with_text(text)
|
||||
.with_metadata(MessageMetadata::agent_only().with_turn_context())
|
||||
};
|
||||
let messages = vec![
|
||||
Message::user().with_text("oldest prompt"),
|
||||
turn_context("turn context one"),
|
||||
Message::assistant().with_text("ok"),
|
||||
Message::user().with_text("middle prompt"),
|
||||
turn_context("turn context two"),
|
||||
Message::assistant().with_text("done"),
|
||||
Message::user().with_text("newest prompt"),
|
||||
turn_context("turn context three"),
|
||||
];
|
||||
|
||||
let extracted = scanner.extract_user_messages(&messages, 3);
|
||||
|
||||
assert_eq!(
|
||||
extracted,
|
||||
vec!["newest prompt", "middle prompt", "oldest prompt"]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user