openai / openai/codex

Allow PreCompact hooks to replace the active conversation history

Open
#46,337 2 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

context enhancement hooks
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

What feature would you like to see?

Please allow PreCompact hooks to return a replacement transcript / replacement message history that Codex should use instead of running the built-in compaction over the original history.

Codex already has a very useful compaction lifecycle:

  • PreCompact
  • built-in compaction
  • PostCompact

and PreCompact already receives useful metadata including session_id, turn_id, transcript_path, cwd, model, and trigger.

However, the current hook output only supports control-flow effects such as:

{
  "continue": false,
  "stopReason": "..."
}

There is currently no supported way for a PreCompact hook to return a modified conversation history and tell Codex:

use these messages as the compacted context instead of producing the built-in summary

That is the missing capability.

Why this matters

A custom compactor does not necessarily want to summarize the conversation.

For example, a compactor may want to:

  1. preserve all user and assistant text verbatim
  2. inspect historical tool calls and tool results
  3. decide which tool calls/results are still relevant
  4. remove obsolete tool calls/results
  5. truncate large stale tool outputs
  6. keep exact paths, commands, errors, constraints, and other high-value details without rewriting them into a lossy summary
  7. hand the resulting message list back to Codex as the new active history

This makes alternative compaction strategies possible without forking Codex.

Concrete reference: Claude Code implementation

This is already possible with Claude Code's early-access function hooks and is used by:

https://github.com/tamaratran/fast-jev-compaction

The plugin intercepts Claude Code's session.compact event, processes the original messages, and can return:

return { messages };

Claude Code then continues using that replacement message list instead of its built-in summary.

Relevant implementation:

https://github.com/tamaratran/fast-jev-compaction/blob/main/hooks/fast-jev.ts

The important behavior is conceptually:

on('session.compact', async ($, event, next) => {
    const messages = await customCompaction(event.messages);

    // Replace built-in compaction result
    return { messages };
});

If the custom compactor fails or decides not to replace the compaction, it can delegate to the built-in behavior.

That API makes projects such as fast-jev-compaction possible without patching the agent runtime.

Current Codex implementation

Codex already appears very close to supporting this.

The current compact hook implementation is here:

https://github.com/openai/codex/blob/main/codex-rs/hooks/src/events/compact.rs

The compaction lifecycle is invoked from the core before the actual compaction, for example here:

https://github.com/openai/codex/blob/main/codex-rs/core/src/compact_token_budget.rs

Conceptually the current flow is:

PreCompact
    ↓
Continue / Stop
    ↓
Codex built-in compaction
    ↓
PostCompact

What is missing is:

PreCompact
    ↓
Continue / Stop / ReplaceHistory
                     ↓
              custom messages
                     ↓
          install replacement history
                     ↓
                PostCompact
Possible API shape

The exact schema is of course up to the Codex team, but something along these lines would be sufficient:

{
  "hookSpecificOutput": {
    "hookEventName": "PreCompact",
    "replacementHistory": [
      ...
    ]
  }
}

or:

{
  "compact": {
    "messages": [
      ...
    ]
  }
}

The important part is not the exact JSON shape. The important capability is that a trusted synchronous PreCompact hook can return a replacement conversation history.

It would also be useful to distinguish three outcomes explicitly:

continue   -> run normal Codex compaction
replace    -> install returned history and skip built-in compaction
stop       -> abort compaction
Transcript access

Since PreCompact already receives transcript_path, an external command/MCP hook can already inspect the conversation.

Therefore the main missing primitive is not reading the history; it is safely writing/replacing the active history through a supported hook result.

Directly editing the rollout/transcript file from a hook would be brittle and would couple extensions to Codex's persistence format. A first-class replacement result would provide a much cleaner extension point.

Example use case

The motivating example is the JEV-based compactor above.

Instead of asking another LLM to summarize an old conversation, it scores individual historical tool calls/results for continued relevance.

A possible flow in Codex would be:

auto/manual compact
        ↓
PreCompact
        ↓
hook reads transcript
        ↓
external compactor/JEV scores old tool calls
        ↓
drop stale tool calls/results
truncate selected results
keep important messages verbatim
        ↓
return replacement history
        ↓
Codex installs that history
        ↓
PostCompact

This would allow custom context-management strategies while keeping the rest of Codex unchanged.

Relation to existing issues

#17148 requested Pre/PostCompact lifecycle hooks. Those hooks now exist, which is great.

This request is specifically about extending PreCompact from an observe/control hook into a replacement-capable compaction hook.

#21753 also tracks broader Claude Code hook parity; this would close an important functional parity gap for custom compaction.

Why a first-class API is preferable

Without replacement support, implementing this requires either:

  • maintaining a fork of Codex
  • patching internal compaction code
  • mutating internal transcript files and relying on implementation details
  • running an external wrapper that reconstructs sessions

A supported replacement result would make custom compaction implementations small, isolated, and plugin-friendly.

Thanks for considering it.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading codex-rs/hooks/src/events/compact.rs and the compaction flow in codex-rs/core/src/compact_token_budget.rs. Trace how PreCompact results currently support continue and stop, then determine how a replacement history would be represented and installed. Done means a supported replacement outcome skips built-in compaction, preserves the lifecycle, and is covered by relevant tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
cli, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.