> ## Documentation Index
> Fetch the complete documentation index at: https://docs.runaether.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Teleport

> Move a live Claude Code or Codex session from your machine to an Aether task

Teleport hands a coding session in progress off to Aether. You are working locally in Claude Code or Codex, you need to close the laptop, and instead of writing a handoff prompt you run one skill: your working tree is pushed to a fresh branch, the conversation transcript is imported, and the [task](/guides/tasks) that opens on Aether resumes that exact conversation with full memory of what you were doing.

Two things travel:

* **The repo's working state.** Every tracked and untracked change (respecting `.gitignore`) is committed to a fresh `aether/teleport-<hex>` branch and pushed to `origin`, along with any local commits `origin` does 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.

## Prerequisites

| Requirement | Detail                                                                                                  |
| ----------- | ------------------------------------------------------------------------------------------------------- |
| Runtime     | Claude Code or Codex. Other agent runtimes cannot teleport.                                             |
| Python      | `python3` 3.9 or newer. The script is standard library only.                                            |
| Repo        | The repo's `origin` must be a GitHub repo [connected to an Aether project](/guides/github-integration). |
| Credential  | A [platform API key](/api-reference/authentication) exported as `AETHER_TOKEN`.                         |

## Installing the Skill

The skill is two files — a `SKILL.md` and the `teleport.py` script it runs. Install them into your agent's skills directory.

<Tabs>
  <Tab title="Claude Code">
    ```bash theme={null}
    mkdir -p ~/.claude/skills/aether && \
      curl -fsSL https://app.runaether.dev/skills/aether/SKILL.md -o ~/.claude/skills/aether/SKILL.md && \
      curl -fsSL https://app.runaether.dev/skills/aether/teleport.py -o ~/.claude/skills/aether/teleport.py
    ```
  </Tab>

  <Tab title="Codex">
    ```bash theme={null}
    mkdir -p ~/.codex/skills/aether && \
      curl -fsSL https://app.runaether.dev/skills/aether/SKILL.md -o ~/.codex/skills/aether/SKILL.md && \
      curl -fsSL https://app.runaether.dev/skills/aether/teleport.py -o ~/.codex/skills/aether/teleport.py
    ```
  </Tab>
</Tabs>

<Note>
  This is a local skill for the CLI on your machine, not an [Aether skill](/guides/skills) uploaded to your account. It has to live where the local agent can load it, because it reads that agent's own session files.
</Note>

## Authenticating

Teleport calls the Aether API as you, using a platform API key. Create one under **Settings → API Keys**, or from the CLI:

```bash theme={null}
aether token create --name "teleport"
```

Export it wherever your shell will have it when the agent runs:

```bash theme={null}
export AETHER_TOKEN="aether_..."
```

<Warning>
  A platform API key is unscoped and does not expire — it can do anything your account can. Treat it like a password: keep it out of the repo, store it in your shell profile or a secret manager, and revoke it with `aether token revoke <id>` if it leaks.
</Warning>

## Teleporting a Session

From the session you want to move, invoke the skill — `/aether`, or just tell the agent to teleport this session to Aether.

<Warning>
  **Teleporting publishes more than your working tree.** The capture is `git add -A`, so anything `.gitignore` does not exclude is committed — a tracked or merely un-ignored `.env`, private key, or `.npmrc` included. The push then puts that commit *and every local commit `origin` does not already have* on the remote. A secret in an unpushed commit therefore teleports from a perfectly clean working tree, and so does one a later commit deleted, because the blob stays reachable through history.

  As a backstop the script scans exactly that set — every file added or modified by the commits the push would newly publish — and aborts, listing them, if any basename looks like a credential (`.env`, `.env.*`, `*.pem`, `*.key`, `id_rsa*`, `id_ed25519*`, `.netrc`, `.npmrc`, `*credentials*`, `service-account*.json`, `*.p12`, `*.pfx`). Files `origin` already has are not reported: teleporting does not make them any more public. To decide that accurately the script runs `git fetch --prune origin` first, because a branch deleted or rewound on the server leaves a stale local tracking ref that would otherwise make an unpublished commit look published. If `origin` cannot be reached the script fails rather than scanning against stale refs.

  The scan matches on **filename only**, so a secret in a file it does not recognize still teleports. Once you have confirmed the flagged files are safe to publish, `--allow-secrets` proceeds.
</Warning>

If the scan flags a file, the fix depends on where it lives:

* **Only in your working tree.** Add it to `.gitignore`, and `git rm --cached <file>` if it is already tracked.
* **Already in an unpushed commit.** `git rm --cached` is 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.

<Steps>
  <Step title="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.
  </Step>

  <Step title="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`.
  </Step>

  <Step title="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.
  </Step>

  <Step title="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.
  </Step>
</Steps>

Once the script succeeds, stop working locally. Edits made after the capture never reach the teleported task.

### Options

The agent passes `--agent` and `--nonce` itself. The rest you can ask it to add:

| Flag                 | When you need it                                                                                                                                                                                                                                                                                                                      |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--message`          | A one-sentence handoff describing the current state and the next step. Defaults to a generic resume instruction.                                                                                                                                                                                                                      |
| `--model`            | Required when the Aether project's default agent differs from the session's runtime — a Codex-default project teleporting a Claude Code session, for example. That default comes from the project's linked agent profile when it has one, and from its task defaults otherwise. The script fails loudly rather than guessing a model. |
| `--reasoning-effort` | Overrides the reasoning effort for the teleported task.                                                                                                                                                                                                                                                                               |
| `--base`             | Sets the base branch explicitly. Needed when the current branch has no upstream and `origin/HEAD` is unset.                                                                                                                                                                                                                           |
| `--allow-secrets`    | Pushes files the secret scan flagged. Only pass it after reading the list the script printed and confirming those files are safe to publish.                                                                                                                                                                                          |
| `--dry-run`          | Runs the local checks and prints the payload without pushing or creating the task.                                                                                                                                                                                                                                                    |

#### What a dry run actually checks

A dry run is a client-side rehearsal, not a preflight against the Aether API. It confirms 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 your `origin` matches an Aether project, that an agent and model resolve, 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. 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. 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 `origin` already has.** A `.env` that is gitignored and was never committed stays behind — and the workspace uses the project's configured [environment variables](/guides/environment-variables) and [secret files](/guides/secret-files) instead, so add anything the session depended on to the project first. A `.env` that 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.

<Note>
  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.
</Note>

## Troubleshooting

The script prints every failure to stderr prefixed with `teleport:` 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 on `origin`.
* 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.

Before you retry, check your [task list](https://app.runaether.dev) for a task on that branch. If one is there, the teleport worked — open it. If not, retry; each run generates a fresh branch name, so retries never collide. Either way, delete branches you have abandoned:

```bash theme={null}
git push origin --delete aether/teleport-<hex>
```

| Error                                                               | Cause and fix                                                                                                                                                                                                                                                                                                            |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `AETHER_TOKEN is not set`                                           | No key in the environment the agent runs in. Create one (**Settings → API Keys** or `aether token create`) and export it as `AETHER_TOKEN`.                                                                                                                                                                              |
| `no session file contains the nonce`                                | The nonce line was not written to the transcript before the script ran, or the script ran from a different directory than the one the session started in. Ask the agent to print a fresh nonce first, and run from the session's directory.                                                                              |
| `no Aether project matches <owner/repo>`                            | The repo's `origin` is not linked to any of your projects. [Import the repo](/guides/github-integration) into a project first. The message lists the repos that do match.                                                                                                                                                |
| `the Aether project's default agent is ... but this session is ...` | The project's default agent is a different runtime. The message names where that default came from — the project's linked agent profile if it has one, otherwise its task defaults. Pass `--model` (and optionally `--reasoning-effort`).                                                                                |
| `pushing this session would publish these files`                    | The push would newly publish files whose names look like credentials; the message lists them. The remedy depends on whether each file is uncommitted, already in an unpushed commit, or previously pushed — see [the fix list above](#teleporting-a-session). Re-run with `--allow-secrets` if they are safe to publish. |
| `HTTP 409: a task already exists for branch ...`                    | A task in that project already claims the head branch. Re-run — each teleport generates a new random branch name.                                                                                                                                                                                                        |
| `session transcript is N bytes; the checkpoint limit is 33554432`   | The session exceeds the 32 MiB checkpoint cap. Start a fresh session with a summary of the work and teleport that.                                                                                                                                                                                                       |
| `could not determine a base branch`                                 | The current branch has no upstream and `origin/HEAD` is unset. Pass `--base <branch>`.                                                                                                                                                                                                                                   |

## API

Teleport is a thin client over one endpoint — [Teleport a session into a task](/api-reference/v1/tasks/post-tasks-teleport). It takes the project, the base and head branches, the agent and model, and the gzipped session transcript, and returns the created task.
