Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
271 changes: 271 additions & 0 deletions artifacts/github/bundles/openai-codex-pr-27443.json

Large diffs are not rendered by default.

142 changes: 142 additions & 0 deletions artifacts/github/bundles/openai-codex-pr-27488.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
{
"analysis_mode": "pr_first",
"commits": [
{
"author": "pakrym-oai",
"committed_at": "2026-06-10T18:54:24Z",
"message": "Add token budget context feature",
"sha": "a19ec71e8d73d4dbd1dfb5b34894744ee8e6ef1a",
"url": "https://github.com/openai/codex/commit/a19ec71e8d73d4dbd1dfb5b34894744ee8e6ef1a"
},
{
"author": "pakrym-oai",
"committed_at": "2026-06-10T20:33:42Z",
"message": "codex: address token budget review feedback (#27438)",
"sha": "67a29aab4885be483dc7ea05ec9bce8fddbfdc00",
"url": "https://github.com/openai/codex/commit/67a29aab4885be483dc7ea05ec9bce8fddbfdc00"
},
{
"author": "pakrym-oai",
"committed_at": "2026-06-10T20:42:56Z",
"message": "codex: update token budget context wording",
"sha": "9e25474a95a9ceb092c04ac628cbb6985f7f30eb",
"url": "https://github.com/openai/codex/commit/9e25474a95a9ceb092c04ac628cbb6985f7f30eb"
},
{
"author": "pakrym-oai",
"committed_at": "2026-06-10T22:58:44Z",
"message": "codex: add new context window tool",
"sha": "812afdf99973b744cc03839a08128a536df58e4c",
"url": "https://github.com/openai/codex/commit/812afdf99973b744cc03839a08128a536df58e4c"
},
{
"author": "pakrym-oai",
"committed_at": "2026-06-11T03:25:47Z",
"message": "Merge remote-tracking branch 'origin/main' into pakrym/token-budget-window-tool",
"sha": "57885a18cccf9db57602e1240e553aaf1df92da9",
"url": "https://github.com/openai/codex/commit/57885a18cccf9db57602e1240e553aaf1df92da9"
}
],
"default_branch": "main",
"docs_refs": [],
"examples_refs": [],
"extracted_flags": [
"NEW_CONTEXT_WINDOW_TOOL_NAME",
"NEW_CONTEXT_WINDOW_MESSAGE",
"PERMISSIONS_INSTRUCTIONS",
"SKILLS_INSTRUCTIONS",
"ENVIRONMENT_CONTEXT",
"CWD",
"CONFIGURED_CONTEXT_WINDOW",
"EFFECTIVE_CONTEXT_WINDOW"
],
"files": [
{
"additions": 38,
"deletions": 0,
"patch_excerpt": "@@ -3058,6 +3058,44 @@ impl Session {\n state.advance_auto_compact_window_id()\n }\n \n+ pub(crate) async fn request_new_context_window(&self) {\n+ let mut state = self.state.lock().await;\n+ state.request_new_context_window();\n+ }\n+\n+ pub(crate) async fn maybe_start_new_context_window(\n+ &self,\n+ turn_context: &TurnContext,\n+ ) -> Option<u64> {\n+ let window_id = {\n+ let mut state = self.state.lock().await;\n+ state.start_new_context_window_if_requested()\n+ };\n+ let window_id = window_id?;\n+ let context_items = self.build_initial_context(turn_context).await;\n+ let turn_context_item = turn_context.to_turn_context_item();\n+ let replacement_history = context_items;\n+ {\n+ let mut state = self.state.lock().await;\n+ state.replace_history(replacement_history....",
"path": "codex-rs/core/src/session/mod.rs",
"status": "modified"
},
{
"additions": 9,
"deletions": 0,
"patch_excerpt": "@@ -285,6 +285,15 @@ pub(crate) async fn run_turn(\n )\n .await;\n \n+ let started_new_context_window = sess\n+ .maybe_start_new_context_window(turn_context.as_ref())\n+ .await\n+ .is_some();\n+ if started_new_context_window && needs_follow_up {\n+ can_drain_pending_input = !model_needs_follow_up;\n+ continue;\n+ }\n+\n // as long as compaction works well in getting us way below the token limit, we shouldn't worry about being in an infinite loop.\n if token_limit_reached && needs_follow_up {\n if let Err(err) = run_auto_compact(",
"path": "codex-rs/core/src/session/turn.rs",
"status": "modified"
},
{
"additions": 18,
"deletions": 0,
"patch_excerpt": "@@ -14,6 +14,7 @@ enum AutoCompactWindowPrefill {\n #[derive(Debug, Clone, Copy, PartialEq, Eq)]\n pub(super) struct AutoCompactWindow {\n window_id: u64,\n+ new_context_window_requested: bool,\n /// Absolute input-token baseline for the current compaction window.\n ///\n /// `body_after_prefix` subtracts this from later active-context usage. It is\n@@ -26,6 +27,7 @@ impl AutoCompactWindow {\n pub(super) fn new() -> Self {\n Self {\n window_id: 0,\n+ new_context_window_requested: false,\n prefill_input_tokens: None,\n }\n }\n@@ -44,9 +46,20 @@ impl AutoCompactWindow {\n \n pub(super) fn advance_window_id(&mut self) -> u64 {\n self.window_id = self.window_id.saturating_add(1);\n+ self.new_context_window_requested = false;\n self.window_id\n }\n \n+ pub(super) fn request_new_context_window(&mut self) {\n...",
"path": "codex-rs/core/src/state/auto_compact_window.rs",
"status": "modified"
},
{
"additions": 14,
"deletions": 0,
"patch_excerpt": "@@ -156,6 +156,20 @@ impl SessionState {\n self.auto_compact_window.advance_window_id()\n }\n \n+ pub(crate) fn request_new_context_window(&mut self) {\n+ self.auto_compact_window.request_new_context_window();\n+ }\n+\n+ pub(crate) fn start_new_context_window_if_requested(&mut self) -> Option<u64> {\n+ if !self.auto_compact_window.take_new_context_window_request() {\n+ return None;\n+ }\n+\n+ let window_id = self.auto_compact_window.advance_window_id();\n+ self.auto_compact_window.clear_prefill();\n+ Some(window_id)\n+ }\n+\n pub(crate) fn token_info(&self) -> Option<TokenUsageInfo> {\n self.history.token_info()\n }",
"path": "codex-rs/core/src/state/session.rs",
"status": "modified"
},
{
"additions": 3,
"deletions": 0,
"patch_excerpt": "@@ -13,6 +13,8 @@ pub(crate) mod multi_agents;\n pub(crate) mod multi_agents_common;\n pub(crate) mod multi_agents_spec;\n pub(crate) mod multi_agents_v2;\n+mod new_context_window;\n+pub(crate) mod new_context_window_spec;\n mod plan;\n pub(crate) mod plan_spec;\n mod request_permissions;\n@@ -56,6 +58,7 @@ pub use mcp::McpHandler;\n pub use mcp_resource::ListMcpResourceTemplatesHandler;\n pub use mcp_resource::ListMcpResourcesHandler;\n pub use mcp_resource::ReadMcpResourceHandler;\n+pub use new_context_window::NewContextWindowHandler;\n pub use plan::PlanHandler;\n pub use request_permissions::RequestPermissionsHandler;\n pub use request_plugin_install::RequestPluginInstallHandler;",
"path": "codex-rs/core/src/tools/handlers/mod.rs",
"status": "modified"
},
{
"additions": 45,
"deletions": 0,
"patch_excerpt": "@@ -0,0 +1,45 @@\n+use crate::function_tool::FunctionCallError;\n+use crate::tools::context::FunctionToolOutput;\n+use crate::tools::context::ToolInvocation;\n+use crate::tools::context::ToolPayload;\n+use crate::tools::context::boxed_tool_output;\n+use crate::tools::handlers::new_context_window_spec::NEW_CONTEXT_WINDOW_TOOL_NAME;\n+use crate::tools::handlers::new_context_window_spec::create_new_context_window_tool;\n+use crate::tools::registry::CoreToolRuntime;\n+use crate::tools::registry::ToolExecutor;\n+use codex_tools::ToolName;\n+use codex_tools::ToolSpec;\n+\n+pub(crate) const NEW_CONTEXT_WINDOW_MESSAGE: &str =\n+ \"A new context window will start without summarizing conversation history.\";\n+\n+pub struct NewContextWindowHandler;\n+\n+impl ToolExecutor<ToolInvocation> for NewContextWindowHandler {\n+ fn tool_name(&self) -> ToolName {\n+ ToolName::plain(NEW_CONTEXT_WINDOW_TOOL_NAME)\n+ ...",
"path": "codex-rs/core/src/tools/handlers/new_context_window.rs",
"status": "added"
},
{
"additions": 17,
"deletions": 0,
"patch_excerpt": "@@ -0,0 +1,17 @@\n+use codex_tools::JsonSchema;\n+use codex_tools::ResponsesApiTool;\n+use codex_tools::ToolSpec;\n+use std::collections::BTreeMap;\n+\n+pub(crate) const NEW_CONTEXT_WINDOW_TOOL_NAME: &str = \"new_context\";\n+\n+pub fn create_new_context_window_tool() -> ToolSpec {\n+ ToolSpec::Function(ResponsesApiTool {\n+ name: NEW_CONTEXT_WINDOW_TOOL_NAME.to_string(),\n+ description: \"Start a new context window.\".to_string(),\n+ strict: false,\n+ defer_loading: None,\n+ parameters: JsonSchema::object(BTreeMap::new(), /*required*/ None, Some(false.into())),\n+ output_schema: None,\n+ })\n+}",
"path": "codex-rs/core/src/tools/handlers/new_context_window_spec.rs",
"status": "added"
},
{
"additions": 5,
"deletions": 0,
"patch_excerpt": "@@ -13,6 +13,7 @@ use crate::tools::handlers::ListAvailablePluginsToInstallHandler;\n use crate::tools::handlers::ListMcpResourceTemplatesHandler;\n use crate::tools::handlers::ListMcpResourcesHandler;\n use crate::tools::handlers::McpHandler;\n+use crate::tools::handlers::NewContextWindowHandler;\n use crate::tools::handlers::PlanHandler;\n use crate::tools::handlers::ReadMcpResourceHandler;\n use crate::tools::handlers::RequestPermissionsHandler;\n@@ -659,6 +660,10 @@ fn add_core_utility_tools(context: &CoreToolPlanContext<'_>, planned_tools: &mut\n planned_tools.add(RequestPermissionsHandler);\n }\n \n+ if features.enabled(Feature::TokenBudget) {\n+ planned_tools.add_with_exposure(NewContextWindowHandler, ToolExposure::DirectModelOnly);\n+ }\n+\n if tool_suggest_enabled(turn_context)\n && let Some(discoverable_tools) =\n context.discoverable_tools.filter...",
"path": "codex-rs/core/src/tools/spec_plan.rs",
"status": "modified"
},
{
"additions": 15,
"deletions": 0,
"patch_excerpt": "@@ -0,0 +1,15 @@\n+---\n+source: core/tests/suite/token_budget.rs\n+assertion_line: 300\n+expression: \"context_snapshot::format_labeled_requests_snapshot(\\\"New context window tool installs fresh full context before the next follow-up request.\\\",\\n&[(\\\"Final Follow-Up Request\\\", &requests[2])],\\n&ContextSnapshotOptions::default(),)\"\n+---\n+Scenario: New context window tool installs fresh full context before the next follow-up request.\n+\n+## Final Follow-Up Request\n+00:message/developer[3]:\n+ [01] <PERMISSIONS_INSTRUCTIONS>\n+ [02] <SKILLS_INSTRUCTIONS>\n+ [03] <token_budget>\\nCurrent context window 1.\\nYou have 121600 tokens left in this context window.\\n</token_budget>\n+01:message/user:<ENVIRONMENT_CONTEXT:cwd=<CWD>>\n+02:function_call/update_plan\n+03:function_call_output:Plan updated",
"path": "codex-rs/core/tests/suite/snapshots/all__suite__token_budget__token_budget_new_context_window_tool_full_context.snap",
"status": "added"
},
{
"additions": 97,
"deletions": 0,
"patch_excerpt": "@@ -4,10 +4,13 @@ use codex_model_provider_info::built_in_model_providers;\n use codex_protocol::protocol::EventMsg;\n use codex_protocol::protocol::Op;\n use core_test_support::PathBufExt;\n+use core_test_support::context_snapshot;\n+use core_test_support::context_snapshot::ContextSnapshotOptions;\n use core_test_support::responses::ResponsesRequest;\n use core_test_support::responses::ev_assistant_message;\n use core_test_support::responses::ev_completed;\n use core_test_support::responses::ev_completed_with_tokens;\n+use core_test_support::responses::ev_function_call;\n use core_test_support::responses::ev_response_created;\n use core_test_support::responses::mount_sse_sequence;\n use core_test_support::responses::sse;\n@@ -17,6 +20,8 @@ use core_test_support::test_codex::local;\n use core_test_support::test_codex::test_codex;\n use core_test_support::wait_for_event;\n use pretty_assertions::assert_eq...",
"path": "codex-rs/core/tests/suite/token_budget.rs",
"status": "modified"
}
],
"linked_issues": [
"#27438"
],
"notes": [
"Built from GitHub pull-request, commits, files, and repo endpoints."
],
"primary_pr": {
"body": "## Why\n\nThe token budget feature tells the model how much room remains in the current context window. When the model decides the current window is no longer useful, it needs a way to ask Codex to start over with a fresh context window without spending tokens on a compaction summary.\n\nThis PR adds that model-requestable escape hatch on top of #27438.\n\n## What changed\n\n- Added a direct-model-only `new_context` tool behind `Feature::TokenBudget`.\n- Stores the tool request on `AutoCompactWindow` and consumes it after sampling so the next follow-up request in the same turn starts in the new window.\n- Starts the new window as a no-summary compaction checkpoint that contains only fresh initial context, not preserved conversation history.\n- Keeps the new window aligned with token-budget startup context, including the `Current context window Z` message.\n- Added integration coverage and a snapshot showing the same-turn `new_context` flow into a fresh full-context follow-up request.\n\n## Validation\n\n- `just test -p codex-core token_budget`\n",
"labels": [],
"merged_at": "2026-06-11T03:39:08Z",
"number": 27488,
"state": "merged",
"title": "[codex] Add new context window tool",
"url": "https://github.com/openai/codex/pull/27488"
},
"repo": "openai/codex",
"schema": "github_change_bundle/v1"
}
68 changes: 68 additions & 0 deletions artifacts/github/bundles/openai-codex-pr-27517.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"analysis_mode": "pr_first",
"commits": [
{
"author": "xl-openai",
"committed_at": "2026-06-11T02:32:54Z",
"message": "Pass auth mode to plugin manager",
"sha": "66d506e86e5da8691b0773563eff7f73ecff20b9",
"url": "https://github.com/openai/codex/commit/66d506e86e5da8691b0773563eff7f73ecff20b9"
}
],
"default_branch": "main",
"docs_refs": [],
"examples_refs": [],
"extracted_flags": [
"MCP",
"API",
"CODEX_SANDBOX_NETWORK_DISABLED",
"CODEX_SANDBOX",
"CONFIG_TOML_FILE",
"MAX_CAPABILITY_SUMMARY_DESCRIPTION_LEN"
],
"files": [
{
"additions": 7,
"deletions": 0,
"patch_excerpt": "@@ -158,6 +158,9 @@ impl AccountRequestProcessor {\n \n pub(crate) fn clear_external_auth(&self) {\n self.auth_manager.clear_external_auth();\n+ self.thread_manager\n+ .plugins_manager()\n+ .set_auth_mode(self.auth_manager.get_api_auth_mode());\n }\n \n fn current_account_updated_notification(&self) -> AccountUpdatedNotification {\n@@ -173,6 +176,10 @@ impl AccountRequestProcessor {\n thread_manager: &Arc<ThreadManager>,\n auth: Option<CodexAuth>,\n ) {\n+ thread_manager\n+ .plugins_manager()\n+ .set_auth_mode(auth.as_ref().map(CodexAuth::api_auth_mode));\n+\n match config_manager\n .load_latest_config(/*fallback_cwd*/ None)\n .await",
"path": "codex-rs/app-server/src/request_processors/account_processor.rs",
"status": "modified"
},
{
"additions": 39,
"deletions": 5,
"patch_excerpt": "@@ -48,6 +48,7 @@ use crate::store::PluginInstallResult as StorePluginInstallResult;\n use crate::store::PluginStore;\n use crate::store::PluginStoreError;\n use codex_analytics::AnalyticsEventsClient;\n+use codex_app_server_protocol::AuthMode;\n use codex_config::ConfigLayerStack;\n use codex_config::clear_user_plugin;\n use codex_config::set_user_plugin_enabled;\n@@ -203,6 +204,13 @@ fn featured_plugin_ids_cache_key(\n }\n }\n \n+fn project_plugin_load_outcome_for_auth(\n+ outcome: PluginLoadOutcome,\n+ _auth_mode: Option<AuthMode>,\n+) -> PluginLoadOutcome {\n+ outcome\n+}\n+\n #[derive(Debug, Clone, PartialEq, Eq)]\n pub struct PluginInstallRequest {\n pub plugin_name: String,\n@@ -319,6 +327,7 @@ pub struct PluginsManager {\n remote_installed_plugins_cache_refresh_state: RwLock<RemoteInstalledPluginsCacheRefreshState>,\n global_remote_catalog_cache_refresh_state: RwLock<GlobalRemot...",
"path": "codex-rs/core-plugins/src/manager.rs",
"status": "modified"
},
{
"additions": 16,
"deletions": 0,
"patch_excerpt": "@@ -17,6 +17,7 @@ use crate::test_support::load_plugins_config as load_plugins_config_input;\n use crate::test_support::write_curated_plugin_sha_with as write_curated_plugin_sha;\n use crate::test_support::write_file;\n use crate::test_support::write_openai_curated_marketplace;\n+use codex_app_server_protocol::AuthMode;\n use codex_app_server_protocol::ConfigLayerSource;\n use codex_config::AppToolApproval;\n use codex_config::CONFIG_TOML_FILE;\n@@ -47,6 +48,21 @@ use wiremock::matchers::query_param;\n \n const MAX_CAPABILITY_SUMMARY_DESCRIPTION_LEN: usize = 1024;\n \n+#[test]\n+fn plugins_manager_tracks_auth_mode() {\n+ let tmp = TempDir::new().unwrap();\n+ let manager = PluginsManager::new(tmp.path().to_path_buf());\n+\n+ assert_eq!(manager.auth_mode(), None);\n+ assert!(manager.set_auth_mode(Some(AuthMode::ApiKey)));\n+ assert_eq!(manager.auth_mode(), Some(AuthMode::ApiKey));\n+ assert!...",
"path": "codex-rs/core-plugins/src/manager_tests.rs",
"status": "modified"
},
{
"additions": 2,
"deletions": 0,
"patch_excerpt": "@@ -272,6 +272,7 @@ impl ThreadManager {\n codex_home.to_path_buf(),\n restriction_product,\n ));\n+ plugins_manager.set_auth_mode(auth_manager.get_api_auth_mode());\n let mcp_manager = Arc::new(McpManager::new_with_extensions(\n Arc::clone(&plugins_manager),\n Arc::clone(&extensions),\n@@ -365,6 +366,7 @@ impl ThreadManager {\n codex_home.clone(),\n restriction_product,\n ));\n+ plugins_manager.set_auth_mode(auth_manager.get_api_auth_mode());\n let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager)));\n let skills_manager = Arc::new(SkillsManager::new_with_restriction_product(\n skills_codex_home,",
"path": "codex-rs/core/src/thread_manager.rs",
"status": "modified"
}
],
"linked_issues": [],
"notes": [
"Built from GitHub pull-request, commits, files, and repo endpoints."
],
"primary_pr": {
"body": "## Summary\n- Add auth mode state to `PluginsManager`.\n- Sync the plugin manager auth mode when `ThreadManager` is created and when account auth changes.\n- Route plugin load outcomes through an auth-aware projection hook so follow-up plugin filtering can stay inside `core-plugins`.\n\n## Motivation\nThis prepares plugin capability loading to be configured by auth mode, such as hiding or exposing app/MCP-backed plugin surfaces based on whether the user is using ChatGPT auth or API-key auth, without leaking those details outside the plugin manager.\n\n## Tests\n- `just fmt`\n- `just test -p codex-core-plugins`\n- `env -u CODEX_SANDBOX_NETWORK_DISABLED -u CODEX_SANDBOX just test -p codex-core thread_manager::tests`\n- `env -u CODEX_SANDBOX_NETWORK_DISABLED -u CODEX_SANDBOX just test -p codex-app-server`",
"labels": [],
"merged_at": "2026-06-11T03:57:35Z",
"number": 27517,
"state": "merged",
"title": "[codex] Pass auth mode to plugin manager",
"url": "https://github.com/openai/codex/pull/27517"
},
"repo": "openai/codex",
"schema": "github_change_bundle/v1"
}
Loading