One conversation. Any agent runtime.
Install · Quickstart · The Record · Headless CLI · Trust Boundary · How It Works · Development
Constant is a local-first continuity tool for agent CLIs. Start a thread in Codex, carry it to Claude Code or OpenCode, then carry it back without re-explaining the work.
Codex changes.
Claude changes.
The conversation stays constant.
Constant is early software. It works by reading and writing local runtime session files, so read the trust boundary below before using it on important sessions.
Constant has three surfaces:
constant host: run one agent CLI at a time inside a native PTY and switch the live runtime with a tmux-style prefix key. A persistent status bar shows the active runtime and your place in the thread.constant carry: headless carry into a target runtime's native session and print the resume command.- the record: every carry first writes the distilled conversation as a neutral
snapshot under
~/.constant/snapshots/, so any hop can be listed (constant snapshots), reprinted (constant restore), or re-hosted (constant resume) — even if the native session files are later deleted.
In host mode, the child keeps its normal terminal UI, colors, keybindings, and resume behavior. Constant sits outside it, watches for a prefix key, and swaps the active runtime when you ask.
constant host codex
you talk to Codex
Ctrl-B c -> Claude Code continues the same thread
Ctrl-B o -> OpenCode continues it
Ctrl-B x -> Codex takes it back
The switch is not a pasted summary. Constant reads the source runtime's local session, distills it to the conversation, records a snapshot, writes a target-native session, and starts the target runtime with its own resume command. Every carry prints a receipt of exactly what moved:
cobalt-37 · fix-the-bug · ch02 codex → claude · continue ·
carried 14 turns · dropped 6 tool events · 2 redactions
The same carry path is available without opening a hosted terminal:
constant carry --from codex --to claude
# carried -> claude session <id>
# carried 14 turns · dropped 6 tool events · 2 redactions
# resume with: claude -r <id>If you are on macOS and use Homebrew, use this:
brew tap kennykankush/constant https://github.com/kennykankush/constant
brew install kennykankush/constant/constant
constant --version
constant doctorThat is the recommended install path. The explicit
kennykankush/constant/constant name avoids ambiguity with any other formula
named constant.
You also need the agent CLIs you want to switch between:
codex --version # optional, for Codex support
claude --version # optional, for Claude Code support
opencode --version # optional, for OpenCode support
gemini --version # optional, for Gemini (carry source)Use this if you do not want Homebrew, or you are on Linux x86_64:
curl -fsSL https://raw.githubusercontent.com/kennykankush/constant/main/scripts/install.sh | sh
constant --version
constant doctorThe installer downloads the latest prebuilt binary, verifies its checksum,
smoke-tests it, and installs it to ~/.local/bin. If ~/.local/bin is not on
your PATH, it prints the exact export line to add.
Supported prebuilt targets:
- macOS Apple Silicon
- macOS Intel
- Linux x86_64
Use this only if you are hacking on Constant or want to build directly from the repo:
cargo install --git https://github.com/kennykankush/constant --lockedFrom a local checkout:
cargo install --path . --locked
cargo build --release
./target/release/constant --versionCheck your local runtime setup and current project state:
constant doctor
constant statusHost Codex:
constant host codexInside a hosted session, the default prefix is Ctrl-B.
| Keys | Action |
|---|---|
Ctrl-B c |
Continue in Claude Code, refreshing the existing Claude projection if one exists |
Ctrl-B C |
Create a new Claude continuation |
Ctrl-B x |
Continue in Codex |
Ctrl-B X |
Create a new Codex continuation |
Ctrl-B o |
Continue in OpenCode |
Ctrl-B O |
Create a new OpenCode continuation |
Ctrl-B t |
Toggle the cockpit — the chapter graph, with actions: c/x/o switch from inside it, r rename |
Ctrl-B : |
Open the Constant command line (switch claude, rename auth bug, quit; Tab completes, ↑/↓ recall history) |
Ctrl-B d |
Quit Constant (the hosted CLI exits with it) |
Ctrl-B Ctrl-B |
Send a literal Ctrl-B to the child runtime |
A status bar on the bottom row shows the hosted runtime, the thread position
(ch04·fix-the-bug), and the prefix keys. The child is simply told the terminal
is one row shorter — Constant stays a pass-through proxy, not a compositor.
Disable it with --no-bar.
If you are already inside tmux, pick another prefix:
constant host codex --prefix C-t
CONSTANT_PREFIX=C-t constant host codexEvery conversation gets a stable handle (cobalt-37 — one color word + a
2-digit tail, pinned in the ledger, collision-proof by registry) and a
title (your rename wins forever; otherwise a runtime-generated title is
harvested when one exists; otherwise the first contentful words). Carries are
chapters (ch04): each chapter is one runtime's turn narrating the thread.
constant rename auth bug # name it; native pickers are re-stamped
constant resume cobalt-37 # handles are never ambiguousRe-host a conversation later, straight from the ledger:
constant resume # newest conversation in this directory
constant resume fix-the-bug # match by slug or conversation id
constant resume --list --all # see everything resumableresume wakes the conversation's latest projection live in the harness. If
every native projection has been deleted, it reprints one from the latest
record snapshot first.
See every agent conversation alive on the machine right now (any runtime, hosted or not — read-only):
constant ps
# live agent sessions (14):
# claude 01-15:29:00 fix-the-bug fefd68b4-5ee… ~/dev/fantopy-hadi
# codex 01-15:27:35 - 019e9a25-91e… ~/dev/belvedereShow the current Constant projections for this directory:
constant trail
constant route
constant trail --allconstant trail shows the current projection per runtime. If the same projection
was updated multiple times, it is shown as synced Nx instead of repeated as a
new conversation. To inspect the raw append-only switch ledger:
constant trail --eventsconstant route is the debug view. It reconstructs the fork graph Constant knows
about and labels projections with readable aliases like codex[1] and
claude[1.1].
Move a conversation to another machine — the record travels, native sessions reprint on arrival:
constant pack cobalt-37 # → cobalt-37.constant-pack.json
# copy the file to the other machine, then:
constant unpack cobalt-37.constant-pack.json
constant resume cobalt-37 # wakes from the record, same handleEvery carry writes the distilled conversation to
~/.constant/snapshots/<conversation>/chNN-from-<runtime>.json — atomically,
owner-only, post-redaction, BEFORE the native copy is materialized. The neutral
snapshot is the durable record; native sessions are reprintable projections of
it.
constant snapshots # list record volumes for this directory
constant snapshots --all
constant restore <snapshot> # reprint a fresh native session from any hop
constant restore <snapshot> --to opencoderestore always mints a new session — it never overwrites anything, least of
all the record. Restores are logged to the trail, so lineage stays joined. If a
runtime update ever rejects Constant's sessions, or a cleanup deletes them, the
conversation reprints from its record.
Use the headless commands when you want continuity without hosting an interactive terminal.
List carryable sessions:
constant sessions
constant sessions --from gemini --all
constant sessions --from opencode --titles # OpenCode titles come from its own db
constant sessions --all --jsonBy default, sessions scopes to the current directory. --all scans all known
runtime stores. --titles reads transcripts to derive a preview title, so it is
slower on large stores. In text output, · marks a session known to be empty
when titles were requested.
Preview a carry without writing anything:
constant carry --from codex --to claude --dry-run
constant carry --session <path-or-session-id> --to opencode --dry-run --jsonCarry into the target runtime's native session and print the resume command:
constant carry --from codex --to claude
constant carry --session ses_… --to codex # OpenCode ids resolve automatically
constant carry --session <gemini session file> --to claudeBy default, carry continues the current target projection for the conversation:
if codex[1] already has claude[1.1], another carry to Claude updates that
same Claude session. Use --new when you want a separate target continuation:
constant carry --from codex --to claude --new
# codex[1] -> claude[1.2]Carry tool history too (experimental):
constant carry --from codex --to opencode --with-tools--with-tools (also on host and resume) carries tool calls and results
across the boundary instead of dropping them. Every string in their payloads is
redacted, oversized tool outputs are capped, and reasoning is never carried.
The receipt distinguishes kept N tool events from dropped N tool events.
distill is kept as an alias for carry, but carry is the public verb.
Export the distilled, redacted neutral IR:
constant export --from codex --out thread.constant.json
constant export --session <path-or-session-id>export writes to --out when provided, otherwise it prints JSON to stdout. It
refuses to overwrite the source session.
Constant does not claim shared native memory between agent CLIs. It reads visible local session evidence and authors a new session that the target runtime can resume.
Constant currently writes:
~/.claude/projects/<cwd-slug>/<id>.jsonl~/.claude/history.jsonl~/.codex/sessions/YYYY/MM/DD/rollout-<timestamp>-<id>.jsonl~/.codex/session_index.jsonl~/.codex/state_5.sqlite- the OpenCode store, via
opencode importonly (its own supported, validating entry point — Constant never writes OpenCode's sqlite directly) ~/.constant/trail.jsonl,~/.constant/snapshots/,~/.constant/cache/
Gemini support is currently read-only: Constant loads gemini sessions as
carry sources and never writes into ~/.gemini (carrying INTO gemini is gated
until its current session-storage behavior is verified — gemini's own store
migration has been observed deleting legacy-format chats, so Constant refuses
to guess).
All writes are atomic (temp file + rename): a crash mid-switch cannot leave a torn session, and the previous projection always survives a failed write.
Original source sessions are read as seeds and are not overwritten. Constant maintains its own runtime projections for the carried thread, then keeps those projections in sync as you switch back and forth.
Constant redacts common secrets from carried text and strips runtime scaffold, but it still moves conversation content across a trust boundary. Do not use it on a session if you are not comfortable with the target runtime reading that thread.
F1 invariant: original runtime sessions are seeds, not targets. host and
carry write or update Constant-owned projection sessions, then resume those
projections. They do not overwrite the original source session.
Command write behavior:
constant doctor: reads CLI/version/store presence onlyconstant status: reads runtime readiness, latest sessions, and the trail ledgerconstant trail/constant route/constant snapshots: read the ledgerconstant sessions: reads session metadata;--titlesalso reads transcriptsconstant carry --dry-run: reads and distills, writes nothingconstant carry: writes a record snapshot + a target-native projection, updates the trailconstant restore: writes a fresh native projection from a record volumeconstant resume: hosts an existing projection; writes only when you switchconstant host: writes only when you switch runtimesconstant export: writes only the requested--outfile, or stdout
Carries:
- user and assistant conversation turns
- the working directory recorded by the source runtime
- a Constant trail name so the carried session is recognizable in the target's resume picker
- with
--with-tools: tool calls and results (redacted, size-capped)
Stripped on purpose:
- runtime scaffold such as system prompts, injected environment blocks, skill lists, plugin lists, and memory blocks
- secrets: API keys, GitHub/Slack tokens, AWS access keys, private-key blocks, JWTs, authorization headers, password/token assignments, and email addresses
Not carried:
- tool calls and tool results (unless
--with-tools) - reasoning traces (never — model-internal)
- hidden provider state
- approvals, sandbox state, prompt cache state, or private runtime internals
Every carry prints a receipt of what was kept, dropped, and redacted — the lossiness is declared, never silent.
┌────────────────── constant host ──────────────────┐
│ real terminal │
│ input -> prefix interceptor -> PTY -> runtime │
│ screen <- child output <- PTY <- runtime │
│ [ status bar on a protected bottom row ] │
└───────────────────────────────────────────────────┘
on switch:
source runtime session
-> alembic reader (codex | claude | gemini | opencode)
-> neutral thread model
-> sanitize + redact (receipt)
-> record snapshot (~/.constant/snapshots/…)
-> target-native session writer (codex | claude | opencode)
-> target runtime resume
The distillation layer is named alembic. It is the part that knows how to load
each runtime's session format, strip it down to the portable conversation, and
materialize the target runtime's native session shape. Runtimes plug in as
codecs around the neutral model — N runtimes mean N codecs, not N² translators.
Public commands:
constant host [codex|claude|opencode] [--prefix C-t] [--with-tools] [--no-bar]
constant resume [QUERY] [--in RT] [--list] [--all] [--prefix C-t] [--with-tools] [--no-bar]
constant carry --to codex|claude|opencode [--from RT | --session <path-or-id>] [--json] [--dry-run] [--debug] [--new] [--with-tools]
constant sessions [--from RT] [--all] [--titles] [--json]
constant rename [--of HANDLE] NEW NAME...
constant pack HANDLE [--out FILE]
constant unpack FILE
constant ps [--json]
constant snapshots [--all]
constant restore <snapshot> [--to RT] [--json]
constant export (--from RT | --session <path-or-id>) [--out FILE]
constant doctor [--json]
constant status [--all]
constant trail [--all] [--events]
constant route [--all] [--session <path-or-id>]
constant --versionDebug and inspection commands:
constant distill --from codex --to claude
constant keysconstant distill is the older name for constant carry; the internal cartridge
still calls this step distillation. constant keys prints raw key bytes for
debugging prefix behavior.
Current support:
- Codex CLI: full (host, carry in/out, resume) — validated against
0.139.x - Claude Code: full — validated against
2.1.x - OpenCode: full — validated against
1.14.x; reads viaopencode export, writes viaopencode import, resumes viaopencode -s <id> - Gemini CLI: carry source — validated against
0.40.x; existing gemini conversations load, list, and carry into the other runtimes
The session formats are private and can change between runtime releases. Constant has round-trip tests for the current known shapes, but a runtime update can still require a codec refresh — and if one ever rejects Constant's sessions, the conversation reprints from its record snapshot.
Planned: carrying into Gemini; Aider.
Constant is probably the wrong tool if:
- you need a lossless transfer of reasoning traces or hidden runtime state
- you do not want any writes into
~/.claude,~/.codex, or the OpenCode store - your source runtime is still generating the current turn
- your installed runtime version is outside the validated range
- you want a multi-agent dashboard, model router, API proxy, or terminal multiplexer
Useful checks:
cargo test
cargo clippy --all-targets -- -D warnings
cargo build --releaseProject map:
src/
main.rs CLI entrypoint (carry, resume, restore, snapshots, …)
runtime.rs runtime launch/resume commands
host.rs PTY host, prefix handling, status bar, switch orchestration
trail.rs Constant-owned lineage ledger + record snapshot paths
alembic/
mod.rs distill, sanitize, redact, receipts, discovery, registry steps
ir.rs neutral session model
sha256.rs minimal SHA-256 (gemini keys its store by sha256(cwd))
formats/
claude.rs Claude session reader/writer
codex.rs Codex rollout reader/writer
gemini.rs Gemini session reader (carry source)
opencode.rs OpenCode export-shape reader/writer (import/export based)
The low-level Codex/Claude session codecs and the neutral IR are
vendored from transession (MIT; see
src/alembic/LICENSE.transession). Constant adds the sanitize/redact pass,
native-resume hardening, stable runtime projections, the record, trail naming,
the Gemini and OpenCode codecs, and the live host.
MIT.