core: bundle settings diff updates into one dev/user envelope (#12417)

## Summary
- bundle contextual prompt injection into at most one developer message
plus one contextual user message in both:
  - per-turn settings updates
  - initial context insertion
- preserve `<model_switch>` across compaction by rebuilding it through
canonical initial-context injection, instead of relying on
strip/reattach hacks
- centralize contextual user fragment detection in one shared definition
table and reuse it for parsing/compaction logic
- keep `AGENTS.md` in its natural serialized format:
  - `# AGENTS.md instructions for {dirname}`
  - `<INSTRUCTIONS>...</INSTRUCTIONS>`
- simplify related tests/helpers and accept the expected snapshot/layout
updates from bundled multi-part messages

## Why
The goal is to converge toward a simpler, more intentional prompt shape
where contextual updates are consistently represented as one developer
envelope plus one contextual user envelope, while keeping parsing and
compaction behavior aligned with that representation.

## Notable details
- the temporary `SettingsUpdateEnvelope` wrapper was removed; these
paths now return `Vec<ResponseItem>` directly
- local/remote compaction no longer rely on model-switch strip/restore
helpers
- contextual user detection is now driven by shared fragment definitions
instead of ad hoc matcher assembly
- AGENTS/user instructions are still the same logical context; only the
synthetic `<user_instructions>` wrapper was replaced by the natural
AGENTS text format

## Testing
- `just fmt`
- `cargo test -p codex-app-server
codex_message_processor::tests::extract_conversation_summary_prefers_plain_user_messages
-- --exact`
- `cargo test -p codex-core
compact::tests::collect_user_messages_filters_session_prefix_entries
--lib -- --exact`
- `cargo test -p codex-core --test all
'suite::compact::snapshot_request_shape_pre_turn_compaction_strips_incoming_model_switch'
-- --exact`
- `cargo test -p codex-core --test all
'suite::compact_remote::snapshot_request_shape_remote_pre_turn_compaction_strips_incoming_model_switch'
-- --exact`
- `cargo test -p codex-core --test all
'suite::client::includes_apps_guidance_as_developer_message_when_enabled'
-- --exact`
- `cargo test -p codex-core --test all
'suite::client::includes_developer_instructions_message_in_request' --
--exact`
- `cargo test -p codex-core --test all
'suite::client::includes_user_instructions_message_in_request' --
--exact`
- `cargo test -p codex-core --test all
'suite::client::resume_includes_initial_messages_and_sends_prior_items'
-- --exact`
- `cargo test -p codex-core --test all
'suite::review::review_input_isolated_from_parent_history' -- --exact`
- `cargo test -p codex-exec --test all
'suite::resume::exec_resume_last_respects_cwd_filter_and_all_flag' --
--exact`
- `cargo test -p core_test_support
context_snapshot::tests::full_text_mode_preserves_unredacted_text --
--exact`

## Notes
- I also ran several targeted `compact`, `compact_remote`,
`prompt_caching`, `model_visible_layout`, and `event_mapping` tests
while iterating on prompt-shape changes.
- I have not claimed a clean full-workspace `cargo test` from this
environment because local sandbox/resource conditions have previously
produced unrelated failures in large workspace runs.
This commit is contained in:
Charley Cunningham
2026-02-26 00:12:08 -08:00
committed by GitHub
parent 28bfbb8f2b
commit 07aefffb1f
47 changed files with 966 additions and 813 deletions

View File

@@ -62,7 +62,7 @@ pub fn format_response_items_snapshot(items: &[Value], options: &ContextSnapshot
match item_type {
"message" => {
let role = item.get("role").and_then(Value::as_str).unwrap_or("unknown");
let text = item
let rendered_parts = item
.get("content")
.and_then(Value::as_array)
.map(|content| {
@@ -93,11 +93,27 @@ pub fn format_response_items_snapshot(items: &[Value], options: &ContextSnapshot
}
})
.collect::<Vec<String>>()
.join(" | ")
})
.filter(|text| !text.is_empty())
.unwrap_or_else(|| "<NO_TEXT>".to_string());
format!("{idx:02}:message/{role}:{text}")
.unwrap_or_default();
let role = if rendered_parts.len() > 1 {
format!("{role}[{}]", rendered_parts.len())
} else {
role.to_string()
};
if rendered_parts.is_empty() {
return format!("{idx:02}:message/{role}:<NO_TEXT>");
}
if rendered_parts.len() == 1 {
return format!("{idx:02}:message/{role}:{}", rendered_parts[0]);
}
let parts = rendered_parts
.iter()
.enumerate()
.map(|(part_idx, part)| format!(" [{:02}] {part}", part_idx + 1))
.collect::<Vec<String>>()
.join("\n");
format!("{idx:02}:message/{role}:\n{parts}")
}
"function_call" => {
let name = item.get("name").and_then(Value::as_str).unwrap_or("unknown");
@@ -258,7 +274,7 @@ mod tests {
"role": "user",
"content": [{
"type": "input_text",
"text": "# AGENTS.md instructions for /tmp/example"
"text": "# AGENTS.md instructions for /tmp/example\n\n<INSTRUCTIONS>\nbody\n</INSTRUCTIONS>"
}]
})];
@@ -269,7 +285,7 @@ mod tests {
assert_eq!(
rendered,
"00:message/user:# AGENTS.md instructions for /tmp/example"
r"00:message/user:# AGENTS.md instructions for /tmp/example\n\n<INSTRUCTIONS>\nbody\n</INSTRUCTIONS>"
);
}
@@ -280,7 +296,7 @@ mod tests {
"role": "user",
"content": [{
"type": "input_text",
"text": "# AGENTS.md instructions for /tmp/example"
"text": "# AGENTS.md instructions for /tmp/example\n\n<INSTRUCTIONS>\nbody\n</INSTRUCTIONS>"
}]
})];
@@ -333,7 +349,7 @@ mod tests {
assert_eq!(
rendered,
"00:message/user:<image> | <input_image:image_url> | </image>"
"00:message/user[3]:\n [01] <image>\n [02] <input_image:image_url>\n [03] </image>"
);
}
}