Commit Graph
14 Commits
Author SHA1 Message Date
jif 885113aa1d Prevent command hooks from hanging on blocked stdin (#44288)
## Why

Writing hook input before draining output can deadlock when pipe buffers fill. Stdin writes also ran outside the hook timeout, allowing hooks that never read input to hang indefinitely.

## What changed

Write stdin concurrently with draining stdout and stderr, and apply the configured timeout to both input delivery and command completion. Preserve handling of broken pipes and distinct stdin and wait errors.

## Testing

Add a regression test that sends a large input to a hook that fills stdout and stderr without reading stdin. Verify that output drains and the hook reports its configured two-second timeout.

GitOrigin-RevId: 48cf33a2acfebbc5122a4522660d7a6b3fb1e9b0
2026-09-09 19:49:47 +00:00
Abhinav cfd5d77d63 Detach Unix hook commands from the controlling terminal (#43876)
## Why

Shell startup code that accesses the controlling terminal can stop a hook on background terminal I/O, preventing the hook command from running.

## What changed

Use `detach_from_tty` before executing Unix hook commands to avoid inheriting the controlling terminal while preserving process-group cleanup.

## Testing

Add a Unix regression test that runs under a controlling terminal and uses `BASH_ENV` to invoke `stty` during shell startup. Verify that the hook completes successfully and produces the expected output.

GitOrigin-RevId: 231125d4b169fb1bd828e3f8e96476a13bd25d26
2026-09-08 18:04:48 +00:00
Krish Chainani 8b61c50ebe Run allowlisted executor plugin stop hooks (#40009)
## What changed

- Discover inline hooks from executor-provided plugin manifests and accept only
  the bundled Computer Use `Stop` hook for `node_repl.turn_ended`.
- Add the accepted hook to the current step's hook engine with
  executor-scoped provenance.
- Run executor-scoped hooks in the background after regular stop-hook
  decisions, without delaying turn completion, applying control effects, or
  exposing them in hook summaries. Skip them when regular hooks only block
  continuation.

## Testing

- Added coverage for manifest filtering and option preservation.
- Added coverage for background execution, interaction with regular stop
  hooks, and non-blocking turn completion.

GitOrigin-RevId: c0f75f83a23ba8a83bb3a5d5997c4882aacc32d6
2026-08-21 21:01:59 +00:00
Abhinav 4d8c664a49 Run hooks with the captured session environment (#39314)
## What changed

- Capture the process environment when the hook registry is created and reuse
  that snapshot across configuration reloads.
- Clear the live environment before launching command hooks and legacy notify
  commands, then apply hook-specific overrides and scrub non-inheritable
  credentials.
- Resolve the default shell from the captured environment.

## Testing

Add coverage for snapshot replay, overrides, credential scrubbing, default
shell selection, non-Unicode values, and runtime reconfiguration.

GitOrigin-RevId: fee60c88e842980cdfc1bd49b14b62b9a56b08cd
2026-08-18 23:23:23 +00:00
Abhinav 85fc4def35 Add MCP tool handler support to the hooks engine (#38705)
## What changed

- Discover synchronous `mcp_tool` hook handlers and invoke their configured MCP server and tool through a supplied executor.
- Expand nested hook-event placeholders in MCP tool inputs while preserving JSON types, and process tool output through the existing hook output contract.
- Represent hook details as handler-specific metadata in `hooks/list`, including MCP server and tool fields, and show those details in the TUI hooks browser.
- Skip unsupported `SessionEnd` MCP hooks and runtimes without MCP invocation support with startup warnings.

## Testing

- Cover argument expansion, missing placeholders, MCP invocation and hook decisions, discovery warnings, `hooks/list` metadata, and TUI rendering.

GitOrigin-RevId: 295b845471fe92bd7ad7cd272fbcd2c3713912e0
2026-08-15 05:53:54 +00:00
Abhinav a875dd6b22 Generalize hook handler execution (#37644)
## What changed

- Represent configured handlers by handler kind and route execution through the
  hooks engine, while preserving command hook behavior.
- Reject MCP tool inputs containing values such as `null` that cannot be
  represented in TOML for trust hashing.

## Testing

- Add coverage for rejecting `null` at the top level and within nested MCP tool
  input objects and arrays.

GitOrigin-RevId: 3854a349c2a721691450467592f99cf57e6260ef
2026-08-09 00:57:15 +00:00
cooper-oai c4513cb982 Prevent launch context from reaching child processes (#37607)
## Why

Model-reachable child processes should not inherit Codex launch context.

## What changed

- Treat `OPENAI_FEDERATION_RULE_ID` and `OPENAI_IDENTITY_TOKEN_FILE` as non-inheritable environment variables, with case-insensitive matching.
- Remove them after shell environment policy overrides and before spawning commands across execution, MCP, hooks, Git helpers, and remote helper processes.

## Testing

- Cover inherited and explicitly configured variants, including mixed-case names.
- Verify the variables are absent from real child environments and app-server command and process execution.

GitOrigin-RevId: 2535527893985fef0995617f4c5b2462bea7c136
2026-08-08 16:58:26 +00:00
Abhinav 6f647caa9b Support asynchronous command hooks (#37533)
## Why

Hook configurations can mark command handlers as asynchronous, but Codex previously skipped those handlers outside `SessionEnd`.

## What changed

- Run asynchronous command hooks in the background with a per-session concurrency limit, while keeping `SessionEnd` hooks synchronous.
- Prevent asynchronous hooks from blocking, stopping, rewriting, or otherwise controlling the operation that launched them.
- Deliver warnings and additional context at safe turn boundaries: inject results into an active turn after sampling, or buffer them ahead of the next user prompt when the session is idle.
- Preserve in-flight hooks across configuration reloads, scope spilled output to the thread, and abort outstanding work during session shutdown.

## Testing

Add unit and integration coverage for background scheduling, concurrency, output parsing, active and idle result delivery, configuration reloads, and shutdown cleanup.

GitOrigin-RevId: 8094552e4afe7b47b09a61bb575bb20f4e491d8d
2026-08-08 04:24:12 +00:00
Abhinav dd916428cd Terminate timed-out hook process trees (#37527)
## Why

Timing out a command hook can leave descendant processes running after the hook itself is stopped.

## What changed

- Run hook commands in a process group on Unix and a job object on Windows.
- Terminate the full process tree when hook execution is cancelled or fails, with `taskkill` as a Windows fallback.
- Preserve descendants when a hook completes successfully so intentionally detached helpers can continue running.

GitOrigin-RevId: 1a18653d34c6f1685ef8c4f8e8546663b276292a
2026-08-08 03:48:34 +00:00
jif 634a998d8a Preserve output from hooks that exit before reading stdin (#35194)
## Why

A hook can exit successfully without consuming its JSON input. If it closes
stdin while Codex is still writing, the resulting broken pipe should not hide
the hook's exit status and output.

## What changed

Ignore broken-pipe errors when writing hook stdin and continue waiting for the
process result. Other stdin write errors remain failures.

## Testing

Add a cross-platform regression test that sends a large input to a
fast-exiting hook and verifies that its successful status and stdout are
preserved.

GitOrigin-RevId: e1b6e7c6ac5e39232e49b45245ea0df9d34d1fe6
2026-07-24 14:37:11 +00:00
Abhinav 3ac476bed2 Fix quoted hook commands on Windows (#33926)
## Why

Windows hook commands whose executable paths contain spaces fail when their
outer quotes are escaped during argument construction.

## What changed

- Pass hook command strings as raw arguments to the default Windows shell.
- Do the same for configured shells that use the case-insensitive `/c` flag,
  while retaining normal argument handling for other shells.

## Testing

Add a Windows regression test that runs a quoted `.cmd` hook from a directory
with spaces through both the default shell and an explicit `COMSPEC /c` shell.

GitOrigin-RevId: 0e7ce44203e01d3823daaaec750c2aeadfcd34b7
2026-07-17 23:24:26 +00:00
Tom f8d07b5641 trace hook command execution (#31501)
Adds a single trace span around configured hook command execution so
slow installed hooks are visible directly in traces.

The span records low-cardinality hook metadata and command outcome
without command text, hook input, stdout, or stderr.
2026-07-07 18:00:35 -07:00
AbhinavandCodex c6e7d564c3 Discover hooks bundled with plugins (#19705)
## Why

Plugins can bundle lifecycle hooks, but Codex previously only discovered
hooks from user, project, and managed config layers. This adds the
plugin discovery and runtime plumbing needed for plugin-bundled hooks
while keeping execution behind the `plugin_hooks` feature flag.

## What

- Discovers plugin hook sources from each plugin's default
`hooks/hooks.json`.
- Supports `plugin.json` manifest `hooks` entries as either relative
paths or inline hook objects.
- Plumbs discovered plugin hook sources through plugin loading into the
hook runtime when `plugin_hooks` is enabled.
- Marks plugin-originated hook runs as `HookSource::Plugin`.
- Injects `PLUGIN_ROOT` and `CLAUDE_PLUGIN_ROOT` into plugin hook
command environments.
- Updates generated schemas and hook source metadata for the plugin hook
source.

## Stack

1. This PR - openai/codex#19705
2. openai/codex#19778
3. openai/codex#19840
4. openai/codex#19882

## Reviewer Notes

- Core logic is in `codex-rs/core-plugins/src/loader.rs` and
`codex-rs/hooks/src/engine/discovery.rs`
- Moved existing / adding new tests to
`codex-rs/core-plugins/src/loader_tests.rs` hence the large diff there
- Otherwise mostly plumbing and minor schema updates

### Core Changes

The `codex-rs/core` changes are limited to wiring plugin hook support
into existing core flows:

- `core/src/session/session.rs` conditionally pulls effective plugin
hook sources and plugin hook load warnings from `PluginsManager` when
`plugin_hooks` is enabled, then passes them into `HooksConfig`.
- `core/src/hook_runtime.rs` adds the `plugin` metric tag for
`HookSource::Plugin`.
- `core/config.schema.json` picks up the new `plugin_hooks` feature
flag, and `core/src/plugins/manager_tests.rs` updates fixtures for the
added plugin hook fields.

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-28 14:17:18 -07:00
Andrei Eternal 244b2d53f4 start of hooks engine (#13276)
(Experimental)

This PR adds a first MVP for hooks, with SessionStart and Stop

The core design is:

- hooks live in a dedicated engine under codex-rs/hooks
- each hook type has its own event-specific file
- hook execution is synchronous and blocks normal turn progression while
running
- matching hooks run in parallel, then their results are aggregated into
a normalized HookRunSummary

On the AppServer side, hooks are exposed as operational metadata rather
than transcript-native items:

- new live notifications: hook/started, hook/completed
- persisted/replayed hook results live on Turn.hookRuns
- we intentionally did not add hook-specific ThreadItem variants

Hooks messages are not persisted, they remain ephemeral. The context
changes they add are (they get appended to the user's prompt)
2026-03-10 04:11:31 +00:00