feat: record messages from user in ~/.codex/history.jsonl #939
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This is a large change to support a "history" feature like you would expect in a shell like Bash.
History events are recorded in
$CODEX_HOME/history.jsonl
. Because it is a JSONL file, it is straightforward to append new entries (as opposed to the TypeScript file that uses$CODEX_HOME/history.json
, so to be valid JSON, each new entry entails rewriting the entire file). Because it is possible for there to be multiple instances of Codex CLI writing tohistory.jsonl
at once, we use advisory file locking when working withhistory.jsonl
incodex-rs/core/src/message_history.rs
.Because we believe history is a sufficiently useful feature, we enable it by default. Though to provide some safety, we set the file permissions of
history.jsonl
to beo600
so that other users on the system cannot read the user's history. We do not yet support a default list ofSENSITIVE_PATTERNS
as the TypeScript CLI does:codex/codex-cli/src/utils/storage/command-history.ts
Lines 10 to 17 in 3fdf9df
We are going to take a more conservative approach to this list in the Rust CLI. For example, while
/\b[A-Za-z0-9-_]{20,}\b/
might exclude sensitive information like API tokens, it would also exclude valuable information such as references to Git commits.As noted in the updated documentation, users can opt-out of history by adding the following to
config.toml
:Because
history.jsonl
could, in theory, be quite large, we take a[n arguably overly pedantic] approach in reading history entries into memory. Specifically, we start by telling the client the current number of entries in the history file (history_entry_count
) as well as the inode (history_log_id
) ofhistory.jsonl
(see the new fields onSessionConfiguredEvent
).The client is responsible for keeping new entries in memory to create a "local history," but if the user hits up enough times to go "past" the end of local history, then the client should use the new
GetHistoryEntryRequest
in the protocol to fetch older entries. Specifically, it should pass thehistory_log_id
it was given originally and work backwards fromhistory_entry_count
. (It should really fetch history in batches rather than one-at-a-time, but that is something we can improve upon in subsequent PRs.)The motivation behind this crazy scheme is that it is designed to defend against:
history.jsonl
being truncated during the session such that the index into the history is no longer consistent with what had been read up to that point. We do not yet have logic to enforce amax_bytes
forhistory.jsonl
, but once we do, we will aspire to implement it in a way that should result in a new inode for the file on most systems.history.jsonl
is an append-only log, so long as the client reads backwards fromhistory_entry_count
, it should always get a consistent view of history. (That said, it will not be able to read new commands from concurrent sessions, but perhaps we will introduce a/
command to reload latest history or something down the road.)Admittedly, my testing of this feature thus far has been fairly light. I expect we will find bugs and introduce enhancements/fixes going forward.