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
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- main
- release/0.3
pull_request:
branches:
- main
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release-truth.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- main
- release/0.3
schedule:
- cron: '17 11 * * *'
workflow_dispatch:
Expand Down
17 changes: 14 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,22 @@ jobs:
fetch-depth: 0
persist-credentials: false

- name: Verify release commit belongs to main
- name: Verify release commit belongs to a trusted release line
shell: bash
run: |
git fetch --no-tags origin main
git merge-base --is-ancestor HEAD origin/main
git fetch --no-tags origin main release/0.3

if git merge-base --is-ancestor HEAD origin/main; then
exit 0
fi

if [[ "$RELEASE_TAG" =~ ^v0\.3\.[0-9]+$ ]] && \
test "$(git rev-parse HEAD)" = "$(git rev-parse origin/release/0.3)"; then
exit 0
fi

echo "Release commit is not on main or the exact v0.3 maintenance head" >&2
exit 1

- name: Set up Node.js and npm registry
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

<!-- SOURCE-0.3-WORKFLOWS:START -->

> **Requires 0.3.0 or newer.** Check the installed CLI with `aether --version`.
> **Requires 0.3.2 or newer.** Check the installed CLI with `aether --version`.

## Quickstart

Expand Down Expand Up @@ -171,7 +171,7 @@ badge or `npm view aether-agents version` for the npm `latest` dist-tag, and
|---|---:|---|
| npm `latest` | [![npm latest](https://img.shields.io/npm/v/aether-agents?label=&color=14b8a6)](https://www.npmjs.com/package/aether-agents) | Published package; the badge resolves the live dist-tag. |
| PyPI `aether-agent` | [![PyPI latest](https://img.shields.io/pypi/v/aether-agent?label=&color=3775a9)](https://pypi.org/project/aether-agent/) | Launcher that installs and runs the npm CLI; it fetches the npm `latest` dist-tag unless you pin one. |
| `main` source build | **0.3.0** | Current repository source and its 0.3 workflow. |
| `main` source build | **0.3.2** | Current repository source: the 0.3 workflow plus every 0.3.1 maintenance fix. |

The [release record](docs/releases/2026-08-22.md),
[release notes](RELEASE_NOTES.md), and
Expand Down
28 changes: 28 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
# Aether Agent v0.3.2 — one line again

**September 3, 2026**

- **The v0.3.1 fixes are on `main`.** v0.3.1 was released from a branch that was
never merged, so `main` moved forward without its browser-launcher
verification and browser-login recovery path — while still declaring `0.3.0`,
a version older than the one on the registry. That line is merged, and the
declared version leads the published one again.
- **Browser sign-in reports what actually happened.** Opening the sign-in page
now waits for the operating system launcher to start or fail, so a machine
with no browser says so instead of reporting a launch it only attempted.
- **No new surface.** This patch adds no command, flag, or contract of its own.

---

# Aether Agent v0.3.1 — first-run recovery

**August 28, 2026**

- **Clearer first-run recovery.** The CLI now stops before executing on an
unsupported Node.js runtime and prints the upgrade path. Browser-based sign-in
continues with the displayed URL and code when the system browser cannot open.
- **No feature expansion.** This is a compatibility patch over v0.3.0; it does
not include the later device-runtime work on `main`.

---

# Aether Agent v0.3.0 — durable work, verified delivery

**Release candidate finalized August 27, 2026**
Expand Down
7 changes: 7 additions & 0 deletions docs/releases/2026-08-28.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Aether Agent v0.3.1 — first-run recovery

This patch makes two startup failures recoverable: an unsupported Node.js
runtime explains how to upgrade before command handling begins, and browser
sign-in keeps the displayed URL and code usable when a browser cannot be
opened. It intentionally excludes the later feature work already present on
`main`.
14 changes: 14 additions & 0 deletions docs/releases/2026-09-03.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Aether Agent v0.3.2 — one line again

This patch reunites the v0.3.1 maintenance line with `main`. v0.3.1 was released
from a branch that was never merged, so `main` carried on without its
browser-launcher verification and browser-login recovery, while still declaring
`0.3.0` — a version behind the one the registry was serving. The merge lands
those fixes and takes the next patch number, so the source tree leads the
published version again.

It adds no command, flag, or contract of its own. The device-runtime, telemetry,
and session-library work already on `main` is carried by the merge and is not
announced as new here.

Packet: [OPERATOR-PACKET-v0.3.2.md](OPERATOR-PACKET-v0.3.2.md).
56 changes: 56 additions & 0 deletions docs/releases/OPERATOR-PACKET-v0.3.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Operator packet — Aether Agent v0.3.1

This packet records a narrowly scoped patch candidate. It is not a release
authorization: the tag, GitHub release, and npm publication remain prohibited
until the final pull-request commit has passed the required hosted checks.

| | |
|---|---|
| Package | `aether-agents` |
| Proposed tag | `v0.3.1` |
| Release line base | `fb7ceb9c78fdacf84a864a07523185fb4387f531` (`v0.3.0`) |
| Candidate branch | `codex/patch-release-031` |
| Release scope | First-run recovery fixes only: early Node.js-version guidance and browser-login fallback messaging. |
| Deliberately excluded | Later feature work on `main`, including the device-runtime surface and other unreleased command changes. |
| Selected-change provenance | Equivalent, selectively applied portions of `44f1223f62e83c14e8d7741d8590ce61721e3fab` (first-run hardening), reviewed against v0.3.0. |
| Required qualification | Exact-head Windows, Linux, CodeQL, supply-chain, documentation, package, and release-truth checks; exact-package install canaries; then the release workflow’s trusted publication and provenance attestation. |
| Archive evidence | No release archive exists. The current local Windows `npm pack --dry-run --json --ignore-scripts` reported `aether-agents-0.3.1.tgz`, 837,371 packed bytes, 3,695,485 unpacked bytes, `shasum` `889864b707b5611eefa93461bde5f90757538731`, and integrity `sha512-wJjcMrxHQbdo7RfTciMD7evZJSt2wo1CNsrONNRI8w4zPeBILxNG+2/1WHaH7RVFeXz563gp1T4Bd0abpgTHqw==`; dry-run metadata is not a release checksum or provenance attestation. |
| Package manifest | The same current local dry run reported 618 entries. Hosted exact-head package evidence remains required before release. |
| Provenance evidence | Pending the trusted-publishing workflow — no v0.3.1 provenance attestation exists yet. |
| Rollback | Restore npm `latest` to `0.3.0` if a post-publication regression is confirmed; do not unpublish a released version. |

## Version decision

`0.3.0 → 0.3.1` is appropriate because the candidate changes recovery
messaging and failure handling without adding a public feature or changing a
supported command contract. The complete `v0.3.0...main` range is not a patch
candidate and is intentionally outside this release line.

## Qualification record

The final merge commit, workflow run IDs, artifact digests, package integrity,
SBOM, provenance attestation, and platform canary results are added only from
the corresponding immutable hosted evidence. Until then this candidate is not
publishable.

## Commands retained without an explicit release-note invocation

These existing visible commands are intentionally retained without adding a
new v0.3.1 announcement. The named exemptions are reviewed at each release so
the patch does not silently add a command surface:

- `aether help`
- `aether chat`
- `aether run`
- `aether agents`
- `aether github`
- `aether vault`
- `aether workflow`
- `aether memory`
- `aether image`
- `aether video`
- `aether output`
- `aether audit`
- `aether receipt`
- `aether mcp`
- `aether config`
60 changes: 60 additions & 0 deletions docs/releases/OPERATOR-PACKET-v0.3.2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Operator packet — Aether Agent v0.3.2

This packet records the candidate that reunites the v0.3.1 maintenance line with
`main`. It is not a release authorization: the tag, GitHub release, and npm
publication remain prohibited until the final pull-request commit has passed the
required hosted checks.

| | |
|---|---|
| Package | `aether-agents` |
| Proposed tag | `v0.3.2` |
| Release line base | `c4a16242ad117a499f91e3b531baa80f1a3ff0bd` (`v0.3.1`) |
| Candidate branch | `fix/reconcile-v031-into-main` |
| Release scope | Reconciliation. v0.3.1 was released from `codex/patch-release-031`, which was never merged, so `main` shipped forward without the browser-launcher verification (`openTargetChecked` / `openBrowserAwaitLaunch`), the async browser-login path, and the maintenance-line release guards — while still declaring `0.3.0`. This candidate merges that line into `main` and takes the next patch number, so the declared version is once again ahead of the published `latest`. |
| Deliberately excluded | No new feature work. The device-runtime, telemetry, and session-library work already on `main` is carried by the merge, not introduced here, and is not announced as new in this entry. |
| Selected-change provenance | Full merge of `v0.3.1` (`c4a1624`) into `main` at `5de5aa24c4b6e753affdcf46934499293a286dd0`. Five conflicts resolved: `README.md` and `test/login.test.ts` kept `main`'s rebuilt copy; `src/commands/login.ts` took the v0.3.1 async launcher path; `package.json` and `src/main.ts` were reconciled by hand. |
| Version-drift record | Before this candidate, npm `latest` served `0.3.1` while `main` declared `0.3.0`, so the source tree claimed a version older than the published one and lacked its fixes. `v0.3.1` is now an ancestor of `main`, and that class of drift is detectable by `git merge-base --is-ancestor`. |
| Required qualification | Exact-head Windows, Linux, CodeQL, supply-chain, documentation, package, and release-truth checks; exact-package install canaries; then the release workflow's trusted publication and provenance attestation. |
| Archive evidence | No release archive exists. The current local Windows `npm pack --dry-run --json --ignore-scripts` reported `aether-agents-0.3.2.tgz`, 1,387,500 packed bytes, 4,461,548 unpacked bytes, `shasum` `3e22664fe2bc647efe87a72a4d4fb95cb5566e83`, and integrity `sha512-AKvvYi40h64DvOylTk3qDKbs6k+MLP5kt3tUBtoQUDLcu+nTOiEnPxYDbmcmelynROKZjPVnsKxzlOCFJfodow==`; dry-run metadata is not a release checksum or provenance attestation. |
| Package manifest | The same current local dry run reported 673 entries. Hosted exact-head package evidence remains required before release. |
| Provenance evidence | Pending the trusted-publishing workflow — no v0.3.2 provenance attestation exists yet. |
| PyPI launcher | `packages/pypi-cli` publishes separately as `aether-agent` and is synced to this version by `node packages/sync-version.mjs`. It installs the npm `latest` dist-tag rather than its own version, so it is not a second copy of this release. |
| Rollback | Restore npm `latest` to `0.3.1` if a post-publication regression is confirmed; do not unpublish a released version. |

## Version decision

`0.3.1 → 0.3.2` is appropriate because this candidate publishes no new command,
flag, or contract of its own. It carries the v0.3.1 fixes onto `main` and
restores an ordering the registry already assumed. The feature work that has
accumulated on `main` is carried along by the merge but is not announced here;
whichever release chooses to announce it should take the next minor number.

## Qualification record

The final merge commit, workflow run IDs, artifact digests, package integrity,
SBOM, provenance attestation, and platform canary results are added only from
the corresponding immutable hosted evidence. Until then this candidate is not
publishable.

## Commands retained without an explicit release-note invocation

These existing visible commands are intentionally retained without adding a new
v0.3.2 announcement. The named exemptions are reviewed at each release so the
patch does not silently add a command surface:

- `aether help`
- `aether chat`
- `aether run`
- `aether agents`
- `aether github`
- `aether vault`
- `aether workflow`
- `aether memory`
- `aether image`
- `aether video`
- `aether output`
- `aether audit`
- `aether receipt`
- `aether mcp`
- `aether config`
7 changes: 7 additions & 0 deletions docs/releases/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ manifest, the evidence gathered, and the founder-owned steps that publish it.

## Index

- [2026-09-03](2026-09-03.md) — **v0.3.2** patch candidate: the v0.3.1
maintenance line merged back into `main`, restoring the browser-launcher
verification and putting the declared version ahead of the published one.
Packet: [OPERATOR-PACKET-v0.3.2.md](OPERATOR-PACKET-v0.3.2.md).
- [2026-08-28](2026-08-28.md) — **v0.3.1** patch candidate: actionable
Node.js-version recovery and browser-login fallback. Packet:
[OPERATOR-PACKET-v0.3.1.md](OPERATOR-PACKET-v0.3.1.md).
- [v0.3.0 release body](RELEASE-BODY-v0.3.0.md) — publish-ready user-facing
notes for the GitHub release. It deliberately makes no pre-publication npm
claim; the dated publication record is added only after registry verification.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "aether-agents",
"version": "0.3.0",
"version": "0.3.2",
"description": "Open-source terminal coding agent for hosted models or local Ollama, with repository-aware tools, operator-controlled execution, and verified results.",
"type": "module",
"bin": {
Expand Down
2 changes: 1 addition & 1 deletion packages/pypi-cli/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ build-backend = "hatchling.build"
# fails the build if they drift.
[project]
name = "aether-agent"
version = "0.3.0"
version = "0.3.2"
description = "Install and run Aether Agent, the open-source terminal coding agent, from pip or pipx."
readme = "README.md"
requires-python = ">=3.10"
Expand Down
2 changes: 1 addition & 1 deletion packages/pypi-cli/src/aether_agent/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from __future__ import annotations

__version__ = "0.3.0"
__version__ = "0.3.2"

#: The npm package this launcher installs and runs.
NPM_PACKAGE = "aether-agents"
Expand Down
8 changes: 8 additions & 0 deletions scripts/verify-production.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,14 @@ export function validateWorkflowText(name: string, text: string): string[] {
if (!/git merge-base --is-ancestor HEAD origin\/main/.test(text)) {
errors.push(`${name}: release commit ancestry to main must be verified`);
}
if (/origin\/release\/0\.3/.test(text)) {
if (!text.includes('[[ "$RELEASE_TAG" =~ ^v0\\.3\\.[0-9]+$ ]]')) {
errors.push(`${name}: the release/0.3 exception must be restricted to numeric v0.3.x tags`);
}
if (!text.includes('test "$(git rev-parse HEAD)" = "$(git rev-parse origin/release/0.3)"')) {
errors.push(`${name}: v0.3.x publication must equal the exact release/0.3 head`);
}
}
if (!/npm install --global[^\n]*--ignore-scripts/.test(text)) {
errors.push(`${name}: the exact tarball must pass an install smoke test`);
}
Expand Down
8 changes: 4 additions & 4 deletions src/commands/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type { AppContext } from "../core/context.js";
import { loginWithPassword } from "../core/auth.js";
import { LOGOUT_PATH } from "../core/transport.js";
import { requestDeviceCode, pollForToken } from "../core/device.js";
import { openBrowserChecked } from "../core/browser.js";
import { openBrowserAwaitLaunch } from "../core/browser.js";
import type { OpenOutcome } from "../core/opener.js";
import { theme } from "../ui/theme.js";
import { errorHint, errorMessage } from "../core/errors.js";
Expand All @@ -27,10 +27,10 @@ export interface LoginOpts {
}

export interface LoginDependencies {
openBrowser: (url: string) => OpenOutcome;
openBrowser: (url: string) => OpenOutcome | Promise<OpenOutcome>;
}

const LOGIN_DEPENDENCIES: LoginDependencies = { openBrowser: openBrowserChecked };
const LOGIN_DEPENDENCIES: LoginDependencies = { openBrowser: openBrowserAwaitLaunch };

/** After a successful login, flag a shell-level AETHER_TOKEN: it is re-read by
* every NEW process and would shadow the token just stored — the classic
Expand Down Expand Up @@ -113,7 +113,7 @@ export async function cmdLogin(
if (opts.noBrowser) {
process.stdout.write("Browser not opened (--no-browser); use the URL and code above.\n");
} else {
const opened = dependencies.openBrowser(code.verification_uri_complete);
const opened = await dependencies.openBrowser(code.verification_uri_complete);
if (opened.status !== "spawned") {
process.stderr.write(
`⚠ Browser was not opened (${opened.status}). Use the URL and code above, ` +
Expand Down
8 changes: 7 additions & 1 deletion src/core/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// `cmd /c start "" <url>` on Windows, which handed the URL to the command
// interpreter as a token.

import { openTarget, type OpenOutcome } from "./opener.js";
import { openTarget, openTargetChecked, type OpenOutcome } from "./opener.js";

/** Open `url` in the default browser. Never throws. */
export function openBrowser(url: string): void {
Expand All @@ -19,3 +19,9 @@ export function openBrowser(url: string): void {
export function openBrowserChecked(url: string): OpenOutcome {
return openTarget(url);
}

/** Wait for the initial OS launcher outcome when an interactive recovery path
* needs to tell the user whether the browser was actually started. */
export function openBrowserAwaitLaunch(url: string): Promise<OpenOutcome> {
return openTargetChecked(url);
}
Loading