What variant of Codex are you using?
CLI
What feature would you like to see?
Support first-class per-thread skill/plugin selection in app-server thread/start
Summary
App-server hosts that run multiple independent Codex agent sessions need a way to choose different skill/plugin sets for each thread/start request without mutating the user's global/project config.toml and without running each agent type under a separate CODEX_HOME.
Current main has useful partial mechanisms, but none provide positive per-thread selection of installed plugins/skills by logical identity, nor a clear replace/restrict mode for the user's existing capability set.
Checked against local main at beca198b8a (telemetry: log structured direct tool-call timing (#30334)).
Why this matters
Some hosts launch many Codex sessions for different task types:
- a code-review agent with review-only skills/plugins
- a planning agent with project-management integrations
- a support/debugging agent with log or incident-response integrations
- an internal automation agent with only a narrow allowlist of tools
The host should be able to start each thread with the intended capabilities. It should not need to rewrite a user's config.toml, ask the user to toggle plugins globally, or create one synthetic CODEX_HOME per agent type just to isolate plugin/skill loading.
Current mechanisms and gaps
thread/start.selectedCapabilityRoots
selectedCapabilityRoots is an experimental field for environment-owned capability roots. The current shape is SelectedCapabilityRoot { id, location }, and CapabilityRootLocation only has Environment { environmentId, path }. The roots are stored in thread extension data and SessionMeta; current code preserves them across resume/fork and copies them for spawned child threads. The README now documents the basic behavior for environment-owned plugin or standalone-skill roots.
This is useful, but it is still path/root based and additive. The host must know an environment-native absolute path, there is no local-only or installed-capability locator, and there is no way to reference an installed plugin by marketplace/plugin ID or a skill by logical name. It also cannot replace or restrict the user-config-derived skill/plugin set.
thread/start.config
thread/start.config is loaded as a ConfigLayerSource::SessionFlags layer. That gives hosts some per-thread control:
features.plugins = false disables all plugin loading for that thread.
[mcp_servers.*] can add or override MCP servers for that thread.
[[skills.config]] name/path rules can disable specific skills for that thread, because skill_config_rules_from_stack reads SessionFlags.
The plugin path is asymmetric: [plugins.*] state from SessionFlags is ignored because configured_plugins_from_stack goes through project_effective_user_config() and effective_user_config(), which only merge ConfigLayerSource::User layers. Project .codex/config.toml does not provide a project-scoped plugin profile either; it can disable all plugins with features.plugins=false, but it cannot replace the user's enabled plugin set. Skill roots have a similar limitation for adding new roots: they are derived from system/user/project config folders, and SessionFlags has no config folder.
Other related fields
thread/start.environments can make environment-owned selected roots available or unavailable, but it is not a selector for user-installed skills/plugins. thread/start.dynamic_tools adds host-defined tools for one thread, but does not restrict existing skills/plugins. permissions selects permission profiles, not skill/plugin capability sets. ThreadSettingsUpdateParams can update runtime settings after start, but cannot alter the thread's capability selection.
Combined, a host can currently turn all plugins off, add ad-hoc MCP servers, and selectively disable named skills. It cannot positively select or replace a per-thread set of installed skills/plugins by logical identity.
Request
Please either document/stabilize the existing mechanisms as the intended app-server answer for per-thread skill/plugin selection, or add a first-class thread/start API for selecting capabilities by logical identity.
Option A: Clarify or extend selectedCapabilityRoots
I've been waiting for an official surface for this kind of per-thread capability selection. Seeing selectedCapabilityRoots was encouraging, but its current shape seems focused on environment-owned roots rather than the installed skill/plugin selection use case described here.
If selectedCapabilityRoots is intended to become the official per-thread capability-selection surface, it would help to:
- add a local or installed-capability locator, so hosts do not have to express every root as an environment path
- clarify that today's behavior is additive, and add a mode that can restrict/replace the user-config-derived skill/plugin set
- support referencing installed plugins/skills by logical ID instead of only absolute paths
- document lifecycle behavior for resume/fork/spawn, trust/ownership/policy validation, and the experimental-gate stability plan
If selectedCapabilityRoots is only meant for environment-owned capability paths, please document that boundary explicitly so hosts do not treat it as a general capability profile API.
Option B: Add logical per-thread capability selection
For installed plugins/skills, a higher-level request field could select by logical IDs or named profiles:
{
"method": "thread/start",
"params": {
"capabilitySelection": {
"mode": "replace",
"plugins": ["github@openai", "linear@internal"],
"skills": ["repo-review", "incident-debugging"],
"mcpServers": ["github", "linear"]
}
}
}
This would let the app-server host choose a capability profile per agent session while Codex still owns installation lookup, policy checks, config precedence, and environment resolution.
This is the direction I am currently implementing locally because mutating config.toml cannot safely support concurrent agents with different capability sets. It would be much better for app-server to expose this as a native, documented contract.
Option C: Extend SessionFlags to cover [plugins.*]
A narrower fix would be to let configured_plugins_from_stack include the SessionFlags layer for [plugins.*] enable/disable state, similar to how skill_config_rules_from_stack already handles SessionFlags. Then a host could pass [plugins.foo] enabled = false per thread the same way it can disable skills today.
Desired behavior
An app-server host can start two threads against the same Codex server and same user CODEX_HOME, but with different effective capability sets:
thread/start for agent A enables/selects only review-related skills/plugins.
thread/start for agent B enables/selects only planning-related skills/plugins.
- The selection is persisted with the thread and preserved across resume/fork/spawn according to documented rules.
- The user's global installed/enabled plugin state is not mutated.
- The host does not need to create one
CODEX_HOME per agent type.
Related issues
These look adjacent but not exact duplicates:
This issue is specifically about app-server thread/start choosing different skill/plugin capability sets per new agent thread.
Additional information
No response
What variant of Codex are you using?
CLI
What feature would you like to see?
Support first-class per-thread skill/plugin selection in app-server
thread/startSummary
App-server hosts that run multiple independent Codex agent sessions need a way to choose different skill/plugin sets for each
thread/startrequest without mutating the user's global/projectconfig.tomland without running each agent type under a separateCODEX_HOME.Current
mainhas useful partial mechanisms, but none provide positive per-thread selection of installed plugins/skills by logical identity, nor a clear replace/restrict mode for the user's existing capability set.Checked against local
mainatbeca198b8a(telemetry: log structured direct tool-call timing (#30334)).Why this matters
Some hosts launch many Codex sessions for different task types:
The host should be able to start each thread with the intended capabilities. It should not need to rewrite a user's
config.toml, ask the user to toggle plugins globally, or create one syntheticCODEX_HOMEper agent type just to isolate plugin/skill loading.Current mechanisms and gaps
thread/start.selectedCapabilityRootsselectedCapabilityRootsis an experimental field for environment-owned capability roots. The current shape isSelectedCapabilityRoot { id, location }, andCapabilityRootLocationonly hasEnvironment { environmentId, path }. The roots are stored in thread extension data andSessionMeta; current code preserves them across resume/fork and copies them for spawned child threads. The README now documents the basic behavior for environment-owned plugin or standalone-skill roots.This is useful, but it is still path/root based and additive. The host must know an environment-native absolute path, there is no local-only or installed-capability locator, and there is no way to reference an installed plugin by marketplace/plugin ID or a skill by logical name. It also cannot replace or restrict the user-config-derived skill/plugin set.
thread/start.configthread/start.configis loaded as aConfigLayerSource::SessionFlagslayer. That gives hosts some per-thread control:features.plugins = falsedisables all plugin loading for that thread.[mcp_servers.*]can add or override MCP servers for that thread.[[skills.config]]name/path rules can disable specific skills for that thread, becauseskill_config_rules_from_stackreadsSessionFlags.The plugin path is asymmetric:
[plugins.*]state fromSessionFlagsis ignored becauseconfigured_plugins_from_stackgoes throughproject_effective_user_config()andeffective_user_config(), which only mergeConfigLayerSource::Userlayers. Project.codex/config.tomldoes not provide a project-scoped plugin profile either; it can disable all plugins withfeatures.plugins=false, but it cannot replace the user's enabled plugin set. Skill roots have a similar limitation for adding new roots: they are derived from system/user/project config folders, andSessionFlagshas no config folder.Other related fields
thread/start.environmentscan make environment-owned selected roots available or unavailable, but it is not a selector for user-installed skills/plugins.thread/start.dynamic_toolsadds host-defined tools for one thread, but does not restrict existing skills/plugins.permissionsselects permission profiles, not skill/plugin capability sets.ThreadSettingsUpdateParamscan update runtime settings after start, but cannot alter the thread's capability selection.Combined, a host can currently turn all plugins off, add ad-hoc MCP servers, and selectively disable named skills. It cannot positively select or replace a per-thread set of installed skills/plugins by logical identity.
Request
Please either document/stabilize the existing mechanisms as the intended app-server answer for per-thread skill/plugin selection, or add a first-class
thread/startAPI for selecting capabilities by logical identity.Option A: Clarify or extend
selectedCapabilityRootsI've been waiting for an official surface for this kind of per-thread capability selection. Seeing
selectedCapabilityRootswas encouraging, but its current shape seems focused on environment-owned roots rather than the installed skill/plugin selection use case described here.If
selectedCapabilityRootsis intended to become the official per-thread capability-selection surface, it would help to:If
selectedCapabilityRootsis only meant for environment-owned capability paths, please document that boundary explicitly so hosts do not treat it as a general capability profile API.Option B: Add logical per-thread capability selection
For installed plugins/skills, a higher-level request field could select by logical IDs or named profiles:
{ "method": "thread/start", "params": { "capabilitySelection": { "mode": "replace", "plugins": ["github@openai", "linear@internal"], "skills": ["repo-review", "incident-debugging"], "mcpServers": ["github", "linear"] } } }This would let the app-server host choose a capability profile per agent session while Codex still owns installation lookup, policy checks, config precedence, and environment resolution.
This is the direction I am currently implementing locally because mutating
config.tomlcannot safely support concurrent agents with different capability sets. It would be much better for app-server to expose this as a native, documented contract.Option C: Extend
SessionFlagsto cover[plugins.*]A narrower fix would be to let
configured_plugins_from_stackinclude theSessionFlagslayer for[plugins.*]enable/disable state, similar to howskill_config_rules_from_stackalready handlesSessionFlags. Then a host could pass[plugins.foo] enabled = falseper thread the same way it can disable skills today.Desired behavior
An app-server host can start two threads against the same Codex server and same user
CODEX_HOME, but with different effective capability sets:thread/startfor agent A enables/selects only review-related skills/plugins.thread/startfor agent B enables/selects only planning-related skills/plugins.CODEX_HOMEper agent type.Related issues
These look adjacent but not exact duplicates:
This issue is specifically about app-server
thread/startchoosing different skill/plugin capability sets per new agent thread.Additional information
No response