Skip to content

release: v0.2.0 — the work outlives the session - #81

Merged
AetherAI3 merged 5 commits into
mainfrom
release/v0.2.0
Aug 19, 2026
Merged

release: v0.2.0 — the work outlives the session#81
AetherAI3 merged 5 commits into
mainfrom
release/v0.2.0

Conversation

@AetherAI3

Copy link
Copy Markdown
Owner

Stacked on #80. Everything here is packaging: no behaviour changes.

Why

npm still serves 0.1.0, published 2026-06-11. Every npm i -g aether-agents today predates agent dev sessions, the durable media history, doctor v2, and the whole of #80. Publishing is release-triggered (.github/workflows/release.yml), so cutting the tag is what ships it.

README first screen

The old hero led with the category — "a coding agent for your terminal" — which is true of every tool on the shelf. It now leads with the two things specific to this one, both reproducible from a clean checkout:

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

Below it: the install command, a runnable handoff example, and npm run demo:handoff. Model tables, pricing, and platform links stay where they are, below the fold.

Two claims corrected, not restated

  • The README credited the local brain to the Unlimited Context engine. That engine is a separate Python package the npm tarball does not carry; the shipped offline brain talks to Ollama directly. Both are now described for what they are, with the Python one shown as the AETHER_LOCAL_BRAIN=python opt-in it is.
  • --test-cmd was documented as defaulting to pytest -q. It has no default — a run without one ends unverified, and never ok.

The old hero paragraph also claimed QOPC memory made the agent "measurably better the more you use it". That is a server-side claim the client cannot demonstrate, so it is out of the first screen.

Version

0.1.00.2.0 in package.json and src/version.ts (the lockstep test covers both), plus the pinned-version examples in README.md and install.sh.

test/production_hardening.test.ts asserted the literal v0.1.0. It reads package.json now — a version bump is a release step, not a reason for the release gate to go red.

Release notes

RELEASE_NOTES.md gains the dated v0.2.0 entry; docs/releases/2026-08-19.md carries the long form and is indexed in docs/releases/README.md.

Verification

  • npm run typecheck — clean
  • version + production_hardening — 9 tests green at 0.2.0, including the pack → global install → aether --versionaether --help smoke against the real tarball
  • npm pack --dry-runaether-agents-0.2.0.tgz, 416 files, 2.0 MB unpacked, zero runtime dependencies

Not done here

Cutting the GitHub release (and therefore the npm publish) is deliberately left to a human — the draft text is ready in docs/releases/2026-08-19.md.

@AetherAI3
AetherAI3 changed the base branch from feat/growth-release to main August 19, 2026 11:33
@AetherAI3

Copy link
Copy Markdown
Owner Author

Retargeted to main so CI actually runs: the workflow triggers on pull_request against main only, so while this pointed at feat/growth-release it reported no checks at all.

Until #80 merges, this PR's diff shows both commits. Merge #80 first and this reduces to the release-packaging commits alone.

`aether resume` could replay a prior session's transcript to the screen, but
the brain never saw a byte of it: continuing meant re-typing the story so far,
and only ever on the machine where the work started. This makes the resume path
mean what it says.

A handoff (core/handoff.ts) is the machine-facing half of a session log — a
small JSON document distilled from one run: the task, the model that ran it,
the verify gate's verdict, the failing-test count, the files it changed, the
verification command, and the repository identity (origin remote, branch, HEAD).

- `aether agent --resume <id>` now prepends a continuation brief built from
  that record to the task the brain receives, so a different model picks the
  thread up with the project context in hand. With no new task, the run
  continues the original one (it used to fail with "nothing to do").
- `aether resume export [id] [--out <file>]` writes the handoff to a file.
  Nothing in it is keyed to an absolute path, so it can be copied to another
  checkout, machine, or OS, and `aether agent --resume <file>` continues there.
  A file reference is deliberately not workspace-scoped; a session id still is.
- The handoff is a summary, not a transcript: no file contents, no shell
  commands, no credential-shaped values. Untrusted files are validated field by
  field on the way back in, and one written by a newer Agent is refused with an
  upgrade hint rather than half-read.

Three fixes the above needed, each a bug in its own right:

- `aether agent --local "<task>"` spawned the separately-installed Python brain
  unconditionally, so a plain `npm i -g aether-agents` could only ever answer
  `spawn python ENOENT`. The one-shot offline path now drives the Ollama brain
  that ships inside the package — the same one the REPL's `--local` turns
  already used. `AETHER_LOCAL_BRAIN=python` opts back in, and the choice is a
  pure decision in core/backend.ts beside chooseBackend.
- The session log's credential filter matched `pat` as a substring, so `path`
  (and PATH, patch, pattern) was stored as "[REDACTED]". Every log could say a
  file changed but not which one. `pat` is now anchored to a whole segment;
  pat, gh_pat and pat_token are still redacted.
- The Ollama brain reported a placeholder `remaining: 1` on any unsuccessful
  run, so an unreachable-Ollama turn printed "1 test failing" when no test had
  run. The failing count comes only from the host's own verify run.

`npm run demo:handoff` (scripts/handoff-demo.ts, docs/demo/handoff.md) is the
end-to-end proof and takes about five seconds: it builds a throwaway git repo
with two genuinely failing tests, runs the real CLI on model A until half the
work is done, exports the handoff, creates a second checkout at a different
path, deletes the first checkout AND its logs, then finishes the job on model B
with `--resume <file>` and no restated task. The model — and only the model —
is a scripted local stub so the run is deterministic and needs no download or
account; AETHER_DEMO_REAL=1 runs the identical script against real Ollama
models. It asserts that session B's prompt carried the brief, that `node --test`
is green when run independently of the agent, and that the verify gate exited 0,
so it works as a CI gate as well as a demo.
…log once

Cleanup pass over the handoff feature. No intended behaviour change except
where a reused helper is strictly better than the hand-rolled version — those
are called out below.

Reuse:
- `writeHandoff` uses `atomicWriteFile` (core/durable_store.ts), the same
  write-then-rename every other durable file in this CLI already uses. An
  interrupted `resume export` can no longer destroy a good handoff or leave
  half a JSON document, and `--out reports/handoff.json` now creates the
  missing parent instead of failing with ENOENT.
- `readHandoff` uses `readJsonFile`, which already distinguishes missing from
  unreadable from corrupt — so the hand-rolled ENOENT check is gone and an
  empty file reads as "corrupt", the codebase's word for it, rather than
  "Unexpected end of JSON input".
- Untrusted strings go through `sanitizeTerm` (ui/text.ts) and `clipCodePoints`
  (ui/theme.ts) instead of a local clipper. A handoff arrives from another
  machine and its strings are BOTH printed and prepended to the brain's prompt,
  so it was the only untrusted-input surface in the CLI not passing through the
  terminal sanitizer. `clipCodePoints` also stops a truncation from cutting a
  surrogate pair in half.
- `wroteFile()` is now the one definition of "the run changed a file", called
  by both a handoff's `filesTouched` and cmdCode's live blast-radius set, which
  had the predicate written out twice.

`isHandoffPath` had a real defect: its separator class held only a forward
slash, so `--resume C:\work\handoff` (no .json) was classified as a session id
and died as "invalid session id". It now inverts `requireOpaqueId` — the same
rule `loadSession` enforces a moment later — so the two branches provably
partition the input, and Windows separators, `..`, and `~/` fall out for free.

Read the session log once. `resolveHandoff` loaded the session, distilled it,
and threw it away; `replaySession` then re-derived the file-vs-id decision and
loaded the same session again to render it. `resolveResume` returns
`{handoff, session}` and `resumeReplayLines` renders from it — one read, one
decision, and `replaySession`'s `ref`/`cwd` parameters disappear. On a
2000-event log that is 2000 fewer JSON parses and decodes per resume.

`aether resume` dispatches on argv like every other subcommand-bearing command
in main.ts, instead of joining `rest` into a string and re-splitting it by
prefix. `cmdResumeExport` is its own exported function. Side effect: `aether
resume abc def` reports `no such session: abc` rather than looking up the id
`"abc def"`.

Smaller: `summarizeEvents` bounds `highlights` as it goes rather than growing
to N and slicing to 40, and uses a Set for `filesTouched`; `parseHandoff`
bounds arrays before clipping them, so a hostile file with 10k highlights costs
40 clips rather than 10k; one `repoFrom` and one `asObject` replace two
spellings each; the demo's two model scripts are one shape with two
substitutions; the demo header no longer restates docs/demo/handoff.md.

Tests: the four resume-command cases share one `withLogRoot` fixture instead of
pasting the same 10-line prologue and env restore four times, and there are new
cases for the Windows path classification, the sanitizer, the shared write
predicate, and `--out` into a missing directory.

Deliberately not done: collapsing readRepoIdentity's three git spawns into two
(one saved spawn, subtler parsing); `latestSession`'s full-log scan (real, but
pre-existing and outside this diff); rebuilding `summarizeEvents` on top of
`monologueLine` (its tree-rendering shape is not a summary's).
The README's first screen led with the category ("a coding agent for your
terminal"), which is true of every tool on the shelf. It now leads with the two
things that are specific to this one and reproducible from a clean checkout:
the verify gate decides when a run is done, and a run's context can leave the
machine it started on.

- README hero: "Start a task on one model. Finish it on another, on another
  machine. Your tests decide when it's done." — followed by the install command,
  a runnable handoff example, and `npm run demo:handoff`.
- Two claims corrected rather than restated. The local brain was credited to
  the Unlimited Context engine, which lives in a separate Python package the npm
  tarball does not carry; the shipped offline brain talks to Ollama directly,
  and the Python one is now described as the opt-in it is. `--test-cmd` was
  documented as defaulting to `pytest -q`; it has no default, and a run without
  one ends `unverified`, never `ok`.
- The version bumps to 0.2.0. npm still serves 0.1.0 from June, so every
  `npm i -g aether-agents` today predates agent dev sessions, the media history,
  doctor v2, and everything in this release. Publishing is release-triggered
  (.github/workflows/release.yml), so cutting the tag is what ships it.
- The production verifier's test read the expected version from a literal
  "v0.1.0". It reads package.json now: a version bump is a release step, not a
  reason for the release gate to go red.

Release notes in RELEASE_NOTES.md and docs/releases/2026-08-19.md.
@AetherAI3
AetherAI3 merged commit 477f0fc into main Aug 19, 2026
5 checks passed
@AetherAI3
AetherAI3 deleted the release/v0.2.0 branch August 19, 2026 12:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant