Make the update_plan tool opt-in (#41744)

## What changed

- Default `tools.update_plan.enabled` to `false`; users can explicitly enable it to expose `update_plan`.
- Remove bundled `update_plan` guidance from model, collaboration-mode, multi-agent, compaction, prewarm, and goal-continuation prompts when the tool is disabled.
- Preserve custom base instructions, model catalog instructions, collaboration policies, and user goal text even when they mention planning or `update_plan`.

## Testing

- Cover default and explicitly enabled tool registration, prompt consistency across request paths, and preservation of custom instructions.

GitOrigin-RevId: a53964e6e72f98e2557dd3090fdb7caccb956527
This commit is contained in:
rka-oai authored and copyberry committed 2026-08-31 00:53:30 +00:00
1 parent b7cd519c76
commit a9519cbcdd
31 files changed
+500 -71

No files matched your search

+4 -1
View File
@@ -448,7 +448,10 @@ impl GoalRuntimeHandle {
.get::<TurnStartOptions>()
.map(|options| options.as_ref().clone())
.unwrap_or_default();
let item = continuation_steering_item(&protocol_goal_from_state(goal));
let item = continuation_steering_item(
&protocol_goal_from_state(goal),
thread.config().await.update_plan_enabled,
);
match thread
.start_turn_if_idle(
+21 -5
View File
@@ -1,6 +1,7 @@
use codex_core::context::ContextualUserFragment;
use codex_core::context::InternalContextSource;
use codex_core::context::InternalModelContextFragment;
use codex_core::context::without_update_plan_instructions;
use codex_protocol::models::ResponseItem;
use codex_protocol::protocol::ThreadGoal;
use codex_utils_template::Template;
@@ -13,6 +14,13 @@ static CONTINUATION_PROMPT_TEMPLATE: LazyLock<Template> = LazyLock::new(|| {
)
});
static CONTINUATION_PROMPT_WITHOUT_UPDATE_PLAN: LazyLock<Template> = LazyLock::new(|| {
parse_embedded_template(
&without_update_plan_instructions(include_str!("../templates/goals/continuation.md")),
"goals/continuation.md",
)
});
static BUDGET_LIMIT_PROMPT_TEMPLATE: LazyLock<Template> = LazyLock::new(|| {
parse_embedded_template(
include_str!("../templates/goals/budget_limit.md"),
@@ -27,7 +35,7 @@ static OBJECTIVE_UPDATED_PROMPT_TEMPLATE: LazyLock<Template> = LazyLock::new(||
)
});
fn parse_embedded_template(source: &'static str, template_name: &str) -> Template {
fn parse_embedded_template(source: &str, template_name: &str) -> Template {
match Template::parse(source) {
Ok(template) => template,
Err(err) => panic!("embedded template {template_name} is invalid: {err}"),
@@ -42,8 +50,11 @@ pub(crate) fn objective_updated_steering_item(goal: &ThreadGoal) -> ResponseItem
goal_context_input_item(objective_updated_prompt(goal))
}
pub(crate) fn continuation_steering_item(goal: &ThreadGoal) -> ResponseItem {
goal_context_input_item(continuation_prompt(goal))
pub(crate) fn continuation_steering_item(
goal: &ThreadGoal,
update_plan_enabled: bool,
) -> ResponseItem {
goal_context_input_item(continuation_prompt(goal, update_plan_enabled))
}
fn goal_context_input_item(prompt: String) -> ResponseItem {
@@ -53,7 +64,7 @@ fn goal_context_input_item(prompt: String) -> ResponseItem {
))
}
fn continuation_prompt(goal: &ThreadGoal) -> String {
fn continuation_prompt(goal: &ThreadGoal, update_plan_enabled: bool) -> String {
let objective = escape_xml_text(&goal.objective);
let tokens_used = goal.tokens_used.to_string();
let token_budget = goal
@@ -65,7 +76,12 @@ fn continuation_prompt(goal: &ThreadGoal) -> String {
.map(|budget| (budget - goal.tokens_used).max(0).to_string())
.unwrap_or_else(|| "unbounded".to_string());
CONTINUATION_PROMPT_TEMPLATE
let template = if update_plan_enabled {
&*CONTINUATION_PROMPT_TEMPLATE
} else {
&*CONTINUATION_PROMPT_WITHOUT_UPDATE_PLAN
};
template
.render([
("objective", objective.as_str()),
("tokens_used", tokens_used.as_str()),