Refine /context user-managed grouping

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
starr-openai
2026-04-07 10:33:18 -07:00
co-authored by Codex
parent 799ee7f304
commit dd5c006700
4 changed files with 26 additions and 42 deletions
+1 -1
View File
@@ -366,7 +366,7 @@ Use `thread/read` to fetch a stored thread by id without resuming it. Pass `incl
### Example: Read live context-window usage
Use `thread/context/read` to fetch an approximate, sectioned breakdown of the current model-visible context for a loaded thread. The response groups usage into `Built-in`, `AGENTS.md`, `Skills`, `Runtime context`, and `Conversation`, with token counts derived from Codex's current byte-based estimator.
Use `thread/context/read` to fetch an approximate, sectioned breakdown of the current model-visible context for a loaded thread. The response groups usage into `Built-in`, `User`, and `Conversation`, with token counts derived from Codex's current byte-based estimator. The `User` section includes AGENTS, skills, and configurable connector/plugin instructions; runtime scaffolding is folded into `Built-in`.
```json
{ "method": "thread/context/read", "id": 24, "params": { "threadId": "thr_123" } }
@@ -41,9 +41,7 @@ pub struct ContextWindowDetail {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum ContextSectionKind {
BuiltIn,
Agents,
Skills,
Runtime,
User,
Conversation,
}
@@ -62,9 +60,7 @@ struct SectionAccumulator {
#[derive(Debug, Default)]
struct BreakdownAccumulator {
built_in: SectionAccumulator,
agents: SectionAccumulator,
skills: SectionAccumulator,
runtime: SectionAccumulator,
user: SectionAccumulator,
conversation: SectionAccumulator,
verbose: bool,
}
@@ -241,9 +237,7 @@ impl BreakdownAccumulator {
fn section_accumulator(&mut self, section: ContextSectionKind) -> &mut SectionAccumulator {
match section {
ContextSectionKind::BuiltIn => &mut self.built_in,
ContextSectionKind::Agents => &mut self.agents,
ContextSectionKind::Skills => &mut self.skills,
ContextSectionKind::Runtime => &mut self.runtime,
ContextSectionKind::User => &mut self.user,
ContextSectionKind::Conversation => &mut self.conversation,
}
}
@@ -251,9 +245,7 @@ impl BreakdownAccumulator {
fn into_sections(self) -> Vec<ContextWindowSection> {
let mut sections: Vec<ContextWindowSection> = [
self.built_in.section,
self.agents.section,
self.skills.section,
self.runtime.section,
self.user.section,
self.conversation.section,
]
.into_iter()
@@ -340,7 +332,7 @@ fn classify_developer_text(text: &str) -> (ContextSectionKind, String) {
let trimmed = text.trim_start();
if starts_with_tag(trimmed, SKILLS_INSTRUCTIONS_OPEN_TAG) {
return (
ContextSectionKind::Skills,
ContextSectionKind::User,
format!(
"Implicit skills catalog ({} skills)",
count_catalog_entries(trimmed)
@@ -349,13 +341,13 @@ fn classify_developer_text(text: &str) -> (ContextSectionKind, String) {
}
if starts_with_tag(trimmed, APPS_INSTRUCTIONS_OPEN_TAG) {
return (
ContextSectionKind::BuiltIn,
ContextSectionKind::User,
"Apps connector instructions".to_string(),
);
}
if starts_with_tag(trimmed, PLUGINS_INSTRUCTIONS_OPEN_TAG) {
return (
ContextSectionKind::BuiltIn,
ContextSectionKind::User,
format!(
"Plugin instructions ({} plugins)",
count_catalog_entries(trimmed)
@@ -387,7 +379,7 @@ fn classify_developer_text(text: &str) -> (ContextSectionKind, String) {
);
}
if starts_with_tag(trimmed, REALTIME_CONVERSATION_OPEN_TAG) {
return (ContextSectionKind::Runtime, "Realtime context".to_string());
return (ContextSectionKind::BuiltIn, "Realtime context".to_string());
}
(
ContextSectionKind::BuiltIn,
@@ -397,38 +389,38 @@ fn classify_developer_text(text: &str) -> (ContextSectionKind, String) {
fn classify_user_text(text: &str, phase: Option<&MessagePhase>) -> (ContextSectionKind, String) {
if AGENTS_MD_FRAGMENT.matches_text(text) {
return (ContextSectionKind::Agents, format_agents_label(text));
return (ContextSectionKind::User, format_agents_label(text));
}
if SKILL_FRAGMENT.matches_text(text) {
return (ContextSectionKind::Skills, format_skill_label(text));
return (ContextSectionKind::User, format_skill_label(text));
}
if ENVIRONMENT_CONTEXT_FRAGMENT.matches_text(text) {
return (
ContextSectionKind::Runtime,
ContextSectionKind::BuiltIn,
"Environment context".to_string(),
);
}
if USER_SHELL_COMMAND_FRAGMENT.matches_text(text) {
return (
ContextSectionKind::Runtime,
ContextSectionKind::BuiltIn,
"User shell command".to_string(),
);
}
if TURN_ABORTED_FRAGMENT.matches_text(text) {
return (
ContextSectionKind::Runtime,
ContextSectionKind::BuiltIn,
"Turn aborted marker".to_string(),
);
}
if SUBAGENT_NOTIFICATION_FRAGMENT.matches_text(text) {
return (
ContextSectionKind::Runtime,
ContextSectionKind::BuiltIn,
"Subagent notification".to_string(),
);
}
if parse_hook_prompt_fragment(text).is_some() {
return (
ContextSectionKind::Runtime,
ContextSectionKind::BuiltIn,
"Hook prompt context".to_string(),
);
}
@@ -568,10 +560,8 @@ fn section_for_message_role(role: &str) -> ContextSectionKind {
fn section_order(label: &str) -> usize {
match label {
"Built-in" => 0,
"AGENTS.md" => 1,
"Skills" => 2,
"Runtime context" => 3,
"Conversation" => 4,
"User" => 1,
"Conversation" => 2,
_ => usize::MAX,
}
}
@@ -580,9 +570,7 @@ impl ContextSectionKind {
fn label(self) -> &'static str {
match self {
ContextSectionKind::BuiltIn => "Built-in",
ContextSectionKind::Agents => "AGENTS.md",
ContextSectionKind::Skills => "Skills",
ContextSectionKind::Runtime => "Runtime context",
ContextSectionKind::User => "User",
ContextSectionKind::Conversation => "Conversation",
}
}
@@ -619,7 +607,7 @@ mod tests {
}
#[test]
fn groups_built_in_agents_skills_runtime_and_conversation_sections() {
fn groups_built_in_user_and_conversation_sections() {
let base_instructions = BaseInstructions {
text: "Base instructions".to_string(),
};
@@ -658,11 +646,9 @@ mod tests {
assert_eq!(
section_labels,
vec![
"AGENTS.md".to_string(),
"Built-in".to_string(),
"Conversation".to_string(),
"Runtime context".to_string(),
"Skills".to_string(),
"User".to_string(),
]
);
assert_eq!(
@@ -766,7 +752,7 @@ mod tests {
.iter()
.map(|(label, _)| label.clone())
.collect::<Vec<_>>(),
vec!["Built-in".to_string(), "Skills".to_string()]
vec!["Built-in".to_string(), "User".to_string()]
);
assert_eq!(
breakdown.total_tokens,
+2 -4
View File
@@ -298,9 +298,7 @@ fn section_marker(label: &str) -> Span<'static> {
fn section_style(label: &str) -> Style {
match label {
"Conversation" => Style::new().cyan().bold(),
"Skills" => Style::new().green().bold(),
"AGENTS.md" => Style::new().magenta().bold(),
"Runtime context" => Style::new().red().bold(),
"User" => Style::new().green().bold(),
"Built-in" => Style::new().fg(ratatui::style::Color::Yellow).bold(),
_ => Style::new(),
}
@@ -402,7 +400,7 @@ mod tests {
total_tokens: 20_000,
sections: vec![
ThreadContextWindowSection {
label: "Skills".to_string(),
label: "User".to_string(),
tokens: 12_000,
details: vec![ThreadContextWindowDetail {
label: "Implicit skills catalog (42 skills)".to_string(),
@@ -10,10 +10,10 @@ Context map
window▕████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▏
used ▕████████████████████████████████████████████▏
■ Skills 60.0%
■ User 60.0%
■ Conversation 40.0%
■ Skills ~12,000 tokens 60.0% of used
■ User ~12,000 tokens 60.0% of used
██████████ Implicit skills catalog (42 skills) (~12,000 tokens)
■ Conversation ~8,000 tokens 40.0% of used