Retain client-marked tool outputs across compaction (#52937)

## Why

Client-supplied tool outputs can carry task instructions. Even when marked with `retain: true`, these instructions could be trimmed before compaction and disappear from the compacted history.

## What changed

Add the disabled-by-default `retain_client_tool_outputs` feature to honor retention for explicitly marked, named `function_call_output` items without a `call_id`.

- Preserve eligible outputs in remote compaction v2 and when starting a new context window, keeping their original role.
- Stop emergency tool-output trimming at a retained output.
- Share the existing 64,000-token retention budget, keeping outputs whole and dropping an oversized output and older items rather than partially truncating it.
- Place initial context before retained standalone outputs and account for their images in retained-image counts.

## Testing

Add a scenario covering enabled and disabled retention, emergency trimming, oversized-output handling, and preservation of the latest instruction after compaction and cold resume. Verify that retention annotations stay out of model requests.

GitOrigin-RevId: 449179670e4d2be2623928700738758e43b5dc1a
This commit is contained in:
Won Park
2026-10-10 17:30:25 +00:00
committed by copyberry
parent be9060ff96
commit 6c0c6759d0
12 changed files with 452 additions and 31 deletions
+6
View File
@@ -1124,6 +1124,9 @@
"retain_client_developer_messages": {
"type": "boolean"
},
"retain_client_tool_outputs": {
"type": "boolean"
},
"rollout_budget": {
"$ref": "#/definitions/FeatureToml_for_RolloutBudgetConfigToml"
},
@@ -7427,6 +7430,9 @@
"retain_client_developer_messages": {
"type": "boolean"
},
"retain_client_tool_outputs": {
"type": "boolean"
},
"rollout_budget": {
"$ref": "#/definitions/FeatureToml_for_RolloutBudgetConfigToml"
},
+5 -1
View File
@@ -606,7 +606,7 @@ pub(crate) fn is_summary_message(message: &str) -> bool {
/// model-expected boundary.
///
/// Placement rules:
/// - Prefer immediately before the last real user or agent message.
/// - Prefer immediately before the last real user, agent message, or retained standalone output.
/// - If no real user messages remain, insert before the compaction summary so
/// the summary stays last.
/// - If there are no user messages, insert before the last compaction item so
@@ -619,6 +619,10 @@ pub(crate) fn insert_initial_context_before_last_real_user_or_summary(
let mut last_user_or_summary_index = None;
let mut last_real_user_index = None;
for (i, item) in compacted_history.iter().enumerate().rev() {
if crate::compact_retained_tool_outputs::is_retained_tool_output(item) {
last_real_user_index = Some(i);
break;
}
if let ResponseItem::AgentMessage { content, .. } = &item.item
&& !matches!(
content.first(),
+15 -4
View File
@@ -1,10 +1,12 @@
use std::borrow::Borrow;
use crate::compact_retained_tool_outputs::is_retained_tool_output;
use crate::context::ContextualUserFragment;
use crate::context::ImageResizeNotice;
use crate::context_manager::ContextManager;
use crate::context_manager::estimate_item_token_count;
use crate::session::turn_context::TurnContext;
use codex_features::Feature;
use codex_history::ResponseItemEnvelope;
use codex_protocol::models::BaseInstructions;
use codex_protocol::models::ContentItem;
@@ -97,10 +99,19 @@ pub(crate) fn trim_function_call_history_to_fit_context_window(
let source_index = original_items
.len()
.saturating_sub(consumed_items.saturating_add(group_item_count));
let Some(rewritten_item) = original_items
.get(source_index)
.and_then(rewritten_output_for_context_window)
else {
let Some(rewritten_item) = original_items.get(source_index).and_then(|envelope| {
if turn_context
.config
.features
.enabled(Feature::RetainClientToolOutputs)
&& is_retained_tool_output(envelope)
{
// Enabled retained outputs form the same boundary as user/developer messages.
None
} else {
rewritten_output_for_context_window(envelope)
}
}) else {
break;
};
estimated_tokens = estimated_tokens
+31 -9
View File
@@ -1,5 +1,7 @@
use std::sync::Arc;
use crate::compact_retained_tool_outputs::is_retained_tool_output;
use crate::Prompt;
use crate::ResponseStream;
use crate::client::ModelClientSession;
@@ -35,6 +37,7 @@ use codex_analytics::CompactionTrigger;
use codex_context_fragments::set_annotated_content;
use codex_context_fragments::to_annotated_content;
use codex_features::Feature;
use codex_features::Features;
use codex_history::CodexHarnessMetadata;
use codex_history::ResponseItemEnvelope;
use codex_protocol::error::CodexErr;
@@ -44,6 +47,8 @@ use codex_protocol::items::ContextCompactionItem;
use codex_protocol::items::TurnItem;
use codex_protocol::models::AgentMessageInputContent;
use codex_protocol::models::ContentItem;
use codex_protocol::models::FunctionCallOutputBody;
use codex_protocol::models::FunctionCallOutputContentItem;
#[cfg(test)]
use codex_protocol::models::ImageReference;
use codex_protocol::models::ResponseItem;
@@ -303,7 +308,7 @@ async fn run_remote_compact_task_inner_impl(
prompt_input,
prompt_input_metadata,
compaction_output,
sess.enabled(Feature::RetainClientDeveloperMessages),
sess.features().get(),
if sess.enabled(Feature::CompactionImageBudget) {
RetainedImageBudget::Enabled
} else {
@@ -495,7 +500,7 @@ fn build_v2_compacted_history(
prompt_input: Vec<ResponseItem>,
prompt_input_metadata: Vec<Option<CodexHarnessMetadata>>,
compaction_output: ResponseItem,
retain_client_developer_messages: bool,
features: &Features,
image_budget: RetainedImageBudget,
) -> (Vec<ResponseItemEnvelope>, usize) {
debug_assert_eq!(prompt_input.len(), prompt_input_metadata.len());
@@ -505,9 +510,7 @@ fn build_v2_compacted_history(
.map(|(item, metadata)| ResponseItemEnvelope { item, metadata })
.collect::<Vec<_>>();
let retained = v2_history_item_groups(prompt_input)
.filter(|group| {
is_retained_for_remote_compaction_v2(&group.source, retain_client_developer_messages)
})
.filter(|group| is_retained_for_remote_compaction_v2(&group.source, features))
.flat_map(HistoryItemGroup::into_items)
.collect::<Vec<_>>();
let mut retained =
@@ -544,8 +547,11 @@ fn v2_history_item_groups(
fn is_retained_for_remote_compaction_v2(
envelope: &ResponseItemEnvelope,
retain_client_developer_messages: bool,
features: &Features,
) -> bool {
if features.enabled(Feature::RetainClientToolOutputs) && is_retained_tool_output(envelope) {
return true;
}
let item = &envelope.item;
if let ResponseItem::AgentMessage {
author,
@@ -583,13 +589,22 @@ fn is_retained_for_remote_compaction_v2(
Some(TurnItem::UserMessage(_) | TurnItem::HookPrompt(_))
),
"developer" => {
retain_client_developer_messages && is_client_authored_developer_message(envelope)
features.enabled(Feature::RetainClientDeveloperMessages)
&& is_client_authored_developer_message(envelope)
}
_ => false,
}
}
fn retained_input_image_count(item: &ResponseItem) -> usize {
if let ResponseItem::FunctionCallOutput { output, .. } = item
&& let FunctionCallOutputBody::ContentItems(content) = &output.body
{
return content
.iter()
.filter(|item| matches!(item, FunctionCallOutputContentItem::InputImage { .. }))
.count();
}
let ResponseItem::Message { content, .. } = item else {
return 0;
};
@@ -648,6 +663,9 @@ fn truncate_retained_messages(
}
truncated_reversed.push(group.source);
remaining = remaining.saturating_sub(token_count);
} else if is_retained_tool_output(&group.source) {
// Keep outputs whole and stop before older items to preserve newest-first ordering.
break;
} else if remaining > notice_tokens {
let available_tokens = remaining - notice_tokens;
let content_budget = if client_developer {
@@ -804,7 +822,7 @@ mod tests {
input,
metadata,
output,
/*retain_client_developer_messages*/ false,
&Features::with_defaults(),
RetainedImageBudget::Disabled,
)
}
@@ -907,6 +925,10 @@ mod tests {
};
for enabled in [false, true] {
let mut features = Features::with_defaults();
if enabled {
features.enable(Feature::RetainClientDeveloperMessages);
}
let (history, _) = build_v2_compacted_history(
vec![
harness.clone(),
@@ -924,7 +946,7 @@ mod tests {
None,
],
output.clone(),
enabled,
&features,
RetainedImageBudget::Disabled,
);
let mut expected = vec![
@@ -0,0 +1,20 @@
//! Retains explicitly marked, named function outputs that have no matching call.
//! Outputs share the normal history token budget and are retained whole or dropped.
use codex_history::ResponseItemEnvelope;
use codex_protocol::models::ResponseItem;
pub(crate) fn is_retained_tool_output(envelope: &ResponseItemEnvelope) -> bool {
envelope
.metadata
.as_ref()
.is_some_and(|metadata| metadata.client_authored)
&& matches!(
envelope.item,
ResponseItem::FunctionCallOutput {
call_id: None,
name: Some(_),
..
}
)
}
+1
View File
@@ -36,6 +36,7 @@ pub use turn_extension_data::WithTurnExtensionData;
mod compact_model_fallback;
mod compact_remote_history;
mod compact_remote_v2;
mod compact_retained_tool_outputs;
mod compact_token_budget;
mod thread_startup_metadata;
pub use codex_network_proxy::EnvironmentNetworkPolicy;
+18 -17
View File
@@ -4663,22 +4663,23 @@ impl Session {
let turn_context = step_context.turn.as_ref();
let history = self.clone_history().await;
let input_goal_ids = crate::context::UserGoalUpdate::message_ids(history.raw_items());
let retained_client_developer_messages =
if self.enabled(Feature::RetainClientDeveloperMessages) {
crate::compact_remote_v2::truncate_retained_messages_for_remote_compaction(
history
.annotated_items()
.iter()
.filter(|item| {
crate::compact_remote_v2::is_client_authored_developer_message(item)
})
.cloned()
.collect(),
crate::compact_remote_v2::RETAINED_MESSAGE_TOKEN_BUDGET,
)
} else {
Vec::new()
};
let retained_client_items =
crate::compact_remote_v2::truncate_retained_messages_for_remote_compaction(
history
.annotated_items()
.iter()
.filter(|item| {
(self.enabled(Feature::RetainClientToolOutputs)
&& crate::compact_retained_tool_outputs::is_retained_tool_output(item))
|| (self.enabled(Feature::RetainClientDeveloperMessages)
&& crate::compact_remote_v2::is_client_authored_developer_message(
item,
))
})
.cloned()
.collect(),
crate::compact_remote_v2::RETAINED_MESSAGE_TOKEN_BUDGET,
);
let window = {
let mut state = self.state.lock().await;
state.start_new_context_window()
@@ -4691,7 +4692,7 @@ impl Session {
crate::context_manager::updates::merge_world_state_updates(context_updates)
.into_iter()
.map(ResponseItemEnvelope::new)
.chain(retained_client_developer_messages)
.chain(retained_client_items)
.collect();
let turn_context_item = step_context.to_turn_context_item();
self.replace_compacted_history(
+3
View File
@@ -89,6 +89,9 @@ use tokio::sync::oneshot;
const ONE_PIXEL_PNG_BASE64: &str = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4z8DwHwAFAAH/iZk9HQAAAABJRU5ErkJggg==";
#[path = "scenarios_retained_tool_outputs.rs"]
mod retained_tool_outputs;
#[path = "scenarios_incremental_tools.rs"]
mod incremental_tools;
#[path = "scenarios_incremental_tools_resume.rs"]
@@ -0,0 +1,149 @@
//! Client-marked function outputs survive compaction and a cold resume in their original role.
use super::*;
use codex_protocol::turn_input::AnnotatedResponseItem;
use codex_protocol::turn_input::ResponseItemAnnotations;
use codex_protocol::turn_input::TurnInput;
use pretty_assertions::assert_eq;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn retained_tool_output_survives_compaction_and_resume() -> Result<()> {
skip_if_no_network!(Ok(()));
for enabled in [false, true] {
let server = start_mock_server().await;
let mock = mount_sse_sequence(&server, vec![
sse(vec![ev_assistant_message("accepted", "I will investigate the tests."), ev_completed("accepted-response")]),
sse(vec![ev_assistant_message("details-accepted", "I will use those task details."), ev_completed("details-response")]),
sse(vec![ev_completed("instruction-response")]),
sse(vec![json!({"type":"response.output_item.done", "item":{"type":"compaction", "encrypted_content":"RETAINED_TOOL_OUTPUT_CHECKPOINT"}}), ev_completed("compact-response")]),
sse(vec![ev_assistant_message("resumed", "The request was to investigate the tests."), ev_completed("resumed-response")]),
]).await;
let configure = move |config: &mut Config| {
configure_scenario_catalog(config);
config
.features
.set_enabled(Feature::RetainClientToolOutputs, enabled)
.unwrap();
config.model_context_window = Some(1_000);
config.model_auto_compact_token_limit = Some(i64::MAX);
// Let this scenario exercise the compaction budget rather than ingestion truncation.
config
.model_catalog
.as_mut()
.unwrap()
.models
.iter_mut()
.find(|model| model.slug == "gpt-6-astra")
.unwrap()
.truncation_policy = codex_protocol::openai_models::TruncationPolicyConfig::tokens(
/*limit*/ 100_000,
);
};
let test = test_codex()
.with_model("gpt-6-astra")
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
.with_config(configure)
.build_with_auto_env(&server)
.await?;
let details = "Investigate the failing tests carefully. ".repeat(7_000);
let latest_instruction = "Report your findings without changing files.";
for (name, output) in [
(
"send_message_to_thread",
"Investigate the failing tests; report your findings without changing files.",
),
("task_details", details.as_str()),
("send_message_to_thread", latest_instruction),
] {
let request = TurnInputRequest::new(TurnInput::AnnotatedResponseItem(
AnnotatedResponseItem {
item: serde_json::from_value(json!({
"type": "function_call_output", "name": name, "namespace": "codex_app", "output": output,
}))?,
annotations: ResponseItemAnnotations { retain: true },
},
));
test.codex.start_or_steer_turn(request).await?;
wait_for_event(&test.codex, |event| {
matches!(event, EventMsg::TurnComplete(_))
})
.await;
}
test.codex
.inject_response_items(vec![serde_json::from_value(json!({
"type": "function_call_output", "name": "progress", "namespace": "example",
"output": "Transient progress update. ".repeat(1000),
}))?])
.await?;
test.codex.submit(Op::Compact).await?;
wait_for_event(&test.codex, |event| {
matches!(event, EventMsg::TurnComplete(_))
})
.await;
let resumed = test_codex()
.with_model("gpt-6-astra")
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
.with_config(move |config| {
configure(config);
config.model_context_window = Some(200_000);
})
.restart_with_auto_env(&server, &test)
.await?;
resumed
.submit_text_turn("What request are you working on?")
.await?;
let requests = mock.requests();
assert_eq!(requests.len(), 5);
for request in &requests[..3] {
assert!(request.inputs_of_type("compaction_trigger").is_empty());
}
let compact_outputs = requests[3].inputs_of_type("function_call_output");
assert_eq!(compact_outputs.len(), 4);
// Emergency trimming removes the trailing log, then stops at the marked instruction.
assert_eq!(compact_outputs[1]["output"], details);
if enabled {
assert_eq!(compact_outputs[2]["output"], latest_instruction);
} else {
assert_eq!(
compact_outputs[2]["output"],
"Output exceeded the available model context and was truncated"
);
}
assert_eq!(
compact_outputs[3]["output"],
"Output exceeded the available model context and was truncated"
);
let last = requests.last().unwrap();
assert!(last.inputs_of_type("compaction_trigger").is_empty());
assert!(last.body_contains_text("What request are you working on?"));
let outputs = last.inputs_of_type("function_call_output");
if enabled {
// Oversized details and older instructions are dropped; the latest stays whole.
assert_eq!(outputs, vec![compact_outputs[2].clone()]);
for output in outputs {
assert!(output.get("retain").is_none());
assert!(output.get("client_authored").is_none());
}
} else {
assert!(outputs.is_empty());
}
insta::assert_snapshot!(
if enabled {
"retained_tool_output_survives_compaction_and_resume"
} else {
"retained_tool_output_retention_disabled"
},
context_snapshot::format_request_history_snapshot(
if enabled {
"Retained thread instructions stop emergency tool-output trimming. Compaction keeps whole outputs within the shared 64K retention budget, dropping oversized task details and older instructions. A cold resume preserves the latest instruction unchanged."
} else {
"With retention disabled, the client can still send retain=true. Tool outputs use ordinary emergency trimming and are not retained after compaction and cold resume."
},
&requests,
&ContextSnapshotOptions::default().rewrite_known_segments(),
)
);
resumed.codex.shutdown_and_wait().await?;
}
Ok(())
}
@@ -0,0 +1,115 @@
---
source: core/tests/suite/scenarios_retained_tool_outputs.rs
expression: "context_snapshot::format_request_history_snapshot(if enabled\n{\n \"Retained thread instructions stop emergency tool-output trimming. Compaction shares the 64K retention budget with oversized task details, and a cold resume preserves the retained tool content.\"\n} else\n{\n \"With retention disabled, the client can still send retain=true. Tool outputs use ordinary emergency trimming and are not retained after compaction and cold resume.\"\n}, &requests, &ContextSnapshotOptions::default().rewrite_known_segments(),)"
---
Scenario: With retention disabled, the client can still send retain=true. Tool outputs use ordinary emergency trimming and are not retained after compaction and cold resume.
## Window 1
-- request 1 (turn) --
00:additional_tools/developer (3; hash=bf2e30605ff5dc17):
- namespace/functions; hash=fada4a476a3754dd
- custom/exec
- function/wait
- function/request_user_input
- function/request_user_input_async
- namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317
- function/sleep
- namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63
- function/followup_task
- function/interrupt_agent
- function/list_agents
- function/send_message
- function/spawn_agent
- function/wait_agent
01:message/developer:
<MODEL_INSTRUCTIONS>
02:message/developer[2]:
[01] <PERMISSIONS_INSTRUCTIONS>
[02] <COLLABORATION_MODE>
03:message/developer:
<MULTI_AGENT_ROLE>
04:message/developer:
<MULTI_AGENT_MODE>
05:message/user:
<ENVIRONMENT_CONTEXT>
06:function_call_output/codex_app.send_message_to_thread:Investigate the failing tests; report your findings without changing files.
-- request 2 (turn) --
07:message/assistant:
I will investigate the tests.
08:function_call_output/codex_app.task_details:Investigate the failing tests carefully. Investigate the failing tests carefully. Investigate the failin...s carefully. Investigate the failing tests carefully. [hash=3c65017b6f98a38f]
-- request 3 (turn) --
09:message/assistant:
I will use those task details.
10:function_call_output/codex_app.send_message_to_thread:Report your findings without changing files.
## Window 2 (after request 3: input diverged at item 10)
-- request 4 (compaction) --
00:additional_tools/developer (3; hash=bf2e30605ff5dc17):
- namespace/functions; hash=fada4a476a3754dd
- custom/exec
- function/wait
- function/request_user_input
- function/request_user_input_async
- namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317
- function/sleep
- namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63
- function/followup_task
- function/interrupt_agent
- function/list_agents
- function/send_message
- function/spawn_agent
- function/wait_agent
01:message/developer:
<MODEL_INSTRUCTIONS>
02:message/developer[2]:
[01] <PERMISSIONS_INSTRUCTIONS>
[02] <COLLABORATION_MODE>
03:message/developer:
<MULTI_AGENT_ROLE>
04:message/developer:
<MULTI_AGENT_MODE>
05:message/user:
<ENVIRONMENT_CONTEXT>
06:function_call_output/codex_app.send_message_to_thread:Investigate the failing tests; report your findings without changing files.
07:message/assistant:
I will investigate the tests.
08:function_call_output/codex_app.task_details:Investigate the failing tests carefully. Investigate the failing tests carefully. Investigate the failin...s carefully. Investigate the failing tests carefully. [hash=3c65017b6f98a38f]
09:message/assistant:
I will use those task details.
10:function_call_output/codex_app.send_message_to_thread:Output exceeded the available model context and was truncated
11:function_call_output/example.progress:Output exceeded the available model context and was truncated
12:compaction_trigger
## Window 3 (after request 4: input diverged at item 06)
-- request 5 (turn) --
00:additional_tools/developer (3; hash=bf2e30605ff5dc17):
- namespace/functions; hash=fada4a476a3754dd
- custom/exec
- function/wait
- function/request_user_input
- function/request_user_input_async
- namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317
- function/sleep
- namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63
- function/followup_task
- function/interrupt_agent
- function/list_agents
- function/send_message
- function/spawn_agent
- function/wait_agent
01:message/developer:
<MODEL_INSTRUCTIONS>
02:message/developer[2]:
[01] <PERMISSIONS_INSTRUCTIONS>
[02] <COLLABORATION_MODE>
03:message/developer:
<MULTI_AGENT_ROLE>
04:message/developer:
<MULTI_AGENT_MODE>
05:message/user:
<ENVIRONMENT_CONTEXT>
06:compaction:encrypted=true; chars=31; hash=dc1cfb1784ca88e3
07:message/user:
<ENVIRONMENT_CONTEXT>
08:message/user:
What request are you working on?
@@ -0,0 +1,81 @@
---
source: core/tests/suite/scenarios_retained_tool_outputs.rs
expression: "context_snapshot::format_request_history_snapshot(\"Retained thread instructions stop emergency tool-output trimming. Compaction keeps whole outputs within the shared 64K retention budget, dropping oversized task details and older instructions. A cold resume preserves the latest instruction unchanged.\",\n&requests, &ContextSnapshotOptions::default().rewrite_known_segments(),)"
---
Scenario: Retained thread instructions stop emergency tool-output trimming. Compaction keeps whole outputs within the shared 64K retention budget, dropping oversized task details and older instructions. A cold resume preserves the latest instruction unchanged.
## Window 1
-- request 1 (turn) --
00:additional_tools/developer (3; hash=bf2e30605ff5dc17):
- namespace/functions; hash=fada4a476a3754dd
- custom/exec
- function/wait
- function/request_user_input
- function/request_user_input_async
- namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317
- function/sleep
- namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63
- function/followup_task
- function/interrupt_agent
- function/list_agents
- function/send_message
- function/spawn_agent
- function/wait_agent
01:message/developer:
<MODEL_INSTRUCTIONS>
02:message/developer[2]:
[01] <PERMISSIONS_INSTRUCTIONS>
[02] <COLLABORATION_MODE>
03:message/developer:
<MULTI_AGENT_ROLE>
04:message/developer:
<MULTI_AGENT_MODE>
05:message/user:
<ENVIRONMENT_CONTEXT>
06:function_call_output/codex_app.send_message_to_thread:Investigate the failing tests; report your findings without changing files.
-- request 2 (turn) --
07:message/assistant:
I will investigate the tests.
08:function_call_output/codex_app.task_details:Investigate the failing tests carefully. Investigate the failing tests carefully. Investigate the failin...s carefully. Investigate the failing tests carefully. [hash=3c65017b6f98a38f]
-- request 3 (turn) --
09:message/assistant:
I will use those task details.
10:function_call_output/codex_app.send_message_to_thread:Report your findings without changing files.
-- request 4 (compaction) --
11:function_call_output/example.progress:Output exceeded the available model context and was truncated
12:compaction_trigger
## Window 2 (after request 4: input diverged at item 06)
-- request 5 (turn) --
00:additional_tools/developer (3; hash=bf2e30605ff5dc17):
- namespace/functions; hash=fada4a476a3754dd
- custom/exec
- function/wait
- function/request_user_input
- function/request_user_input_async
- namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317
- function/sleep
- namespace/collaboration: Tools for spawning and managing sub-agents.; hash=2c8d6295978dbe63
- function/followup_task
- function/interrupt_agent
- function/list_agents
- function/send_message
- function/spawn_agent
- function/wait_agent
01:message/developer:
<MODEL_INSTRUCTIONS>
02:message/developer[2]:
[01] <PERMISSIONS_INSTRUCTIONS>
[02] <COLLABORATION_MODE>
03:message/developer:
<MULTI_AGENT_ROLE>
04:message/developer:
<MULTI_AGENT_MODE>
05:message/user:
<ENVIRONMENT_CONTEXT>
06:function_call_output/codex_app.send_message_to_thread:Report your findings without changing files.
07:compaction:encrypted=true; chars=31; hash=dc1cfb1784ca88e3
08:message/user:
<ENVIRONMENT_CONTEXT>
09:message/user:
What request are you working on?
+8
View File
@@ -423,6 +423,8 @@ pub enum Feature {
CompactionImageBudget,
/// Retain client-authored developer messages across compacted context windows.
RetainClientDeveloperMessages,
/// Honor client-requested tool-output retention across context windows.
RetainClientToolOutputs,
/// Use Agent Identity for ChatGPT-authenticated sessions.
UseAgentIdentity,
/// Enable workspace dependency support.
@@ -2052,6 +2054,12 @@ pub const FEATURES: &[FeatureSpec] = &[
stage: Stage::UnderDevelopment,
default_enabled: false,
},
FeatureSpec {
id: Feature::RetainClientToolOutputs,
key: "retain_client_tool_outputs",
stage: Stage::UnderDevelopment,
default_enabled: false,
},
FeatureSpec {
id: Feature::UseAgentIdentity,
key: "use_agent_identity",