Allow PreCompact hooks to replace the active conversation history
Nobody has claimed this yet.
- 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:
- preserve all user and assistant text verbatim
- inspect historical tool calls and tool results
- decide which tool calls/results are still relevant
- remove obsolete tool calls/results
- truncate large stale tool outputs
- keep exact paths, commands, errors, constraints, and other high-value details without rewriting them into a lossy summary
- 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
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
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