Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 31 additions & 7 deletions COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,15 @@ suggests `aether auth` and exits `2` instead of spending a turn on "auht".

### `aether code "<task>"` — autonomous coding agent
One host loop drives a pluggable brain: cloud (UVT-metered) by default,
`--local` for the Python/Ollama brain. The host renders every event, executes
`--local` for the built-in Ollama brain. The host renders every event, executes
every tool call locally, and verifies the result itself — the final status is
derived from your test command's exit code, never the brain's self-report.
Every run ends with a verdict line: `✓ ok · 4 files changed · tests green · 3m12s`.

| Flag | Meaning |
|---|---|
| `--local` | Use the local brain (Python/Ollama) instead of the cloud. |
| `--local` | Use the built-in offline Ollama brain instead of the cloud. |
| `--resume <id\|file>` | Continue a prior session id, or a handoff file from another machine. |
| `--pool <gb>` | Context pool size in GB (status-bar reach = pool × 233M tokens). |
| `--effort <t>` | Effort tier: `LOW` \| `MED` \| `HIGH` \| `MAX` \| `ULTRA` \| `CODEPRO` (overrides the saved `/effort` dial). |
| `--test-cmd <c>` | Command the verification gate runs (unverified without it). |
Expand All @@ -90,16 +91,35 @@ aether run kronus "audit this service for race conditions and fix them"
```
> Orchestrators are gated to paid tiers. Neo is available on Solo+; Kronus on Pro+.

### `aether resume [id]` — replay a session
### `aether resume [id | export [id]]` — replay or carry a session
Replays a prior local coding session's transcript from `~/.aether-agent/logs/`.
With no id, resumes the most recent session.
With no id, uses the most recent session in this workspace.
```bash
aether resume # the latest session
aether resume <session-id> # a specific session
aether agent --resume <id> "<task>" # resume, then continue working
aether resume # replay the latest session
aether resume <session-id> # replay a specific one
aether resume export # write ./aether-handoff.json
aether resume export <id> --out h.json # …from a specific session, to a path
```
`export` writes a **handoff**: one portable JSON file carrying the task, the
model that ran it, the verify gate's verdict, the failing-test count, the files
the run changed, the verification command, and the repository identity (origin
remote, branch, HEAD). It carries no file contents, no shell commands, and no
absolute paths, so it can be copied to another checkout, machine, or OS.

Continue from either form:
```bash
aether agent --resume <session-id> "<what to do next>" # same machine
aether agent --resume ./handoff.json # anywhere else
aether agent --resume ./handoff.json --model <other> # …on another model
```
With no new task, the run continues the **original** task. Either way the prior
context is summarized into a continuation brief that the brain reads before its
instruction — you never re-paste the conversation. See
[`docs/demo/handoff.md`](docs/demo/handoff.md) for a runnable end-to-end proof.

> Local-first: sessions are read from disk, so resume works offline. When you stop
> a coding run with Ctrl-C, the exact `aether agent --resume <id>` command is printed.
> A session id is workspace-scoped; a handoff file deliberately is not.

### `aether models [use <id>]` — list / pick a model
- `aether models` — list every model **and** orchestrator visible to your tier.
Expand Down Expand Up @@ -381,6 +401,10 @@ Requires an active orchestrator — switch with `/agent neo` or `/agent kronus`
| `AETHER_LOGIN_URL` | `https://aethersystems.net/platform` | Page `aether auth login` opens. |
| `AETHER_TOKEN` | *(unset)* | Inject a session token (CI / headless / embedding). |
| `AETHER_CONFIG_DIR` | `~/.config/aether` | Config + token + REPL-history directory. |
| `AETHER_LOG_DIR` | `~/.aether-agent/logs` | Where session logs (and therefore `aether resume`) live. |
| `AETHER_BACKEND` | `auto` | `local` \| `cloud` \| `auto` — overrides the config `backend`. |
| `AETHER_LOCAL_BRAIN` | *(unset)* | `python` runs the separately-installed Unlimited-Context brain instead of the built-in Ollama one. |
| `OLLAMA_HOST` | `http://localhost:11434` | Where the offline brain looks for Ollama. |
| `AETHER_STREAM_TIMEOUT_MS` | `120000` | Stream open/idle timeout (ms). `0` disables it. |
| `AETHER_NO_ANIM` | *(unset)* | `1` disables all animated status lines and the thinking pulse. |
| `NO_COLOR` | *(unset)* | Any value disables ANSI colors (https://no-color.org). |
Expand Down
106 changes: 106 additions & 0 deletions docs/demo/handoff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# The handoff demo

> Start a task on one model. Finish it on another, on another machine.
> Your tests decide when it's done.

```bash
npm run demo:handoff
```

This is the reproducible proof behind that sentence, and the script a screen
recording should follow. It runs in about five seconds and needs nothing but a
built checkout — no account, no model download, no network.

## What it does

1. **Machine A.** Builds a throwaway git repo (`slugify`, with an `origin`
remote) containing two genuinely failing tests, and runs the real CLI over it
on model A with `--test-cmd`. The session gets half the job done: lowercasing
and hyphenation land, the whitespace case stays red. The verify gate re-runs
the tests itself and marks the run `incomplete`; the process exits non-zero.
2. **The handoff.** `aether resume export --out handoff.json` distils the
session log into one portable file — the task, the model that ran it, the
verdict, the files that changed, the verification command, and the repository
identity.
3. **Moving machines.** A second checkout is created at a different absolute
path, and machine A's checkout **and its session logs are deleted**. Nothing
the next step does can be quietly reading them, because they no longer exist.
4. **Machine B.** The CLI runs in the second checkout on model B with
`--resume handoff.json` and **no restated task**. The handoff is the only
context it is given. It finishes the job.
5. **Proof.** Three independent checks, all of which must hold:
- the scripted model records session B's first prompt, and it must contain
the continuation brief naming model A and `src/slug.js`;
- `node --test` is run directly by the demo script, outside the agent, and
must be green;
- the CLI's own verify gate must have exited 0.

Any failure prints `FAILED` with the reasons and exits non-zero, so the script
works as a CI gate as well as a demo.

## What is real and what is stubbed

Real: the `aether` CLI, the git repositories, the file edits, the tool
permission gate, the session log, the handoff file, `node --test`, and the
verify gate.

Stubbed by default: **the model, and only the model**. A local HTTP server
speaks Ollama's OpenAI-compatible chat endpoint with scripted tool calls. That
is what makes the run byte-deterministic — a 4B model asked to fix a bug does
something slightly different every time, which is fine for a product and useless
for a gate.

To run the identical script against real models:

```bash
# needs `ollama serve` and both tags pulled
AETHER_DEMO_REAL=1 npm run demo:handoff

AETHER_DEMO_MODEL_A=qwen2.5-coder:7b \
AETHER_DEMO_MODEL_B=qwen3:4b \
AETHER_DEMO_REAL=1 npm run demo:handoff
```

In real mode the models decide what to do, so the transcript varies and the run
can legitimately fail — that is the honest shape of a small local model on a
real task. The verify gate still has the last word either way.

The demo never touches your real configuration: it points `AETHER_CONFIG_DIR`
and `AETHER_LOG_DIR` at a temporary directory, so your token, config, and
session history are untouched, and everything it created is removed on exit.

## Recording it

The sequence below is the 20–45 second version, readable with the sound off.
Nothing here is staged: every frame is the script's own output.

| Beat | Seconds | On screen |
|---|---|---|
| 1. The task | 0–6 | `aether agent --model <A> "make the slugify tests pass"` — the agent reads, edits, runs the tests |
| 2. Not done | 6–12 | the red verdict line: `✗ incomplete · tests failing` |
| 3. The handoff | 12–18 | `aether resume export --out handoff.json` and the `⇄ handoff written` line |
| 4. Moving | 18–24 | `cd` into the second checkout; `rm -rf` the first one |
| 5. Continue | 24–36 | `aether agent --model <B> --resume handoff.json` — no task typed, the agent picks up where A stopped |
| 6. Done | 36–45 | the green verdict line: `✓ ok · tests green` |

Capture:

```bash
# 1. build, so the run is instant on camera
npm ci && npm run build

# 2. set the terminal to 100x30 and record
asciinema rec handoff.cast -c "npm run demo:handoff"

# 3. or, for a GIF
# (agg is asciinema's own renderer: https://github.com/asciinema/agg)
agg --font-size 18 --theme dracula handoff.cast handoff.gif
```

`AETHER_NO_ANIM=1` is set inside the demo for the child processes, so the output
is stable text rather than a repainting status line — which is what you want for
a GIF. For a live-feel recording of the product itself, run the two `aether`
commands by hand instead, with animation on.

Do not re-time or re-cut the verdict lines. The whole point of the last beat is
that a test run, not a model, decided it.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
"smoke": "npm run build && node -e \"import('./dist/src/core/smoke.js').then(m=>m.smokeMain()).then(c=>process.exit(c)).catch(e=>{console.error(e);process.exit(1)})\"",
"verify:production": "npm run build && node dist/scripts/verify-production.js",
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
"prepack": "npm run build"
"prepack": "npm run build",
"demo:handoff": "npm run build && node dist/scripts/handoff-demo.js"
},
"keywords": [
"aether",
Expand Down Expand Up @@ -72,4 +73,3 @@
"typescript": "^7.0.2"
}
}

Loading