mirror of
https://github.com/openai/codex.git
synced 2026-10-21 04:48:12 +00:00
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:
@@ -15,7 +15,7 @@ pub(crate) struct TokenBudgetContext {
|
||||
first_window_id: Uuid,
|
||||
previous_window_id: Option<Uuid>,
|
||||
window_id: Uuid,
|
||||
mcp_result: Option<String>,
|
||||
thread_hint: Option<String>,
|
||||
}
|
||||
|
||||
impl TokenBudgetContext {
|
||||
@@ -24,14 +24,14 @@ impl TokenBudgetContext {
|
||||
first_window_id: Uuid,
|
||||
previous_window_id: Option<Uuid>,
|
||||
window_id: Uuid,
|
||||
mcp_result: Option<String>,
|
||||
thread_hint: Option<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
agent_path,
|
||||
first_window_id,
|
||||
previous_window_id,
|
||||
window_id,
|
||||
mcp_result,
|
||||
thread_hint,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,8 +68,8 @@ impl ContextualUserFragment for TokenBudgetContext {
|
||||
if let Some(previous_window_id) = self.previous_window_id {
|
||||
lines.push(format!("Previous context window id: {previous_window_id}"));
|
||||
}
|
||||
if let Some(mcp_result) = &self.mcp_result {
|
||||
lines.push(mcp_result.clone());
|
||||
if let Some(thread_hint) = &self.thread_hint {
|
||||
lines.push(thread_hint.clone());
|
||||
}
|
||||
format!("\n{}\n", lines.join("\n"))
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
|
||||
@@ -116,7 +116,7 @@ impl Session {
|
||||
window_ids.first_window_id,
|
||||
window_ids.previous_window_id,
|
||||
window_ids.window_id,
|
||||
/*mcp_result*/ None,
|
||||
/*thread_hint*/ None,
|
||||
));
|
||||
if let Some(guidance) = turn_context
|
||||
.config
|
||||
|
||||
@@ -8,6 +8,8 @@ use codex_protocol::models::ContentItemKind;
|
||||
pub enum PromptSlot {
|
||||
DeveloperPolicy,
|
||||
DeveloperCapabilities,
|
||||
/// Text inside the context-window message, supplied by `contribute_thread_context`.
|
||||
ContextWindow,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
|
||||
Reference in New Issue
Block a user