Route extension hints into context-window metadata (#40533)

## What changed

- Add a `ContextWindow` prompt slot for thread-scoped extension contributions.
- Include fragments in that slot in the token-budget context-window message.
- Keep the MCP `notes/thread_hint` bridge when the native history-notes extension is disabled, without falling back to it after a native request failure.

GitOrigin-RevId: befcb7cc8570b846094b2b542a112b186b39a30a
This commit is contained in:
pmccrary-oai authored and copyberry committed 2026-08-25 03:38:24 +00:00
1 parent 9c9675d3d0
commit 3a469a297d
4 files changed
+57 -36

No files matched your search

+49 -30
View File
@@ -65,6 +65,7 @@ use codex_execpolicy::prefix_rule_migration;
use codex_extension_api::ConversationHistorySnapshot;
use codex_extension_api::ExtensionDataInit;
use codex_extension_api::LoadedUserInstructions;
use codex_extension_api::PromptSlot;
use codex_extension_api::TurnContextContributionInput;
use codex_features::FEATURES;
use codex_features::Feature;
@@ -3590,6 +3591,7 @@ impl Session {
let mut developer_sections = Vec::<RenderedFragment>::with_capacity(8);
let mut contextual_user_sections = Vec::<RenderedFragment>::with_capacity(2);
let mut separate_developer_sections = Vec::<RenderedFragment>::new();
let mut context_window_hints = Vec::new();
let (session_source, auto_compact_window_ids) = {
let state = self.state.lock().await;
(
@@ -3649,7 +3651,14 @@ impl Session {
)
.await
{
developer_sections.push(fragment.into());
match fragment.slot() {
PromptSlot::ContextWindow => {
context_window_hints.push(fragment.text().to_string());
}
PromptSlot::DeveloperPolicy | PromptSlot::DeveloperCapabilities => {
developer_sections.push(fragment.into());
}
}
}
}
for contributor in &context_contributors {
@@ -3671,34 +3680,44 @@ impl Session {
if turn_context.config.features.enabled(Feature::TokenBudget)
&& turn_context.model_context_window().is_some()
{
let mcp_result = self
.services
.mcp_runtime
.latest_call_tool(
"notes",
"thread_hint",
/*environment_id*/ None,
/*arguments*/ None,
Some(serde_json::json!({
"threadId": self.thread_id().to_string(),
})),
/*requested_timeout*/ None,
/*wait_for_server*/ true,
)
.await
.ok()
.and_then(|result| {
let text = result
.content
.iter()
.filter_map(|content| {
content.get("text").and_then(serde_json::Value::as_str)
})
.filter(|text| !text.is_empty())
.collect::<Vec<_>>()
.join("\n");
(!text.is_empty()).then_some(text)
});
// Keep the legacy bridge hint when native Notes is disabled. A failed
// native request must not fall back to the bridge.
if !turn_context
.config
.token_budget
.as_ref()
.is_some_and(|config| config.use_history_notes_extension)
&& let Some(mcp_result) = self
.services
.mcp_runtime
.latest_call_tool(
"notes",
"thread_hint",
/*environment_id*/ None,
/*arguments*/ None,
Some(serde_json::json!({
"threadId": self.thread_id().to_string(),
})),
/*requested_timeout*/ None,
/*wait_for_server*/ true,
)
.await
.ok()
.and_then(|result| {
let text = result
.content
.iter()
.filter_map(|content| {
content.get("text").and_then(serde_json::Value::as_str)
})
.filter(|text| !text.is_empty())
.collect::<Vec<_>>()
.join("\n");
(!text.is_empty()).then_some(text)
})
{
context_window_hints.push(mcp_result);
}
separate_developer_sections.push(
crate::context::TokenBudgetContext::new(
session_source
@@ -3707,7 +3726,7 @@ impl Session {
auto_compact_window_ids.first_window_id,
auto_compact_window_ids.previous_window_id,
auto_compact_window_ids.window_id,
mcp_result,
(!context_window_hints.is_empty()).then(|| context_window_hints.join("\n")),
)
.render_fragment(),
);