Fidelity-oriented Markdown exports of local OpenAI Codex app/CLI session JSONL. The exporter remains noninteractive and automation-first by default, with optional native dialogs and a terminal browser when they are useful.
- persistent export-directory and filename-template configuration;
- Windows folder picker and Save As dialog through the Python standard library;
- mode-aware filenames (
thread,turn-0012,response-0012,last-response, and so on); - complete PowerShell helpers, including
cdx-sessions; - method-specific token metadata instead of an always-approximate label;
- deterministic batch export, last-N-turn extraction, collision policies, and stable short-ID filenames;
- optional project-aware session browser;
- compaction/rollback-aware reconstructed live-context mode;
- explicit reasoning-summary opt-in without exporting raw internal reasoning;
- recognized/ignored/unknown event accounting and sibling JSON manifests;
- narrow repair of otherwise-valid JSON lines containing unescaped Windows-path backslashes;
- explicit distinction among source-runtime loss, exporter truncation, and harmless display ellipses;
- package metadata,
codex-exportconsole entry point, expanded regression tests, release automation, and a compatibility matrix. - deterministic UTF-8 standard output/error for Windows redirects and machine-readable JSON;
Use the same CPython interpreter that already has, or will receive, tiktoken:
$ErrorActionPreference = 'Stop'
$py = (Get-Command python -ErrorAction Stop).Source
Set-Location '<CLONED_OR_EXTRACTED_CODEX_EXPORTER_DIRECTORY>'
.\Install-CodexThreadExporter.ps1 `
-InstallDir 'C:\projects\CodexTools' `
-PythonPath $py `
-AppendProfile `
-InstallSkill `
-InstallTiktoken
. $PROFILE
cdx-tokenizer -Tokenizer tiktoken -Encoding cl100k_base -Require
cdx-sessionsThe installer accepts custom paths, replaces exactly one marked profile block, preserves unrelated profile content, and is idempotent. It only creates a timestamped profile backup when the generated profile content actually changes.
To install the package/console entry point as well:
.\Install-CodexThreadExporter.ps1 `
-InstallDir 'D:\Tools\Codex Exporter' `
-PythonPath $py `
-AppendProfile `
-InstallPackage `
-InstallTiktokenThe resolver precedence is:
CODEX_THREAD_EXPORT_DIRenvironment variable;- the persisted
last_out_dirin the exporter config; <exporter-script-directory>\codex-thread-exports.
On Windows, the persisted config is normally:
%LOCALAPPDATA%\codex-exporter\config.json
Override its location with CODEX_EXPORT_CONFIG when testing or maintaining separate profiles.
cdx-set-dir # native folder picker; remembers selection
cdx-set-dir 'D:\Codex Exports' # explicit persistent default
cdx-open # open current default in File Explorer
cdx-config # inspect persisted settingsOne-off controls:
cdx-thread <SESSION_ID> -ChooseOutDir # select and remember a folder
cdx-thread <SESSION_ID> -SaveAs # native Save As dialog; remembers parentAt the direct CLI level, --out-dir and --choose-out-dir are remembered by default. Add --no-remember-out-dir for a truly one-off destination.
| Command | Purpose |
|---|---|
cdx-sessions |
List active and archived local Codex sessions |
cdx-browse |
Optional paginated/project-aware multi-select browser |
cdx-thread <ID> |
Full chronological thread export |
cdx-thread-clip <ID> |
Full thread plus verified Unicode clipboard |
cdx-responses <ID> |
List prompt/response block indices |
cdx-turn <ID> N |
Prompt plus response block N, with metadata |
cdx-response-block <ID> N |
Response block N |
cdx-response <ID> |
Latest response, wrapped Markdown, clipboard |
cdx-last <ID> N |
Last N turns |
cdx-live <ID> |
Reconstructed compaction/rollback-aware live context |
cdx-batch <ID1>,<ID2> |
Deterministic batch export with short IDs |
cdx-final <ID> |
Last substantial assistant message |
cdx-msg <ID> N |
One message |
cdx-range <ID> A B |
Inclusive message range |
cdx-set-dir [PATH] |
Select or set persistent export directory |
cdx-set-template [TEMPLATE] |
Set or display persistent filename template |
cdx-tokenizer |
Display active tokenizer integration |
cdx-config |
Display persisted exporter configuration |
cdx-open |
Open the current export folder |
The lower-level Invoke-CodexThreadExport wrapper exposes every advanced option, including -ReasoningSummaries, -ReportEvents, -StrictEvents, -StableFilenames, -Collision, -OpenAfter, and tokenizer controls.
Default filenames use:
codex_{title}_{mode}_{stamp}{session_short_part}{counts}.md
Examples:
codex_example_project_thread_FRI_07172026_015336_AM-PDT_552296lines_approx7867634tokens.md
codex_example_project_turn-0012_FRI_07172026_020000_AM-PDT_1234lines_18543cl100k_base_tokens.md
codex_example_project_response-0012_FRI_07172026_020100_AM-PDT_233lines_2981cl100k_base_tokens.md
Available template placeholders:
{title} {mode} {stamp} {session_id} {session_short} {session_short_part}
{lines} {tokens} {token_method} {token_encoding} {counts}
Configure them persistently:
cdx-set-template 'codex_{title}_{mode}_{stamp}{session_short_part}{counts}.md'
cdx-set-template # print current templateDirect CLI configuration:
codex-export --set-filename-template 'archive_{title}_{mode}{session_short_part}.md'
codex-export --print-filename-template
codex-export --reset-filename-template--stable-filenames removes the timestamp and forces a short session ID. Batch exports include short IDs automatically. Existing-file policy is selected with --collision rename|overwrite|skip|error; rename is the safe default.
Repeat source options or use a line-oriented input file:
codex-export --session-id <ID1> --session-id <ID2> --mode thread
codex-export --jsonl C:\a.jsonl --jsonl C:\b.jsonl --mode thread
codex-export --sessions-file .\sessions.txt --mode thread --stable-filenames
codex-export --session-id <ID> --mode thread --last-n-turns 5--browse is optional and does not change the noninteractive architecture. Browser controls are:
number/range select rows a select current page
n / p next/previous m projects/sessions view
/ text filter s find ID/rollout filename
b back q quit
The default is --tokenizer auto --token-encoding cl100k_base.
When tiktoken succeeds, the export records:
token_count: 12345
token_count_method: "tiktoken"
token_encoding: "cl100k_base"
tokenizer_library: "tiktoken"
tokenizer_version: "0.12.0"
token_count_exact_for_encoding: true
token_special_text_policy: "ordinary_text; disallowed_special=()"When unavailable or intentionally disabled:
token_count_method: "regex_estimate"
token_encoding: "none"
token_count_exact_for_encoding: false“Exact” means exact for the selected encoding and the exact exported Unicode text. It does not claim to reproduce a model API’s hidden role/tool framing, server-side accounting, or another model’s native tokenizer.
Install and validate in the exact interpreter used by the wrapper:
$ErrorActionPreference = 'Stop'
$py = (Get-Command python -ErrorAction Stop).Source
& $py -m pip install --upgrade --only-binary=:all: 'tiktoken>=0.12,<1'
cdx-tokenizer -Tokenizer tiktoken -Encoding cl100k_base -Require
@'
from pathlib import Path
import importlib.metadata as md
import runpy
import sys
import tiktoken
script = Path(r"C:\projects\CodexTools\Export-CodexThread.py")
enc = tiktoken.get_encoding("cl100k_base")
text = "Codex exporter validation: I'll, I’ll, em dash —, emoji 🚀, 中文, code: `Get-ChildItem`"
print(f"python={sys.executable}")
print(f"script={script.resolve()}")
print(f"tiktoken={md.version('tiktoken')}")
print(f"encoding={enc.name}")
print(f"tokens={len(enc.encode(text, disallowed_special=()))}")
print(f"roundtrip={enc.decode(enc.encode(text, disallowed_special=())) == text}")
print(f"script_exists={script.is_file()}")
'@ | & $py -Do not validate with Path("Export-CodexThread.py") from an unrelated working directory; use the installed absolute path or $script:CodexThreadExporter.
Every nonblank source line is attempted with strict json.loads first. On failure, the repair path only escapes invalid backslashes inside JSON strings, preserving valid JSON escapes and structural characters. It does not concatenate lines, invent braces, or guess arbitrary malformed records.
Metadata distinguishes:
parse_error_count: 0
repaired_json_line_count: 3The manifest records each repaired line number, original parser error, repair class, and repair count. Unrecoverable lines remain parse_error_count entries and are never silently discarded.
Codex can store markers such as:
…1234 tokens truncated…
That means the missing material was already absent from the rollout JSONL. No exporter can reconstruct it. The default --source-truncation annotate replaces the ambiguous marker with:
[SOURCE TOOL OUTPUT TRUNCATED BY CODEX BEFORE EXPORT: 1234 tokens omitted; not recoverable from this rollout record]
Other policies:
--source-truncation preserve # keep original marker verbatim
--source-truncation error # abort instead of exporting lossy sourceThe manifest separately counts:
- raw markers in all JSONL lines;
- markers encountered in extracted record types;
- markers remaining in the selected rendered record set.
Exporter-created tail trimming uses an unmistakable independent sentinel:
[EXPORTER TOOL OUTPUT TRUNCATED: kept final N of M characters]
Overlong-line, binary/blob, repeated-line, ANSI/OSC, and transport-header cleanup are independently counted in the manifest.
Every written export receives a sibling .manifest.json unless --no-manifest is used. It reports:
- source path, SHA-256, session ID, and source-event count;
- output path, mode, lines, tokenizer method, and exactness;
- recognized event schemas;
- known ignored schemas and explicit reasons;
- unknown schemas;
- cleaning, summarization, and redaction counts;
- repaired and unrecovered JSON lines;
- source/extracted/selected truncation counts;
- compaction and rollback reconstruction history.
Use:
codex-export --session-id <ID> --mode thread --report-events
codex-export --session-id <ID> --mode thread --strict-events--strict-events is suitable for regression/CI workflows where a new Codex schema should fail loudly.
cdx-live <SESSION_ID>The exporter applies recorded compacted.payload.replacement_history and thread_rolled_back.num_turns events and labels the result:
history_semantics: "reconstructed_live_context"This is an evidence-based reconstruction, not a claim to reproduce the server’s exact hidden context prompt. Chronological cdx-thread remains the authoritative source-history export.
Raw internal reasoning is not exported. --reasoning-summaries includes only explicit summary text already stored in Codex response_item/reasoning.summary records, with an opt-in label.
Invoke-CodexThreadExport -SessionId <ID> -Mode thread -ReasoningSummariespython -m pip install -e '.[test,exact]'
python tools\sync_cli.py
python -m pytest
python -m buildCI covers Python 3.10 and 3.13 on Windows, macOS, and Linux; mirrored-script identity; exact cl100k_base smoke tests; PowerShell parsing; custom installation paths; idempotent profile replacement; archived-session discovery; every export mode; batch collisions; compaction/rollback; unknown schemas; and OS clipboard integration where a runner exposes it.
Tagged v* pushes build distributions and create a GitHub release. See COMPATIBILITY.md and CHANGELOG.md.
Rollouts can contain source code, local paths, credentials, and private repository names. Use --redact before sharing and inspect the result. Redaction and binary/blob suppression are counted in the manifest, but pattern-based redaction is not a substitute for human review.