- The repo’s working state. Every tracked and untracked change (respecting
.gitignore) is committed to a freshaether/teleport-<hex>branch and pushed toorigin, along with any local commitsorigindoes not already have. The commit is built through a temporary index, so your local index and working tree are left untouched. Linked worktrees (git worktree add) capture the same way as a primary checkout. - The session transcript. The local CLI’s session file is uploaded and seeded as the task’s resume checkpoint, so the agent continues the conversation rather than starting a new one.
--model and --reasoning-effort override.
The two differ in one way. A model is required, so a transcript with none is an error you fix with --model. A thinking level is optional: sessions that never recorded one — older sessions, and models with no thinking tiers — teleport with the field absent and run at the server’s default.
They travel together, though. The level is read within the newest model’s stretch of the conversation — walking back from the latest turn for the last level actually set, and stopping where the model changes. Not every line records a level, and one that doesn’t is silent rather than a reset, so a level you set stays in force. But it does not survive a model change: a session that switched to a model without thinking tiers teleports at the new model with no level, rather than pairing the new model with the old one’s — a combination the server rejects. --model onto a different model is a model change like any other and drops the transcript’s level for the same reason; --reasoning-effort sets it explicitly when you want one anyway.
Prerequisites
Installing the Skill
The skill is two files — aSKILL.md and the teleport.py script it runs. Install the public bundle with the Aether CLI; this command does not require a login:
aether-teleport. By default the command installs into ~/.claude/skills/aether-teleport when it finds Claude Code and into ~/.agents/skills/aether-teleport when it finds Codex. Both may be installed. To skip detection and install into one exact directory, use --dir:
~/.codex is what selects the Codex target, but the bundle goes to ~/.agents/skills rather than beside it, because Codex scans the repo’s .agents/skills chain, $HOME/.agents/skills, and /etc/codex/skills — never ~/.codex/skills. Only Claude Code and Codex are detected, matching the prerequisite above: other agents read the shared directory, but teleport itself cannot run under them. The installer skips whichever runtime is absent and fails if both are.
No-CLI fallback
If the Aether CLI is not installed, use the shell fallback:AGENT_DIR to install into exactly one directory instead. Both installers download both files before either is put in place, so a failed or interrupted download leaves an existing installation exactly as it was rather than truncating it or half-updating it.
Manual per-agent fallback
Or fetch the two files yourself:- Claude Code
- Codex
This is a local skill for the CLI on your machine, not an Aether skill uploaded to your account. It has to live where the local agent can load it, because it reads that agent’s own session files.
Authenticating
Teleport calls the Aether API as you. It looks for a credential in two places, in order:AETHER_TOKEN in the environment, then the stored key of a logged-in aether CLI (aether auth token). If neither produces one it stops and names both remedies — it never proceeds unauthenticated.
Both sources now yield a platform API key, so neither expires mid-run. Prefer AETHER_TOKEN when the agent’s environment may not inherit your login, or when you want a credential you can revoke independently of the CLI. Create one under Settings → API Keys, or from the CLI:
aether auth login is enough — teleport will ask the CLI for its key when AETHER_TOKEN is unset. That key is valid until revoked, so the fallback keeps working for as long as the machine stays logged in.
Teleporting a Session
From the session you want to move, invoke the skill —/aether-teleport, or just tell the agent to teleport this session to Aether.
If the scan flags a file, the fix depends on where it lives:
- Only in your working tree. Add it to
.gitignore, andgit rm --cached <file>if it is already tracked. - Already in an unpushed commit.
git rm --cachedis not enough here — it records a later deletion while the earlier blob stays reachable, so the push would still publish it and the guard still refuses. Rewrite every commit that contains the file (git rebase -i, or a soft reset and recommit), then re-run. - Ever pushed anywhere. Assume the credential is burned and rotate it. Scrubbing it from history does not un-leak what was already published.
1
The agent prints a nonce
It emits a
teleport-nonce: <token> line into the conversation. That line is how the script identifies which transcript file on disk belongs to this session, so it has to be written before the script runs.2
The script captures and pushes your work
It commits the working state and pushes it to a new
aether/teleport-<hex> branch on origin. The base branch is your current branch if it has an upstream, otherwise origin/HEAD.3
The transcript is imported
The session file is compressed and posted to the teleport endpoint, which validates it and creates the task with the conversation pre-seeded.
4
You get a task URL
The script prints the Aether task URL. Open it and the agent picks up where the local session left off.
Options
The agent passes--agent and --nonce itself. The rest you can ask it to add:
What a dry run actually checks
A dry run is a client-side rehearsal, not a preflight against the Aether API. It confirms that a credential resolves, that the nonce locates a session transcript and that the file is self-consistent, that the transcript is under the 32 MiB checkpoint cap, that a model can be read from it, that yourorigin matches an Aether project, and that no file the push would newly publish trips the secret scan. It reaches the network twice — listing your projects, and running git fetch --prune origin for the secret scan — plus once more if it falls through to the CLI for a token. Nothing you own changes: your working tree, index, and branches are untouched, no branch is pushed, and no task is created. Two pieces of local Git bookkeeping do update, as they would on any fetch: origin/* remote-tracking refs are refreshed, and the capture commit’s objects are written to the local object store (unreferenced, so git gc reclaims them).
It does not validate the transcript’s contents — malformed JSON on a later line passes the client — and it never contacts the teleport endpoint, so server-side transcript validation, model availability, and credential checks are untested. The thinking level is read but never checked: whether the model accepts it is the server’s call, so a dry run prints a reasoning_effort the real run can still reject. A clean dry run can still be followed by a real run that pushes a branch and is then rejected.
What Does Not Teleport
The conversation and the code come across. The rest of your local machine does not:- Gitignored files, and history
originalready has. A.envthat is gitignored and was never committed stays behind — and the workspace uses the project’s configured environment variables and secret files instead, so add anything the session depended on to the project first. A.envthat is not ignored, or that sits in an unpushed commit, teleports; see the warning above. - Running processes. Dev servers, watchers, and background jobs you started locally are not carried over.
- Locally configured MCP servers. The workspace has its own tool surface.
Your local CLI may be a newer version than the one pinned in the workspace. Transcripts are read tolerantly across versions, but a structurally invalid transcript is rejected at import, and a resume that would silently start a fresh session fails the task instead of continuing without memory. Teleport never quietly drops your history.
Troubleshooting
The script prints every failure to stderr prefixed withteleport: and exits non-zero. It never falls back to creating an ordinary task.
Recovering from a failed run
The branch is pushed before the task is created, so a failure is not automatically a clean slate:- A failure after the push leaves an
aether/teleport-<hex>branch onorigin. - If the response is lost in flight, the script reports failure even though the server created the task. Retrying then produces a second branch and a second task.