fix(goose): exclude preprompt from session title generation (#8793)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt Toohey
2026-04-28 06:21:26 +12:00
committed by GitHub
parent 739f4e88b8
commit 002440b48a
2 changed files with 48 additions and 4 deletions
+43 -3
View File
@@ -731,16 +731,44 @@ pub trait Provider: Send + Sync {
))
}
/// Returns the first 3 user messages as strings for session naming
/// Returns the first 3 user messages as strings for session naming,
/// filtering out assistant-only content (e.g. preprompt blocks).
fn get_initial_user_messages(&self, messages: &Conversation) -> Vec<String> {
messages
.iter()
.filter(|m| m.role == rmcp::model::Role::User)
.take(MSG_COUNT_FOR_SESSION_NAME_GENERATION)
.map(|m| m.as_concat_text())
.map(|m| {
m.content
.iter()
.filter_map(|c| c.filter_for_audience(rmcp::model::Role::User))
.filter_map(|c| c.as_text().map(|s| s.to_string()))
.collect::<Vec<_>>()
.join("\n")
})
.collect()
}
/// Extracts preprompt context (assistant-audience blocks) from the first user message.
/// These are content blocks visible to the assistant but not the user.
fn get_preprompt_context(&self, messages: &Conversation) -> String {
messages
.iter()
.filter(|m| m.role == rmcp::model::Role::User)
.take(1)
.flat_map(|m| m.content.iter())
.filter_map(|c| {
// If this block is NOT visible to the user, it's preprompt/assistant-only content
if c.filter_for_audience(rmcp::model::Role::User).is_none() {
c.as_text().map(|s| s.to_string())
} else {
None
}
})
.collect::<Vec<_>>()
.join("\n")
}
/// Generate a session name/description based on the conversation history
/// Creates a prompt asking for a concise description in 4 words or less.
async fn generate_session_name(
@@ -749,6 +777,7 @@ pub trait Provider: Send + Sync {
messages: &Conversation,
) -> Result<String, ProviderError> {
let context = self.get_initial_user_messages(messages);
let preprompt_context = self.get_preprompt_context(messages);
let system = crate::prompt_template::render_template(
"session_name.md",
&std::collections::HashMap::<String, String>::new(),
@@ -758,8 +787,19 @@ pub trait Provider: Send + Sync {
use super::cli_common::{
SESSION_NAME_BEGIN_MARKER, SESSION_NAME_END_MARKER, SESSION_NAME_SUFFIX,
};
let preprompt_section = if preprompt_context.is_empty() {
String::new()
} else {
format!(
"---BEGIN BACKGROUND CONTEXT (for understanding only, do NOT base the title on this)---\n{}\n---END BACKGROUND CONTEXT---\n\n",
preprompt_context
)
};
let user_text = format!(
"{}\n{}\n{}\n\n{}",
"{}{}\n{}\n{}\n\n{}",
preprompt_section,
SESSION_NAME_BEGIN_MARKER,
context.join("\n"),
SESSION_NAME_END_MARKER,
+5 -1
View File
@@ -56,7 +56,11 @@ pub(crate) fn generate_simple_session_description(
})
.map(|text| {
// Strip the wrapper added by generate_session_name so we get
// the actual user content.
// the actual user content. First strip the optional background context section.
let text = text
.rfind(SESSION_NAME_BEGIN_MARKER)
.and_then(|idx| text.get(idx..))
.unwrap_or(text);
let stripped = text
.strip_prefix(SESSION_NAME_BEGIN_MARKER)
.unwrap_or(text)