mirror of
https://github.com/openai/codex.git
synced 2026-10-19 17:28:46 +00:00
## Why Forked subagents can inherit a parent's compacted history, but their context-window metadata needs to identify the child and start a distinct window lineage. ## What changed - Always identify context windows by agent name and remove the `features.token_budget.mode` setting. - Persist the agent path in world state and emit context-window metadata as a standalone developer message when the identity changes. - Reset inherited compaction metadata to the forked subagent's initial context window and restore that window in session state. ## Testing Extend the full-history subagent test to verify parent and child world-state snapshots, agent identities, distinct window IDs, and the reset child checkpoint. GitOrigin-RevId: 6584201e57b3d2fba0ce626b89a8b3b0b3c752db
114 lines
3.6 KiB
Rust
114 lines
3.6 KiB
Rust
use super::session::Session;
|
|
use super::turn_context::TurnContext;
|
|
use crate::config::Config;
|
|
use crate::config::TokenBudgetConfig;
|
|
use crate::context::ContextualUserFragment;
|
|
use codex_features::Feature;
|
|
use codex_protocol::openai_models::ModelInfo;
|
|
|
|
pub(super) fn has_explicit_settings(config: &Config) -> bool {
|
|
config
|
|
.config_layer_stack
|
|
.effective_config()
|
|
.get("features")
|
|
.and_then(|features| features.get("token_budget"))
|
|
.and_then(|token_budget| token_budget.as_table())
|
|
.is_some_and(|settings| settings.keys().any(|key| key != "enabled"))
|
|
|| config
|
|
.token_budget
|
|
.as_ref()
|
|
.is_some_and(|token_budget| token_budget != &TokenBudgetConfig::default())
|
|
}
|
|
|
|
pub(super) fn apply_model_defaults(config: &mut Config, model_info: &ModelInfo) {
|
|
if !config.features.enabled(Feature::TokenBudget) || has_explicit_settings(config) {
|
|
return;
|
|
}
|
|
|
|
let Some(model_defaults) = model_info
|
|
.model_messages
|
|
.as_ref()
|
|
.and_then(|messages| messages.token_budget.as_ref())
|
|
else {
|
|
return;
|
|
};
|
|
|
|
let token_budget = TokenBudgetConfig {
|
|
reminder_threshold_tokens: Some(model_defaults.reminder_threshold_tokens),
|
|
reminder_message_template: model_defaults.reminder_message_template.clone(),
|
|
guidance_message: Some(model_defaults.guidance_message.clone()),
|
|
auto_compact_fallback_prompt: Some(model_defaults.auto_compact_fallback_prompt.clone()),
|
|
auto_compact_fallback_buffer_tokens: Some(
|
|
model_defaults.auto_compact_fallback_buffer_tokens,
|
|
),
|
|
};
|
|
|
|
if let Err(error) = token_budget.validate() {
|
|
tracing::warn!(
|
|
model = %model_info.slug,
|
|
%error,
|
|
"ignoring invalid model-owned token-budget defaults"
|
|
);
|
|
return;
|
|
}
|
|
|
|
config.token_budget = Some(token_budget);
|
|
}
|
|
|
|
pub(super) async fn maybe_record(
|
|
sess: &Session,
|
|
turn_context: &TurnContext,
|
|
base_window_tokens_remaining: Option<i64>,
|
|
allow_auto_compact_fallback: bool,
|
|
) {
|
|
if !turn_context.config.features.enabled(Feature::TokenBudget) {
|
|
return;
|
|
}
|
|
let Some(base_window_tokens_remaining) = base_window_tokens_remaining else {
|
|
return;
|
|
};
|
|
|
|
let Some(config) = turn_context.config.token_budget.as_ref() else {
|
|
return;
|
|
};
|
|
|
|
if config
|
|
.reminder_threshold_tokens
|
|
.is_some_and(|threshold| base_window_tokens_remaining <= threshold)
|
|
{
|
|
let reminder_due = {
|
|
let mut state = sess.state.lock().await;
|
|
state.claim_token_budget_reminder()
|
|
};
|
|
if reminder_due {
|
|
let response_item =
|
|
ContextualUserFragment::into(crate::context::TokenBudgetReminder::new(
|
|
&config.reminder_message_template,
|
|
base_window_tokens_remaining,
|
|
));
|
|
sess.record_conversation_items(turn_context, std::slice::from_ref(&response_item))
|
|
.await;
|
|
}
|
|
}
|
|
|
|
if !allow_auto_compact_fallback || base_window_tokens_remaining != 0 {
|
|
return;
|
|
}
|
|
let Some(prompt) = config.auto_compact_fallback_prompt.as_deref() else {
|
|
return;
|
|
};
|
|
|
|
let fallback_due = {
|
|
let mut state = sess.state.lock().await;
|
|
state.claim_auto_compact_fallback()
|
|
};
|
|
if !fallback_due {
|
|
return;
|
|
}
|
|
|
|
let response_item =
|
|
ContextualUserFragment::into(crate::context::AutoCompactFallbackPrompt::new(prompt));
|
|
sess.record_conversation_items(turn_context, std::slice::from_ref(&response_item))
|
|
.await;
|
|
}
|