A root plugin.json silently disables all of a plugin's hooks
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
A root plugin.json silently disables all of a plugin's hooks
What happens
When a plugin directory contains a root plugin.json (the
Agent Plugins 1.0.0 manifest), Codex loads it through
the Agent Plugins loader — and that loader has no hook support. The hooks field
in .codex-plugin/plugin.json is never read, so every hook the plugin declares
stops running, with no warning, error, or log line.
Shipping both manifests is a reasonable thing to want: .codex-plugin/plugin.json
for Codex, the root manifest so the same directory loads in Cursor and other spec
clients. Today those two goals are mutually exclusive, and choosing one silently
costs you the other.
Why it is hard to notice
The failure is invisible from every surface a user would check:
- The TUI still prints
hook: SessionStart/hook: SessionStart Completed
lines — for other plugins' hooks. The count looks plausible. ~/.codex/config.tomlkeeps its[hooks.state]entries for the affected
plugin with validtrusted_hashvalues, so the hooks appear registered.codex doctorhas no hooks section.- Nothing is logged.
Two of our plugins were dead for a week before anyone noticed, and locating the
cause took a long bisect against an installed cache.
Reproduction
Codex 0.149.0, Linux (WSL2), plugin installed from a git marketplace.
Plugin layout:
condux/
.codex-plugin/plugin.json # { ..., "hooks": "./hooks/codex-hooks.json" }
.claude-plugin/plugin.json
plugin.json # Agent Plugins 1.0.0 manifest
hooks/codex-hooks.json # SessionStart + Stop
hooks/session-start.mjs
skills/...
hooks/codex-hooks.json:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "node \"${PLUGIN_ROOT}/hooks/session-start.mjs\" --codex",
"additionalContextLimit": 4000
}
]
}
]
}
}
With a tracer added to the body of session-start.mjs (editing the command
string instead would invalidate the trusted hash and prompt), run
codex exec "hi" twice — once with the root plugin.json present, once with it
moved aside. Nothing else changes.
root plugin.json |
SessionStart hooks fired | Tracer |
|---|---|---|
| present | 2 (both from an unrelated plugin) | never runs |
| removed | 3 | runs |
A second plugin in the same install reproduces it identically, including a
UserPromptSubmit hook that goes from absent to firing. And a third plugin that
ships no root manifest has its hooks working throughout — which is what
isolates the manifest as the cause.
What we tried
| Attempt | Result |
|---|---|
hooks field on the root manifest |
ignoring unknown Agent Plugins manifest field |
extensions |
only namespace com.openai is read, keys default_tools_approval_mode / enabled_tools / disabled_tools / tools. No hooks slot |
conventional hooks/hooks.json alongside |
also suppressed |
move the manifest to .cursor-plugin/plugin.json |
hooks return — but that is a Cursor Plugin manifest, a different format |
So there is no supported way to be a spec-conformant Agent Plugin and keep
Codex hooks. We shipped the removal, which costs those two plugins their spec
conformance.
Suggested fix
Either of these would resolve it:
- Precedence — when
.codex-plugin/plugin.jsonis present, let it keep
authority over host-specific fields (hooks, and presumablyinterface)
even if a root manifest also exists. This matches the documented resolution
order.codex-plugin/→.claude-plugin/→.cursor-plugin/, which the
Agent Plugins loader currently bypasses entirely. - A hooks slot under
extensions— e.g.
extensions["com.openai"].hooks, keeping the root manifest inside its
closed schema.
Failing either, a warning would have saved the week: something like
plugin <name> declares hooks in .codex-plugin/plugin.json but ships a root plugin.json; hooks are disabled at load time. Right now the mode is entirely
silent.
Environment
- Codex CLI 0.149.0 (npm, linux-x86_64), Ubuntu 24.04 on WSL2
[features] hooks = true- Plugins installed from a git marketplace source
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Trace the Agent Plugins loader and the .codex-plugin/plugin.json versus root plugin.json resolution path, then reproduce the issue with codex exec "hi" using the supplied manifest layout. Done means hooks from .codex-plugin/plugin.json still fire when a root manifest is present, or the conflicting configuration produces a clear warning.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 56/100