Separate installed plugins from per-session skill metadata injection
Current evidence (updated July 21, 2026)
- Independently reproduced by multiple users on macOS.
- Confirmed on Codex CLI
0.128.0and0.144.6. - One independent reproduction found 14 repo-local
SKILL.mdfiles versus 83SKILL.mdfiles in the installed plugin cache. - That reproduction also found skills associated with a plugin configured as
enabled = falsestill present in the injected Available Skills list, overlapping with #17588. - Increasing the metadata budget (#19679) would reduce truncation, but would not separate installed plugin availability from default per-session injection.
- Fresh CLI
0.144.6Profile V2 A/B with the installeddocuments@openai-primary-runtimeplugin produceddocuments:documentsin both ON runs and removed it in both OFF runs; the first paired prompt delta was exactly 453 characters. - Profile V2 therefore provides a partial binary per-invocation control.
-c plugins.…enabled=…did not change the effective plugin state in inverse controls, and anon_demandmode that preserves explicit invocation is still missing.
What problem does this solve?
Codex currently treats enabled plugins/skills as part of the model-visible skills metadata surface for every new session. With several official plugins enabled, this can quickly hit the fixed skills metadata budget and produce warnings like:
Skill descriptions were shortened to fit the 2% skills context budget. Codex can still see every skill, but some descriptions are shorter. Disable unused skills or plugins to leave more room for the rest.
At the time of the original 0.128.0 report, the practical workaround was to disable plugins globally in config.toml. Codex CLI 0.144.6 now has Profile V2 overlays that partially improve this: a user can pre-create a profile that disables a plugin for one invocation while keeping it installed and enabled in the base configuration. This remains binary and file-based rather than an on-demand injection policy.
Why this is not only a budget-size issue
Related issues already cover important parts of this:
- #19679 asks for the hardcoded 2% budget to be configurable.
- #19090 covers truncation effects in the injected
Available skillsmetadata. - #19957 covers long first-party plugin skill descriptions.
Those would help, but there is a separate product/control-surface issue: plugin installation/enabling is currently too tightly coupled to per-session prompt injection.
For example, a user may want these different modes:
- keep a document/PPT/spreadsheet plugin installed, but not inject its skills into every normal coding session;
- start a dedicated session with those plugin skills fully injected;
- keep Browser Use or Computer Use always available;
- avoid duplicate or near-duplicate skill metadata across plugin/local/project sources;
- prioritize repo-local skills over low-frequency plugin skills when metadata budget is tight.
On current CLI, a pre-created Profile V2 overlay can switch binary plugin sets per invocation. What remains missing is a direct on_demand / manual state that keeps plugin skills explicitly available without injecting their metadata by default, plus equivalent session controls in the Codex App UI.
Requested behavior
Please consider separating plugin/skill availability from default per-session skills metadata injection. Possible designs:
- per-session or profile-based plugin sets, e.g.
default,docs,browser,research; - a config mode per plugin/skill such as
inject = "always" | "on_demand" | "off"while keeping the plugin installed; - lazy loading: inject a compact plugin/skill manifest first, then load detailed routing metadata only when the user invokes or references that capability;
- priority/pinning rules so repo-local or user-pinned skills are preserved before low-frequency plugin skills;
- automatic deduplication of identical or overlapping skill metadata across plugin/local/project roots before applying the budget;
- Codex App UI controls to start a new session with selected plugins instead of editing
config.tomland restarting.
Expected result
Users should be able to keep official plugins installed without each one consuming skills metadata budget in every session. The default coding session stays compact, while specialized plugin-heavy sessions remain easy to start intentionally.
Environment
- Codex CLI:
codex-cli 0.128.0 - Platform: macOS
- Scenario: multiple official plugins enabled via
~/.codex/config.toml, fresh session shows the 2% skills context budget warning.
8 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Reviewed the duplicate suggestions from the bot.
skills.configfiltering not applying; useful and related, but it still assumes filtering/disablement as the control surface.So I think this issue is not a duplicate of those. The specific request here is to separate plugin installation/availability from default prompt injection, with profile/lazy-load/priority/dedupe controls so users can keep plugins installed without globally disabling them or paying the full metadata cost every session.
I took a closer look at the current implementation path and I think the fix can be scoped more narrowly than a full skill-management redesign.
Implementation roadmap
1. Separate plugin skill availability from default injection
The current coupling appears to be:
effective_plugin_skill_roots()SkillLoadOutcomebuild_available_skills()renders all enabled implicit skills into the default<skills_instructions>block, subject only to the metadata budgetA minimal control surface could be a per-plugin mode, for example:
Suggested semantics:
always: current behavior. This should be the default for backward compatibility.on_demand: plugin remains installed/enabled; MCP servers, apps, hooks, and plugin summaries remain available, but this plugin's skills are excluded from the default<skills_instructions>block. They become available only when the user explicitly mentions the plugin or a specific skill.off: plugin remains available for non-skill capabilities, but this plugin's skills are excluded from both default and explicit skill injection.The important part is that plugin installation/enabling should no longer automatically imply default skill metadata injection in every session.
2. Split skill roots by purpose
Instead of treating all active plugin skill roots as one set, the loader/session path could distinguish:
For
always, a plugin contributes to both sets.For
on_demand, a plugin contributes only to explicit availability.For
off, a plugin contributes to neither skill set, while preserving non-skill plugin capabilities.This keeps the behavior compatible while avoiding global disablement as the only workaround.
3. Keep dedupe/priority as a complementary layer
Deduplication is related, but I do not think it is sufficient as the primary fix.
Codex already appears to have root/path-level dedupe for skill discovery and explicit mention handling. What is missing is higher-level dedupe/priority, such as avoiding semantically overlapping plugin skills or preferring repo-local/user-pinned skills when the metadata budget is tight.
That would help, but even perfect dedupe would not solve the core case where a user has many distinct plugin skills installed. Those should still not all be injected into every default coding session.
So I would treat priority/dedupe as a follow-up layer after the availability-vs-default-injection split.
4. Tests I would expect
skill_injectiontoalwaysalwayspreserves current plugin skill injection behavioron_demandexcludes plugin skills from default<skills_instructions>on_demandplugin skills available for that turnoffkeeps the plugin enabled for non-skill capabilities while excluding its skillsskills.configper-skill disabling still wins where applicableThis seems like a focused first step toward profile/lazy-load/priority controls without requiring the whole UX to be designed at once.
Does this match the direction the team would consider? I understand unsolicited PRs are not generally reviewed, but if this approach aligns with the intended solution, I would be happy to prepare a focused PR after maintainer confirmation.
Thanks for opening this. I ran into the same underlying issue while looking at skills/plugins behavior: plugin enablement currently feels coupled to default skill metadata injection.
A small MVP that might address this without changing current behavior by default:
alwaysas the default/current behavior: enabled plugin skills are included in default skill metadata and available by explicit mention.on_demand: the plugin remains enabled and non-skill capabilities remain available, but its skills are omitted from default skill metadata and only loaded when explicitly mentioned.off: the plugin remains enabled for non-skill capabilities, but its skills are excluded from both default metadata and explicit skill injection.This would let users keep plugins installed/enabled without spending prompt budget on every skill description in every session.
Related signals seem to point to the same underlying need:
Possible MVP validation:
on_demandandoff.on_demandplugin skills are omitted from default<skills_instructions>.on_demandskills.offexcludes plugin skills from both default and explicit skill injection.[[skills.config]]behavior still wins for per-skill enable/disable rules.The main design question is probably naming and surface area: whether this should be expressed as something like
skill_injection = "always" | "on_demand" | "off"on plugin config, or through a more general plugin visibility/control setting.Additional reproduction/evidence from macOS with
codex-cli 0.144.6:SKILL.mdfiles, while the installed plugin cache contains 83SKILL.mdfiles across plugin versions/bundles.openai-developers@openai-curated, contributes 5 skills and roughly 1,800 characters of description metadata by itself.~/.codex/config.tomlalready hasplugins."gmail@openai-curated".enabled = false, but Gmail plugin skills were still present in the session's injected Available Skills list. Several apps are also configuredenabled = falsewhile related skill metadata remains visible. This appears to overlap with the disable/config behavior reported in #17588.SKILL.md; that would not materially address the warning.The practical next test is to disable one low-frequency plugin globally, fully restart Codex, and compare the fresh session's injected skill list. Even if that works, it leaves the core problem described here: globally disabling and later remembering to re-enable a plugin is not a reasonable substitute for keeping it installed but loading/injecting it on demand.
A per-session/profile plugin set or
inject = "always" | "on_demand" | "off"control would address the actual leverage point.Thanks @gwh2002 — the disabled-plugin observation is especially useful.
This appears to expose two related layers:
For anyone else affected, please react with 👍 on the issue body and share:
Comparable reproductions across environments would help establish the impact. I have added the current evidence to the issue description.
Fresh CLI 0.144.6 A/B: Profile V2 is a partial solution
I ran a fresh-session A/B on macOS using the installed
documents@openai-primary-runtimeplugin and its uniquedocuments:documentsskill.I used
codex debug prompt-input, which renders the actual model-visible prompt without sending a model request. Both temporary profiles disabled the same 105 non-plugin user skills for the test process only, so the target plugin skill was not hidden by the existing metadata-budget pressure. The only treatment was:Run order was ON / OFF / OFF / ON:
| Arm | Target skill count | Skills entries |
<skills_instructions>chars ||---|---:|---:|---:|
| Profile ON 1 | 1 | 23 | 10,631 |
| Profile OFF 1 | 0 | 22 | 10,178 |
| Profile OFF 2 | 0 | 22 | 10,178 |
| Profile ON 2 | 1 | 18 | 8,271 |
All runs exited 0 with empty stderr. In the first paired diff, the only skill-line difference was
documents:documents; disabling the plugin removed 453 characters (the 452-character entry plus its newline). The second ON run had unrelated catalog variance, but target presence remained deterministic across both repetitions.I also ran inverse session-flag controls:
-c plugins."documents@openai-primary-runtime".enabled=false→ target still present.-c plugins."documents@openai-primary-runtime".enabled=true→ target still absent.This matches the current implementation: Profile V2 is merged into the effective user configuration, while plugin configuration is read from that user/profile view, not the generic session-flag layer.
My updated interpretation:
on_demand: the disabled-plugin loader returns before loading skills, MCP servers, or apps. It cannot keep non-skill capabilities available or preserve explicit skill invocation.allow_implicit_invocation = falsebehavior as a user/profile override for plugin-owned skills, plus App session/profile controls.@etraut-openai, given your notes on the profile replacement in #17588 and explicit-only skills in #22692, could you confirm whether Profile V2 plus an override for implicit invocation is the intended direction for the remaining gap here?
my use case is I want to be able to install plugins and keep their apps, connectors, MCP tools, and skills available without injecting every plugin skill’s routing description into every task even unrelated ones.
It would help to separate these states explicitly:
A policy such as the following could express that separation:
always: inject the skill metadata into every applicable task.on_demand: keep the skill available, but advertise/inject it only after explicit mention, plugin selection, or routing.off: do not expose the skill.Project-, profile-, or task-level selection would also help. For example, a GitHub-heavy task could advertise GitHub skills while an unrelated local Rust task would not pay their metadata cost merely because the GitHub plugin remains installed.
#31088 is related on the observability side: whichever skills are selected should be reported as the effective per-turn catalog, along with the resulting budget usage. That would make installed, available, injected, shortened, and omitted capabilities distinguishable.