Agent sessions
neo sessions uploads your Claude Code, Codex and Antigravity sessions to the
forge, so a pull request or a commit can link to the conversation that produced
it. Nothing is uploaded on its own until you turn it on: neo sessions setup,
or y to the one question neo auth login asks when a supported agent is
installed (the default is no, and the answer is remembered either way). A
neo sessions upload you run by hand uploads either way.
Set it up
Section titled “Set it up”With neo installed and signed in (neo auth login), run
these where your agent runs:
neo sessions setupneo sessions upload --since 7dsetup uploads every session from now on. It does not reach back, so the
upload sends the sessions you already have: every one changed in the last
seven days (any window works, e.g. 30d). A pull request then lists each
uploaded session that made a commit on it under its Sessions tab, once
that commit is pushed, as long as the commit’s author or committer email is one
of your verified emails or your noreply address (neo sessions status warns
when git config user.email is neither).
How the hook works
Section titled “How the hook works”setup adds a neo sessions hook entry to each agent’s hook config:
- Claude Code:
StopandSessionEndin~/.claude/settings.json(or$CLAUDE_CONFIG_DIR/settings.json); - Codex:
Stopin~/.codex/hooks.jsonandhooks = trueunder[features]in~/.codex/config.toml(or under$CODEX_HOME); - Antigravity:
Stopunder"neosource"in~/.gemini/config/hooks.json(or$ANTIGRAVITY_CONFIG_DIR/hooks.json).
Every other setting and hook in those files stays; any existing file is saved
beside it as <name>.neo.bak before it is rewritten. A JSON file that is not
plain JSON (comments included) is refused, not rewritten. The hook hands each
event to a background neo sessions upload and returns at once: for Claude
Code, a Stop within five minutes of the last upload is not lost but deferred
to one upload at the end of those five minutes, and SessionEnd always runs;
for Codex and Antigravity, Stop always checks whether the transcript changed.
Each hook run also uploads up to three idle sessions whose transcript changed
since their last upload, each only to the forge it was last uploaded to.
What is uploaded
Section titled “What is uploaded”The bundle is built and redacted on your machine, from the transcript files the
agent writes for the session (Claude Code’s <id>.jsonl, subagent transcripts
and tool-results/ files; Codex’s rollout-*.jsonl under
~/.codex/sessions/; Antigravity’s
.system_generated/logs/transcript_full.jsonl and step outputs under
~/.gemini/antigravity-cli/brain/<id>/):
- your messages and the model’s replies: text, thinking/reasoning (without signatures or encrypted blobs), tool calls with their input, and tool results as text;
- timing records, token usage and the session’s title;
- each subagent’s type, description and transcript;
- large tool output files as text, cut to 256 KiB each;
- a header naming the repositories the session touched on this forge
(
owner/name), each work tree’s remote URL on this forge with any credentials removed, and a SHA-256 of the work tree’s local path (never the path itself); - commit claims: commit ids printed by the session’s
gitcommands (a commit’s or push’s summary line, or a line starting with an id, asgit log --onelineandgit rev-parseprint), expanded on your machine to their full 40-character id. A work tree removed since is looked up through the checkout it shared objects with. Only commits whose committer is you (the identitygit var GIT_COMMITTER_IDENTreports) and whose committer time falls inside the session are claimed; each carries the branch it was made on when that is not the default branch. Claims sent once are sent again with each later upload, so a commit that stops resolving locally keeps its link; - pull requests it opened, readied or merged, as
neo prprinted them.
What is dropped
Section titled “What is dropped”- record types the projection does not list: API request records,
attachments (the system prompt,
CLAUDE.mdcontents, your email address), file-history snapshots, and anything Claude Code adds later; - images (kept only as a placeholder), thinking signatures, working directories and branch names of each record;
- the remote URL of a repository that is not on this forge (an employer’s GitHub, say): it counts as one repository the forge cannot resolve, and nothing about it is sent;
- whole sessions that touched no repository on this forge: they are skipped, never uploaded;
- sessions you deleted (see below).
Redaction
Section titled “Redaction”Before anything leaves the machine, every string is redacted: your stored
forge token and the value of every environment variable whose name contains
token, secret, key, passw or credential are masked literally, then
the shared rules mask private keys, JWTs, URL credentials, forge, GitHub,
GitLab, AWS, Anthropic, OpenAI and Slack tokens, key=value secrets and
high-entropy strings. The forge runs the same rules over what it receives and
rejects a bundle they still match (redaction_hit); it never edits one.
Who can see a session
Section titled “Who can see a session”- You, always.
- Everyone else only when signed in, only as the session’s visibility
allows, and only if they have that access to every repository the
session touched:
private: nobody else;repo_writers(the default): whoever can write every touched repository;repo_readers: whoever can read every one.
A session that also touched repositories the forge cannot resolve stays private to everyone else until you set its visibility yourself.
Change it with neo sessions visibility <id> private|writers|readers, where
<id> is the forge id neo sessions list prints or the external session
id. neo sessions delete <id> deletes it; the forge then refuses that
session for good, and this machine stops offering it. neo sessions disable
removes the hook and turns uploads off; what is already uploaded stays.
neo sessions upload --force [<id>…] [--since <window>] re-sends sessions even
when nothing changed since their last upload, so the forge re-checks their
links after it changes how it verifies them. neo sessions status and upload
show how many claims the forge dropped (· N dropped): ids it found no trace
of in the transcript, or that name a repository the session did not touch.
Token scopes
Section titled “Token scopes”Uploading uses your neo auth login token: it needs user:write, a human
account, and a token not locked to particular repositories or workspaces.
Reading someone else’s session through the API needs user:read plus the
repository access the visibility names on every touched repository:
repo:write for repo_writers, repo:read for repo_readers. A token with
repo:read alone therefore cannot see a repo_writers session.
~/.local/state/neosource/sessions.log(under$XDG_STATE_HOMEwhen set): one line per upload or skip, with the session id, the outcome and the reason. It never holds transcript text, and it rotates at 1 MiB.~/.local/state/neosource/sessions/<id>.json: each session’s last upload, its counts and refusal code, and its last skip.neo sessions statusreads these, and also warns whengit config user.emailis neither your verified email nor your noreply address, which keeps commits from linking.~/.local/state/neosource/sessions/<id>.tombstone: a session you deleted.- The consent per forge host is the
sessionskey of the CLI config (see Configuration).