mirror of
https://github.com/openai/codex.git
synced 2026-11-05 20:10:33 +00:00
Use generic extension context for goals
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
use super::*;
|
||||
use crate::context::ContextualUserFragment;
|
||||
use crate::context::ExtensionContextTag;
|
||||
use crate::context::SubagentNotification;
|
||||
use codex_protocol::items::HookPromptFragment;
|
||||
use codex_protocol::items::build_hook_prompt_message;
|
||||
@@ -21,15 +20,6 @@ fn detects_extension_context_fragment() {
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detects_goal_extension_context_fragment() {
|
||||
let text = ExtensionContext::with_tag(ExtensionContextTag::GOAL, "hidden goal prompt").render();
|
||||
|
||||
assert!(is_contextual_user_fragment(&ContentItem::InputText {
|
||||
text
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detects_agents_instructions_fragment() {
|
||||
assert!(is_contextual_user_fragment(&ContentItem::InputText {
|
||||
|
||||
@@ -1,51 +1,23 @@
|
||||
//! Hidden user-context wrappers owned by extensions.
|
||||
//!
|
||||
//! Extensions can use one of these host-recognized tags when they need hidden
|
||||
//! model-visible context to survive history filtering without teaching core
|
||||
//! about the extension's domain concepts.
|
||||
//! Extensions can use this host-recognized tag when they need hidden
|
||||
//! model-visible context to survive history filtering.
|
||||
|
||||
use super::ContextualUserFragment;
|
||||
use super::fragment::matches_marked_text;
|
||||
|
||||
/// Host-recognized marker pair for extension-owned hidden context.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct ExtensionContextTag {
|
||||
start_marker: &'static str,
|
||||
end_marker: &'static str,
|
||||
}
|
||||
|
||||
impl ExtensionContextTag {
|
||||
pub const DEFAULT: Self = Self::new("<extension_context>", "</extension_context>");
|
||||
pub const GOAL: Self = Self::new("<goal_context>", "</goal_context>");
|
||||
|
||||
const fn new(start_marker: &'static str, end_marker: &'static str) -> Self {
|
||||
Self {
|
||||
start_marker,
|
||||
end_marker,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const EXTENSION_CONTEXT_TAGS: &[ExtensionContextTag] =
|
||||
&[ExtensionContextTag::DEFAULT, ExtensionContextTag::GOAL];
|
||||
const START_MARKER: &str = "<extension_context>";
|
||||
const END_MARKER: &str = "</extension_context>";
|
||||
|
||||
/// Hidden user-context fragment for extension-owned steering prompts.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ExtensionContext {
|
||||
tag: ExtensionContextTag,
|
||||
body: String,
|
||||
}
|
||||
|
||||
impl ExtensionContext {
|
||||
pub fn new(body: impl Into<String>) -> Self {
|
||||
Self::with_tag(ExtensionContextTag::DEFAULT, body)
|
||||
}
|
||||
|
||||
pub fn with_tag(tag: ExtensionContextTag, body: impl Into<String>) -> Self {
|
||||
Self {
|
||||
tag,
|
||||
body: body.into(),
|
||||
}
|
||||
Self { body: body.into() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,20 +27,15 @@ impl ContextualUserFragment for ExtensionContext {
|
||||
}
|
||||
|
||||
fn markers(&self) -> (&'static str, &'static str) {
|
||||
(self.tag.start_marker, self.tag.end_marker)
|
||||
(START_MARKER, END_MARKER)
|
||||
}
|
||||
|
||||
fn type_markers() -> (&'static str, &'static str) {
|
||||
(
|
||||
ExtensionContextTag::DEFAULT.start_marker,
|
||||
ExtensionContextTag::DEFAULT.end_marker,
|
||||
)
|
||||
(START_MARKER, END_MARKER)
|
||||
}
|
||||
|
||||
fn matches_text(text: &str) -> bool {
|
||||
EXTENSION_CONTEXT_TAGS
|
||||
.iter()
|
||||
.any(|tag| matches_marked_text(tag.start_marker, tag.end_marker, text))
|
||||
matches_marked_text(START_MARKER, END_MARKER, text)
|
||||
}
|
||||
|
||||
fn body(&self) -> String {
|
||||
|
||||
@@ -39,7 +39,6 @@ pub(crate) use contextual_user_message::is_contextual_user_fragment;
|
||||
pub(crate) use contextual_user_message::parse_visible_hook_prompt_message;
|
||||
pub(crate) use environment_context::EnvironmentContext;
|
||||
pub use extension_context::ExtensionContext;
|
||||
pub use extension_context::ExtensionContextTag;
|
||||
pub use fragment::ContextualUserFragment;
|
||||
pub(crate) use fragment::FragmentRegistration;
|
||||
pub(crate) use fragment::FragmentRegistrationProxy;
|
||||
|
||||
@@ -228,8 +228,6 @@ fn idle_extension_input(prompt: String) -> TurnInput {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::context::ExtensionContextTag;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
@@ -240,7 +238,7 @@ mod tests {
|
||||
);
|
||||
let original_len = prompt.len();
|
||||
|
||||
let prompt = ExtensionContext::with_tag(ExtensionContextTag::GOAL, prompt).render();
|
||||
let prompt = ExtensionContext::new(prompt).render();
|
||||
let TurnInput::ResponseInputItem(ResponseInputItem::Message { content, .. }) =
|
||||
idle_extension_input(prompt)
|
||||
else {
|
||||
@@ -250,7 +248,7 @@ mod tests {
|
||||
panic!("expected one text content item");
|
||||
};
|
||||
|
||||
assert!(text.starts_with("<goal_context>"));
|
||||
assert!(text.starts_with("<extension_context>"));
|
||||
assert!(text.contains("start"));
|
||||
assert!(text.contains("end"));
|
||||
assert!(text.len() < original_len);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use codex_core::context::ContextualUserFragment;
|
||||
use codex_core::context::ExtensionContext;
|
||||
use codex_core::context::ExtensionContextTag;
|
||||
use codex_extension_api::ThreadIdleRequest;
|
||||
use codex_protocol::models::ResponseInputItem;
|
||||
use codex_protocol::protocol::ThreadGoal;
|
||||
@@ -47,21 +46,19 @@ fn render_embedded_template<const N: usize>(
|
||||
}
|
||||
|
||||
pub(crate) fn budget_limit_steering_item(goal: &ThreadGoal) -> ResponseInputItem {
|
||||
goal_context_input_item(budget_limit_prompt(goal))
|
||||
extension_context_input_item(budget_limit_prompt(goal))
|
||||
}
|
||||
|
||||
pub(crate) fn continuation_steering_request(goal: &ThreadGoal) -> ThreadIdleRequest {
|
||||
ThreadIdleRequest::new(
|
||||
ExtensionContext::with_tag(ExtensionContextTag::GOAL, continuation_prompt(goal)).render(),
|
||||
)
|
||||
ThreadIdleRequest::new(ExtensionContext::new(continuation_prompt(goal)).render())
|
||||
}
|
||||
|
||||
pub(crate) fn objective_updated_steering_item(goal: &ThreadGoal) -> ResponseInputItem {
|
||||
goal_context_input_item(objective_updated_prompt(goal))
|
||||
extension_context_input_item(objective_updated_prompt(goal))
|
||||
}
|
||||
|
||||
fn goal_context_input_item(prompt: String) -> ResponseInputItem {
|
||||
ExtensionContext::with_tag(ExtensionContextTag::GOAL, prompt).into_response_input_item()
|
||||
fn extension_context_input_item(prompt: String) -> ResponseInputItem {
|
||||
ExtensionContext::new(prompt).into_response_input_item()
|
||||
}
|
||||
|
||||
fn continuation_prompt(goal: &ThreadGoal) -> String {
|
||||
|
||||
@@ -507,14 +507,14 @@ async fn thread_idle_hook_requests_hidden_goal_continuation() -> anyhow::Result<
|
||||
.ok_or_else(|| anyhow::anyhow!("active goal should request idle continuation"))?;
|
||||
|
||||
assert!(request.validation_key.is_some());
|
||||
assert!(request.prompt.starts_with("<goal_context>"));
|
||||
assert!(request.prompt.starts_with("<extension_context>"));
|
||||
assert!(
|
||||
request
|
||||
.prompt
|
||||
.contains("Continue working toward the active thread goal.")
|
||||
);
|
||||
assert!(request.prompt.contains("Completion audit:"));
|
||||
assert!(request.prompt.ends_with("</goal_context>"));
|
||||
assert!(request.prompt.ends_with("</extension_context>"));
|
||||
assert!(
|
||||
harness
|
||||
.should_start_idle_turn(ModeKind::Default, &request)
|
||||
|
||||
Reference in New Issue
Block a user