Separate installed plugins from per-session skill metadata injection

Open 💬 8 comments Opened May 6, 2026 by Yan-Yang-PYZNTech
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Current evidence (updated July 21, 2026)

  • Independently reproduced by multiple users on macOS.
  • Confirmed on Codex CLI 0.128.0 and 0.144.6.
  • One independent reproduction found 14 repo-local SKILL.md files versus 83 SKILL.md files in the installed plugin cache.
  • That reproduction also found skills associated with a plugin configured as enabled = false still 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.6 Profile V2 A/B with the installed documents@openai-primary-runtime plugin produced documents:documents in 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 an on_demand mode 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 skills metadata.
  • #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.toml and 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.

View original on GitHub ↗

8 Comments

github-actions[bot] contributor · 3 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #19911
  • #20210
  • #19695

Powered by Codex Action

Yan-Yang-PYZNTech · 3 months ago

Reviewed the duplicate suggestions from the bot.

  • #19911 overlaps with the general desire for better skill management / picker UX, but this issue is narrower: installed plugins should not automatically consume per-session skill metadata budget in every session.
  • #20210 is about project-local skills.config filtering not applying; useful and related, but it still assumes filtering/disablement as the control surface.
  • #19695 is about explicit-only skill invocation and is already closed.

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.

Yan-Yang-PYZNTech · 3 months ago

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:

  • enabled/active plugin -> plugin skill roots are included in effective_plugin_skill_roots()
  • those roots are loaded into the session/turn SkillLoadOutcome
  • build_available_skills() renders all enabled implicit skills into the default <skills_instructions> block, subject only to the metadata budget

A minimal control surface could be a per-plugin mode, for example:

[plugins."example@marketplace"]
enabled = true
skill_injection = "always" # always | on_demand | off

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:

  • default-injected skill roots: used to build the normal session-visible skills list
  • explicitly available skill roots: available for explicit plugin/skill mention resolution

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
  • config parsing defaults skill_injection to always
  • always preserves current plugin skill injection behavior
  • on_demand excludes plugin skills from default <skills_instructions>
  • explicit plugin mention makes on_demand plugin skills available for that turn
  • off keeps the plugin enabled for non-skill capabilities while excluding its skills
  • existing skills.config per-skill disabling still wins where applicable

This 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.

SylvainWinning · 3 months ago

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:

  • Keep always as the default/current behavior: enabled plugin skills are included in default skill metadata and available by explicit mention.
  • Add 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.
  • Add 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:

  • #24299: hard skill metadata budget and mass description truncation
  • #24237: project-level control over inherited/global skills
  • #24145 and #24319: plugin/skill suggestions becoming noisy or unexpected

Possible MVP validation:

  • Config parsing tests for default/current behavior plus on_demand and off.
  • A test that on_demand plugin skills are omitted from default <skills_instructions>.
  • A test that explicit invocation can still load on_demand skills.
  • A test that off excludes plugin skills from both default and explicit skill injection.
  • A regression test that existing [[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.

gwh2002 · 1 month ago

Additional reproduction/evidence from macOS with codex-cli 0.144.6:

  • A new session showed the exact 2% skills-context warning quoted in this issue.
  • This is primarily plugin-driven in my setup, not project-skill-driven. The current project exposes 14 repo-local SKILL.md files, while the installed plugin cache contains 83 SKILL.md files across plugin versions/bundles.
  • One large enabled bundle, openai-developers@openai-curated, contributes 5 skills and roughly 1,800 characters of description metadata by itself.
  • More importantly, ~/.codex/config.toml already has plugins."gmail@openai-curated".enabled = false, but Gmail plugin skills were still present in the session's injected Available Skills list. Several apps are also configured enabled = false while related skill metadata remains visible. This appears to overlap with the disable/config behavior reported in #17588.
  • I initially considered pruning several rarely used repo-local skills, but their contribution is small and one suspected directory was only a README/provenance record with no 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.

Yan-Yang-PYZNTech · 1 month ago

Thanks @gwh2002 — the disabled-plugin observation is especially useful.

This appears to expose two related layers:

  1. Skills associated with plugins configured as disabled may still be injected, overlapping with #17588.
  2. Even after that behavior is fixed, installed plugin availability remains coupled to default per-session metadata injection, which is the control-surface problem tracked here.

For anyone else affected, please react with 👍 on the issue body and share:

  • Codex version and platform
  • number of enabled plugins and injected plugin skills
  • whether skills associated with disabled plugins still appear
  • whether the 2% metadata-budget warning appears in a fresh session

Comparable reproductions across environments would help establish the impact. I have added the current evidence to the issue description.

Yan-Yang-PYZNTech · 1 month ago

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-runtime plugin and its unique documents:documents skill.

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:

[plugins."documents@openai-primary-runtime"]
enabled = true # ON profile; false in OFF profile

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:

  • ON profile + -c plugins."documents@openai-primary-runtime".enabled=false → target still present.
  • OFF profile + -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:

  1. Profile V2 already solves the binary CLI subset of this request: keep a plugin installed/base-enabled, but exclude it from a selected fresh invocation.
  2. The earlier report of a disabled plugin remaining visible was not reproduced through a valid fresh CLI profile. It may be specific to base-config loading, the Codex App surface, or session/cache refresh behavior.
  3. Binary OFF is still not 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.
  4. The remaining minimal gap may be exposing the existing allow_implicit_invocation = false behavior 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?

TyceHerrman · 17 hours ago

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:

  1. Plugin is installed.
  2. Plugin capabilities are enabled and callable.
  3. Plugin skill metadata is advertised in this task’s model-visible catalog.

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.