mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[codex] Load AGENTS.md from all bound environments (#27696)
## Why We already have the machinery to support multiple environments on a single thread, but we only show the model the contents of `AGENTS.md` files in the primary environment. We should show the model all of the relevant project instructions when we know there's more than one environment. ## Known Gaps As discussed in the RFC, this implementation: 1. doesn't handle environments being added/removed to/from the thread after its creation 2. it doesn't enforce an aggregate context budget across environments, and instead applies the configured project maximum independently to each environment ## Implementation - Discover project instructions in environment order with an independent byte budget per environment and preserve source provenance/order. - Keep the legacy fragment byte-for-byte when exactly one environment contributes project instructions; use environment-labeled sections when two or more environments contribute. - Freeze the complete rendered fragment in `LoadedAgentsMd`, insert it directly into requests, and recognize both layouts in contextual and memory filtering. - Add exact rendering, independent-budget, source-order, creation-snapshot, and consumer coverage without changing app-server schemas.
This commit is contained in:
@@ -17,10 +17,38 @@ fn detects_environment_context_fragment() {
|
||||
|
||||
#[test]
|
||||
fn detects_agents_instructions_fragment() {
|
||||
assert!(is_contextual_user_fragment(&ContentItem::InputText {
|
||||
text: "# AGENTS.md instructions for /tmp\n\n<INSTRUCTIONS>\nbody\n</INSTRUCTIONS>"
|
||||
.to_string(),
|
||||
}));
|
||||
for text in [
|
||||
"# AGENTS.md instructions for /tmp\n\n<INSTRUCTIONS>\nbody\n</INSTRUCTIONS>",
|
||||
"# AGENTS.md instructions\n\n<INSTRUCTIONS>\nbody\n</INSTRUCTIONS>",
|
||||
] {
|
||||
assert!(is_contextual_user_fragment(&ContentItem::InputText {
|
||||
text: text.to_string(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn renders_agents_instructions_with_legacy_directory_header() {
|
||||
assert_eq!(
|
||||
UserInstructions {
|
||||
directory: Some("/tmp".to_string()),
|
||||
text: "body".to_string(),
|
||||
}
|
||||
.render(),
|
||||
"# AGENTS.md instructions for /tmp\n\n<INSTRUCTIONS>\nbody\n</INSTRUCTIONS>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn renders_agents_instructions_without_directory_header() {
|
||||
assert_eq!(
|
||||
UserInstructions {
|
||||
directory: None,
|
||||
text: "body".to_string(),
|
||||
}
|
||||
.render(),
|
||||
"# AGENTS.md instructions\n\n<INSTRUCTIONS>\nbody\n</INSTRUCTIONS>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::ContextualUserFragment;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct UserInstructions {
|
||||
pub(crate) directory: String,
|
||||
pub(crate) directory: Option<String>,
|
||||
pub(crate) text: String,
|
||||
}
|
||||
|
||||
@@ -16,10 +16,15 @@ impl ContextualUserFragment for UserInstructions {
|
||||
}
|
||||
|
||||
fn type_markers() -> (&'static str, &'static str) {
|
||||
("# AGENTS.md instructions for ", "</INSTRUCTIONS>")
|
||||
("# AGENTS.md instructions", "</INSTRUCTIONS>")
|
||||
}
|
||||
|
||||
fn body(&self) -> String {
|
||||
format!("{}\n\n<INSTRUCTIONS>\n{}\n", self.directory, self.text)
|
||||
let directory = self
|
||||
.directory
|
||||
.as_ref()
|
||||
.map(|directory| format!(" for {directory}"))
|
||||
.unwrap_or_default();
|
||||
format!("{directory}\n\n<INSTRUCTIONS>\n{}\n", self.text)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user