mirror of
https://github.com/openai/codex.git
synced 2026-09-14 11:57:03 +00:00
Align ephemeral context with fragment conventions
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -18,10 +18,11 @@
|
||||
//! - `DeveloperContextRole` for developer guidance/policy
|
||||
//! - `ContextualUserContextRole` for contextual user-role state that must be
|
||||
//! parsed as context rather than literal user intent
|
||||
//! - If the fragment is durable turn/session state that should rebuild across
|
||||
//! resume, compaction, backtracking, or fork, implement `build(...)` for the
|
||||
//! common zero-or-one case, or override `build_many(...)` when a fragment
|
||||
//! source needs to emit multiple content items from one turn-state source.
|
||||
//! - If the fragment is turn/session state that should be rebuilt from the
|
||||
//! current `TurnContext` during initial-context assembly or diffing,
|
||||
//! implement `build(...)` for the common zero-or-one case, or override
|
||||
//! `build_many(...)` when a fragment source needs to emit multiple content
|
||||
//! items from one turn-state source.
|
||||
//! `reference_context_item` is the baseline already represented in
|
||||
//! model-visible history; compare against it to avoid duplicates, and use
|
||||
//! `TurnContextDiffParams` for other runtime/session inputs such as
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
//! `matches_contextual_user_text()` for custom matching.
|
||||
//! 4. If the fragment is derived from `TurnContext` and should participate in
|
||||
//! initial-context assembly and turn-to-turn diffing, implementing
|
||||
//! `build(...)`.
|
||||
//! `build(...)` for the common zero-or-one case or overriding
|
||||
//! `build_many(...)` for zero-or-many sources.
|
||||
//! 5. Registering the fragment exactly once in
|
||||
//! `REGISTERED_MODEL_VISIBLE_FRAGMENTS` in the rough order it should appear
|
||||
//! in model-visible context.
|
||||
@@ -79,7 +80,6 @@ use codex_protocol::protocol::TurnContextItem;
|
||||
use codex_protocol::protocol::TurnContextNetworkItem;
|
||||
use codex_protocol::protocol::USER_INSTRUCTIONS_CLOSE_TAG;
|
||||
use codex_protocol::protocol::USER_INSTRUCTIONS_OPEN_TAG;
|
||||
use codex_protocol::user_input::EphemeralContext;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::path::PathBuf;
|
||||
@@ -151,7 +151,7 @@ const REGISTERED_MODEL_VISIBLE_FRAGMENTS: &[ModelVisibleFragmentRegistration] =
|
||||
ModelVisibleFragmentRegistration::of::<SkillsSectionFragment>(),
|
||||
ModelVisibleFragmentRegistration::of::<ChildAgentsInstructionsFragment>(),
|
||||
ModelVisibleFragmentRegistration::of::<EnvironmentContext>(),
|
||||
ModelVisibleFragmentRegistration::of::<EphemeralContext>(),
|
||||
ModelVisibleFragmentRegistration::of::<EphemeralContextFragment>(),
|
||||
ModelVisibleFragmentRegistration::of::<SkillInstructions>(),
|
||||
ModelVisibleFragmentRegistration::of::<PluginInstructions>(),
|
||||
ModelVisibleFragmentRegistration::of::<UserShellCommandFragment>(),
|
||||
@@ -848,7 +848,14 @@ impl ModelVisibleContextFragment for EnvironmentContext {
|
||||
}
|
||||
}
|
||||
|
||||
impl ModelVisibleContextFragment for EphemeralContext {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename = "ephemeral_context_fragment", rename_all = "snake_case")]
|
||||
pub(crate) struct EphemeralContextFragment {
|
||||
title: String,
|
||||
text: String,
|
||||
}
|
||||
|
||||
impl ModelVisibleContextFragment for EphemeralContextFragment {
|
||||
type Role = ContextualUserContextRole;
|
||||
|
||||
fn render_text(&self) -> String {
|
||||
@@ -863,7 +870,17 @@ impl ModelVisibleContextFragment for EphemeralContext {
|
||||
_reference_context_item: Option<&TurnContextItem>,
|
||||
_params: &TurnContextDiffParams<'_>,
|
||||
) -> Vec<Self> {
|
||||
turn_context.ephemeral_context.clone()
|
||||
// Ephemeral context is current-turn-scoped state: rebuild the active
|
||||
// turn's latest snapshot, but never diff it against persisted
|
||||
// reference_context_item history from older turns.
|
||||
turn_context
|
||||
.ephemeral_context
|
||||
.iter()
|
||||
.map(|context| Self {
|
||||
title: context.title.clone(),
|
||||
text: context.text.clone(),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn contextual_user_markers() -> Option<ContextualUserFragmentMarkers> {
|
||||
@@ -1048,7 +1065,7 @@ pub(crate) fn is_ephemeral_context_fragment(content_item: &ContentItem) -> bool
|
||||
let ContentItem::InputText { text } = content_item else {
|
||||
return false;
|
||||
};
|
||||
EphemeralContext::matches_contextual_user_text(text)
|
||||
EphemeralContextFragment::matches_contextual_user_text(text)
|
||||
}
|
||||
|
||||
pub(crate) fn build_turn_state_fragments(
|
||||
|
||||
@@ -6,24 +6,24 @@ Scenario: Mid-turn continuation compaction keeps both prior and active ephemeral
|
||||
|
||||
## Local Compaction Request
|
||||
00:message/developer:<PERMISSIONS_INSTRUCTIONS>
|
||||
01:message/user[2]:
|
||||
[01] <AGENTS_MD>
|
||||
[02] <ENVIRONMENT_CONTEXT:cwd=<CWD>>
|
||||
02:message/user:<additional_context_for_this_turn>\n <title>Context from my edi...
|
||||
03:message/user:SETUP_USER
|
||||
04:message/assistant:FIRST_REPLY
|
||||
05:message/user:<additional_context_for_this_turn>\n <title>Context from my edi...
|
||||
06:message/user:function call limit push
|
||||
07:function_call/test_tool
|
||||
08:function_call_output:unsupported call: test_tool
|
||||
09:message/user:<SUMMARIZATION_PROMPT>
|
||||
01:message/user[3]:
|
||||
[01] <SKILLS_SECTION>
|
||||
[02] <ENVIRONMENT_CONTEXT:cwd=<CWD#1>>
|
||||
[03] <additional_context_for_this_turn>\n <title>Context from my edi...
|
||||
02:message/user:SETUP_USER
|
||||
03:message/assistant:FIRST_REPLY
|
||||
04:message/user:<additional_context_for_this_turn>\n <title>Context from my edi...
|
||||
05:message/user:function call limit push
|
||||
06:function_call/test_tool
|
||||
07:function_call_output:unsupported call: test_tool
|
||||
08:message/user:<SUMMARIZATION_PROMPT>
|
||||
|
||||
## Local Post-Compaction History Layout
|
||||
00:message/user:SETUP_USER
|
||||
01:message/developer:<PERMISSIONS_INSTRUCTIONS>
|
||||
02:message/user[3]:
|
||||
[01] <AGENTS_MD>
|
||||
[02] <ENVIRONMENT_CONTEXT:cwd=<CWD>>
|
||||
[01] <SKILLS_SECTION>
|
||||
[02] <ENVIRONMENT_CONTEXT:cwd=<CWD#1>>
|
||||
[03] <additional_context_for_this_turn>\n <title>Context from my edi...
|
||||
03:message/user:function call limit push
|
||||
04:message/user:<COMPACTION_SUMMARY>\nAUTO_SUMMARY
|
||||
|
||||
@@ -6,15 +6,15 @@ Scenario: Pre-turn auto-compaction keeps prior turn ephemeral_context in the com
|
||||
|
||||
## Local Compaction Request
|
||||
00:message/developer:<PERMISSIONS_INSTRUCTIONS>
|
||||
01:message/user[2]:
|
||||
[01] <AGENTS_MD>
|
||||
[02] <ENVIRONMENT_CONTEXT:cwd=<CWD>>
|
||||
02:message/user:<additional_context_for_this_turn>\n <title>Context from my edi...
|
||||
03:message/user:USER_ONE
|
||||
04:message/assistant:FIRST_REPLY
|
||||
05:message/user:USER_TWO
|
||||
06:message/assistant:SECOND_REPLY
|
||||
07:message/user:<SUMMARIZATION_PROMPT>
|
||||
01:message/user[3]:
|
||||
[01] <SKILLS_SECTION>
|
||||
[02] <ENVIRONMENT_CONTEXT:cwd=<CWD#1>>
|
||||
[03] <additional_context_for_this_turn>\n <title>Context from my edi...
|
||||
02:message/user:USER_ONE
|
||||
03:message/assistant:FIRST_REPLY
|
||||
04:message/user:USER_TWO
|
||||
05:message/assistant:SECOND_REPLY
|
||||
06:message/user:<SUMMARIZATION_PROMPT>
|
||||
|
||||
## Local Post-Compaction History Layout
|
||||
00:message/user:USER_ONE
|
||||
@@ -22,7 +22,7 @@ Scenario: Pre-turn auto-compaction keeps prior turn ephemeral_context in the com
|
||||
02:message/user:<COMPACTION_SUMMARY>\nPRE_TURN_SUMMARY
|
||||
03:message/developer:<PERMISSIONS_INSTRUCTIONS>
|
||||
04:message/user[3]:
|
||||
[01] <AGENTS_MD>
|
||||
[02] <ENVIRONMENT_CONTEXT:cwd=<CWD>>
|
||||
[01] <SKILLS_SECTION>
|
||||
[02] <ENVIRONMENT_CONTEXT:cwd=<CWD#1>>
|
||||
[03] <additional_context_for_this_turn>\n <title>Context from my edi...
|
||||
05:message/user:USER_THREE
|
||||
|
||||
@@ -6,23 +6,23 @@ Scenario: Remote mid-turn continuation compaction keeps both prior and active ep
|
||||
|
||||
## Remote Compaction Request
|
||||
00:message/developer:<PERMISSIONS_INSTRUCTIONS>
|
||||
01:message/user[2]:
|
||||
[01] <AGENTS_MD>
|
||||
[02] <ENVIRONMENT_CONTEXT:cwd=<CWD>>
|
||||
02:message/user:<additional_context_for_this_turn>\n <title>Context from my edi...
|
||||
03:message/user:SETUP_USER
|
||||
04:message/assistant:REMOTE_SETUP_REPLY
|
||||
05:message/user:<additional_context_for_this_turn>\n <title>Context from my edi...
|
||||
06:message/user:USER_TWO
|
||||
07:function_call/test_tool
|
||||
08:function_call_output:unsupported call: test_tool
|
||||
01:message/user[3]:
|
||||
[01] <SKILLS_SECTION>
|
||||
[02] <ENVIRONMENT_CONTEXT:cwd=<CWD#1>>
|
||||
[03] <additional_context_for_this_turn>\n <title>Context from my edi...
|
||||
02:message/user:SETUP_USER
|
||||
03:message/assistant:REMOTE_SETUP_REPLY
|
||||
04:message/user:<additional_context_for_this_turn>\n <title>Context from my edi...
|
||||
05:message/user:USER_TWO
|
||||
06:function_call/test_tool
|
||||
07:function_call_output:unsupported call: test_tool
|
||||
|
||||
## Remote Post-Compaction History Layout
|
||||
00:message/user:SETUP_USER
|
||||
01:message/developer:<PERMISSIONS_INSTRUCTIONS>
|
||||
02:message/user[3]:
|
||||
[01] <AGENTS_MD>
|
||||
[02] <ENVIRONMENT_CONTEXT:cwd=<CWD>>
|
||||
[01] <SKILLS_SECTION>
|
||||
[02] <ENVIRONMENT_CONTEXT:cwd=<CWD#1>>
|
||||
[03] <additional_context_for_this_turn>\n <title>Context from my edi...
|
||||
03:message/user:USER_TWO
|
||||
04:compaction:encrypted=true
|
||||
|
||||
@@ -6,19 +6,19 @@ Scenario: Turns resend fresh ephemeral editor context while keeping it outside d
|
||||
|
||||
## First Request (With Editor Context)
|
||||
00:message/developer:<PERMISSIONS_INSTRUCTIONS>
|
||||
01:message/user[2]:
|
||||
[01] <AGENTS_MD>
|
||||
[02] <ENVIRONMENT_CONTEXT:cwd=<CWD>>
|
||||
02:message/user:<additional_context_for_this_turn>\n <title>Context from my editor</title>\n <content>\n## Act...
|
||||
03:message/user:first turn with editor context
|
||||
01:message/user[3]:
|
||||
[01] <SKILLS_SECTION>
|
||||
[02] <ENVIRONMENT_CONTEXT:cwd=<CWD#1>>
|
||||
[03] <additional_context_for_this_turn>\n <title>Context from my editor</title>\n <content>\n## Act...
|
||||
02:message/user:first turn with editor context
|
||||
|
||||
## Second Request (Refreshed Editor Context)
|
||||
00:message/developer:<PERMISSIONS_INSTRUCTIONS>
|
||||
01:message/user[2]:
|
||||
[01] <AGENTS_MD>
|
||||
[02] <ENVIRONMENT_CONTEXT:cwd=<CWD>>
|
||||
02:message/user:<additional_context_for_this_turn>\n <title>Context from my editor</title>\n <content>\n## Act...
|
||||
03:message/user:first turn with editor context
|
||||
04:message/assistant:turn one complete
|
||||
05:message/user:<additional_context_for_this_turn>\n <title>Context from my editor</title>\n <content>\n## Act...
|
||||
06:message/user:second turn with refreshed editor context
|
||||
01:message/user[3]:
|
||||
[01] <SKILLS_SECTION>
|
||||
[02] <ENVIRONMENT_CONTEXT:cwd=<CWD#1>>
|
||||
[03] <additional_context_for_this_turn>\n <title>Context from my editor</title>\n <content>\n## Act...
|
||||
02:message/user:first turn with editor context
|
||||
03:message/assistant:turn one complete
|
||||
04:message/user:<additional_context_for_this_turn>\n <title>Context from my editor</title>\n <content>\n## Act...
|
||||
05:message/user:second turn with refreshed editor context
|
||||
|
||||
@@ -23,10 +23,13 @@ The key modules are:
|
||||
Model-visible prompt context falls into three buckets:
|
||||
|
||||
1. Turn-state fragments.
|
||||
These are derived from current durable turn/session state and are the ones
|
||||
that must survive history-mutating flows such as resume, compaction,
|
||||
backtracking, and fork by being rebuilt from current state plus an optional
|
||||
persisted baseline.
|
||||
These are derived from current `TurnContext` state. Most represent durable
|
||||
turn/session state that must survive history-mutating flows such as resume,
|
||||
compaction, backtracking, and fork by being rebuilt from current state plus
|
||||
an optional persisted baseline. Some, such as ephemeral context, are
|
||||
intentionally current-turn-scoped: they rebuild from the active
|
||||
`TurnContext`, but are not persisted in the durable baseline for older
|
||||
turns.
|
||||
2. Registered runtime fragments.
|
||||
These are not derived from `TurnContext` diffs, but they are still modeled
|
||||
as typed fragments because they are emitted into model-visible history and,
|
||||
@@ -39,7 +42,7 @@ Model-visible prompt context falls into three buckets:
|
||||
|
||||
The single most important distinction is whether the model-visible state is:
|
||||
|
||||
- durable turn/session state that should be rebuilt from `TurnContext`
|
||||
- turn/session state that should be rebuilt from `TurnContext`
|
||||
- or a one-off event/message that is only relevant because it just happened
|
||||
|
||||
That determines whether the fragment needs `build(...)`.
|
||||
@@ -79,7 +82,7 @@ That trait owns:
|
||||
|
||||
- `type Role`
|
||||
- `render_text()`
|
||||
- optional `build(...)` for turn-state fragments
|
||||
- optional `build(...)` / `build_many(...)` for turn-state fragments
|
||||
- optional contextual-user detection via `contextual_user_markers()` or
|
||||
`matches_contextual_user_text()`
|
||||
- standard conversions such as `into_message()` and `into_response_input_item()`
|
||||
@@ -106,7 +109,13 @@ registered.
|
||||
### 3. Build semantics
|
||||
|
||||
[`ModelVisibleContextFragment::build(...)`](/Users/ccunningham/code/codex-worktree-tria/codex-rs/core/src/model_visible_context.rs#L187)
|
||||
is the canonical hook for turn-state fragments.
|
||||
is the canonical hook for the common zero-or-one turn-state case.
|
||||
|
||||
When one turn-state source intentionally renders multiple model-visible
|
||||
content items, override
|
||||
[`ModelVisibleContextFragment::build_many(...)`](/Users/ccunningham/code/codex-worktree-tria/codex-rs/core/src/model_visible_context.rs#L199)
|
||||
instead. The default `build_many(...)` implementation just lifts
|
||||
`build(...) -> Option<Self>` into `Vec<Self>`.
|
||||
|
||||
It receives:
|
||||
|
||||
@@ -225,8 +234,8 @@ These are typed and registered, but not built from `TurnContext` diffs:
|
||||
|
||||
### Registered turn-state contextual-user fragments
|
||||
|
||||
These implement `build(...)` and participate in both full initial context and
|
||||
steady-state diffs:
|
||||
These implement `build(...)` or `build_many(...)` and participate in both full
|
||||
initial context and steady-state diffs:
|
||||
|
||||
- `UserInstructionsFragment`
|
||||
- `AgentsMdInstructions`
|
||||
@@ -234,11 +243,14 @@ steady-state diffs:
|
||||
- `SkillsSectionFragment`
|
||||
- `ChildAgentsInstructionsFragment`
|
||||
- `EnvironmentContext`
|
||||
- `EphemeralContextFragment`
|
||||
|
||||
Some of these are true steady-state diff fragments (`UserInstructionsFragment`,
|
||||
`AgentsMdInstructions`, `EnvironmentContext`). Others intentionally rebuild only
|
||||
when there is no baseline and therefore behave as initial-context fragments
|
||||
expressed through the same `build(...)` hook (`JsReplInstructionsFragment`,
|
||||
`AgentsMdInstructions`, `EnvironmentContext`). `EphemeralContextFragment`
|
||||
intentionally uses `build_many(...)` because one turn can inject multiple
|
||||
ephemeral context content items. Others intentionally rebuild only when there
|
||||
is no baseline and therefore behave as initial-context fragments expressed
|
||||
through the same `build(...)` hook (`JsReplInstructionsFragment`,
|
||||
`SkillsSectionFragment`, `ChildAgentsInstructionsFragment`).
|
||||
|
||||
### Registered runtime contextual-user fragments
|
||||
@@ -360,6 +372,8 @@ When adding new model-visible context:
|
||||
4. Implement `ModelVisibleContextFragment`.
|
||||
5. Set `type Role` correctly.
|
||||
6. If it is turn-state context, implement `build(...)`.
|
||||
Override `build_many(...)` instead when one turn-state source should emit
|
||||
multiple content items.
|
||||
7. If it is contextual-user, provide stable detection with
|
||||
`contextual_user_markers()` or custom `matches_contextual_user_text()`.
|
||||
8. Register it exactly once in `REGISTERED_MODEL_VISIBLE_FRAGMENTS`, in prompt
|
||||
@@ -371,7 +385,7 @@ When adding new model-visible context:
|
||||
Rule of thumb:
|
||||
|
||||
- “This is durable prompt state” => registered typed fragment, usually with
|
||||
`build(...)`
|
||||
`build(...)` or, for zero-or-many cases, `build_many(...)`
|
||||
- “This is a one-off contextual/runtime marker” => registered typed fragment,
|
||||
usually without `build(...)`
|
||||
- “This is an isolated developer-only text event” => plain developer text is
|
||||
|
||||
Reference in New Issue
Block a user