core: add configurable <context_window_guidance> message (#29936)

## Why

This PR adds a configurable `<context_window_guidance>` developer
section immediately after `<context_window>`. Harness integrations need
this section to give the model deployment-specific instructions for
preparing for context-window transitions.

## What changed

- Add an optional `features.token_budget.guidance_message` config with a
1,000-byte runtime cap and generated schema support.
- Render configured guidance as a developer `ContextualUserFragment`
wrapped in `<context_window_guidance>` immediately after
`<context_window>`.
- Omit the section when guidance is unset, empty, or whitespace-only.
- Preserve the resolved value in config locks and classify persisted
guidance as contextual developer content.
- Add integration coverage for rendered content and ordering.
This commit is contained in:
Michael Bolin authored and GitHub committed 2026-06-24 18:03:44 -07:00
1 parent f4e6aa70e5
commit f15df624a6
12 files changed
+145

No files matched your search

+2
View File
@@ -475,10 +475,12 @@ async fn load_config_resolves_token_budget_config() -> std::io::Result<()> {
enabled = true
reminder_threshold_tokens = 16000
reminder_message_template = "Custom reminder: {n_remaining} tokens."
guidance_message = "Preserve important state before compaction."
"#,
TokenBudgetConfig {
reminder_threshold_tokens: Some(16_000),
reminder_message_template: "Custom reminder: {n_remaining} tokens.".to_string(),
guidance_message: Some("Preserve important state before compaction.".to_string()),
},
),
] {
+19
View File
@@ -1089,11 +1089,13 @@ pub(crate) const DEFAULT_TOKEN_BUDGET_REMINDER_MESSAGE_TEMPLATE: &str = concat!(
"Once reset, message items in current context window will be cleared in the new window, but notes and history items will be persistent across windows."
);
const TOKEN_BUDGET_REMINDER_MESSAGE_TEMPLATE_MAX_BYTES: usize = 1000;
const TOKEN_BUDGET_GUIDANCE_MESSAGE_MAX_BYTES: usize = 1000;
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct TokenBudgetConfig {
pub reminder_threshold_tokens: Option<i64>,
pub reminder_message_template: String,
pub guidance_message: Option<String>,
}
impl Default for TokenBudgetConfig {
@@ -1101,6 +1103,7 @@ impl Default for TokenBudgetConfig {
Self {
reminder_threshold_tokens: None,
reminder_message_template: DEFAULT_TOKEN_BUDGET_REMINDER_MESSAGE_TEMPLATE.to_string(),
guidance_message: None,
}
}
}
@@ -2578,9 +2581,25 @@ fn resolve_token_budget_config(
));
}
let guidance_message = token_budget_config
.and_then(|config| config.guidance_message.clone())
.filter(|message| !message.trim().is_empty());
if guidance_message
.as_ref()
.is_some_and(|message| message.len() > TOKEN_BUDGET_GUIDANCE_MESSAGE_MAX_BYTES)
{
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!(
"features.token_budget.guidance_message must not exceed {TOKEN_BUDGET_GUIDANCE_MESSAGE_MAX_BYTES} bytes"
),
));
}
Ok(Some(TokenBudgetConfig {
reminder_threshold_tokens,
reminder_message_template,
guidance_message,
}))
}