Commit c97d799
committed
refactor(codex): wire bundled plugin via marketplace + per-spawn MCP overrides
Codex consumes the same plugin tree as Claude (resources/plugins/
harness-status/), but the integration path is different in three
fundamental ways the implementation had to discover empirically:
1. Codex does NOT interpolate `${VAR}` in .mcp.json — not user env
(${HARNESS_NODE_EXEC}), not its own injected vars (${CLAUDE_PLUGIN_ROOT}).
Templates are passed verbatim to execve, and the plugin's source
.mcp.json (which Claude expands correctly via --plugin-dir) would
fail with "MCP startup failed: No such file or directory" if Codex
tried to spawn it. We neutralize the cached .mcp.json post-install
to `{"mcpServers": {}}` and register harness-control per-Codex-spawn
via `-c mcp_servers.harness-control.command="..."` literal overrides.
2. Codex spawns MCP subprocesses with a stripped env — only
HOME/PATH/PWD/USER/LANG inherit. The HARNESS_* vars set on the
agent PTY don't reach the bridge. The per-spawn `-c` override
bakes literal values directly into the MCP env block.
3. Codex's `~` resolution uses getpwuid_r (system passwd database)
rather than $HOME on macOS, so an isolated $HOME doesn't reach the
Codex binary. We set process.env.CODEX_HOME = $HOME/.codex at boot
when not already set — idempotent for the common case, fixes
isolation for dev sessions, propagates to all child processes.
Install path:
- `hooks:accept` IPC runs `codex plugin marketplace add <bundled-root>`
+ `codex plugin add harness-status@harness`. Both write to
$CODEX_HOME/config.toml; cleanly reversible via the matching remove
commands.
- Boot logic re-runs the install every launch when consent === 'accepted'.
Codex's plugin cache never auto-refreshes, so re-running is how we
propagate new Harness releases to the cache.
- Spawn args: agents/codex.ts buildSpawnArgs appends -c overrides for
command, args, and env when opts.harnessControl is set.
Verification (Settings → Codex → "Harness status plugin"):
- Plugin enabled in Codex (`codex plugin list --marketplace harness`)
- Hooks file present in cache
- Hooks trusted by Codex (key-based presence check —
`[hooks.state."harness-status@harness:hooks/hooks.json:<event>:N:M"]`
with non-empty trusted_hash for every Codex-recognized event from
hooks.json; Notification is filtered out as Claude-only).
Trust:
- We do NOT auto-trust. Codex's trust prompt is TUI-only; there's no
CLI command or flag to grant trust non-interactively. The Settings
card surfaces inline guidance when hooks aren't yet trusted: open a
Codex tab, accept the "Hooks need review / Trust all and continue"
prompt, then Re-check.
Uninstall:
- `codex plugin remove` + `codex plugin marketplace remove`
- Strip [hooks.state] entries from $CODEX_HOME/config.toml by
`harness-status@harness:` key prefix (Codex's `trusted_hash` is over
a normalized form we can't reproduce, so key-based matching is the
reliable signal). A reinstall re-prompts for trust review rather
than silently inheriting prior consent via content-hash match.
UI:
- Install prompt lives as an in-tab overlay in XTerminal.tsx
(`agentKind === 'codex' && hooksConsent === 'pending'`), mirroring
the Claude "Switch to Chat mode" hint. No top-of-app banner —
Claude-only users never see it.
- Settings card shows the actual CLI commands Harness runs, the
verification panel, and a Re-check button.
- Card title: "Harness status plugin" (it's a plugin Harness ships
for Codex to call back into Harness, not a Codex-authored plugin).
Codex agent module (agents/codex.ts):
- Removed installHooks/uninstallHooks/hooksInstalled (~150 lines of
dead code from the pre-plugin ~/.codex/hooks.json install path).
stripHooksFromWorktree reduced to a one-line delegate satisfying
the AgentModule contract; effectively dead post-migration.
- Sessions dir lookup honors CODEX_HOME.
Migration:
- `codexPluginMigrated` flag gates a one-shot strip of legacy
~/.codex/hooks.json + per-worktree .codex/hooks.json Harness entries
on first boot post-upgrade. Inferred consent (positive legacy strip
→ 'accepted') so prior consenters don't get re-prompted.1 parent 7541332 commit c97d799
16 files changed
Lines changed: 847 additions & 445 deletions
File tree
- resources/plugins/harness-status/.claude-plugin
- src
- main
- agents
- renderer
- components
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
303 | 303 | | |
304 | 304 | | |
305 | 305 | | |
306 | | - | |
307 | | - | |
308 | | - | |
309 | | - | |
310 | | - | |
311 | | - | |
312 | | - | |
313 | | - | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
314 | 323 | | |
315 | 324 | | |
316 | 325 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
| 4 | + | |
5 | 5 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
This file was deleted.
0 commit comments