A root plugin.json silently disables all of a plugin's hooks

Open 💬 1 comment Opened Aug 21, 2026 by vi-hieu

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.toml keeps its [hooks.state] entries for the affected

plugin with valid trusted_hash values, so the hooks appear registered.

  • codex doctor has 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:

  1. Precedence — when .codex-plugin/plugin.json is present, let it keep

authority over host-specific fields (hooks, and presumably interface)
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.

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

View original on GitHub ↗

1 Comment

M-Kepler · 3 days ago

+1