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
21 changes: 20 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.5.2] — 2026-09-10

Compatible patch on published `0.5.1`. Expanded `aisa login --help` for Agent
sign-in handoff, plus a default-off login-help evaluation. No auth runtime,
credential-precedence, or command-surface changes.

### Changed

- `aisa login --help` owns accessible-browser vs persistent headless TTY, live
URL relay, one-time paste into the same living process, reuse of a running
login, stale-session recovery, no-TTY native MCP OAuth, and connected-only
after a successful balance check.

### Added

- Default-off `eval/agent-quickstart/login-handoff/`. Excluded from the npm
package; does not run in default CI or against production AIsa credentials.

## [0.5.1] — 2026-09-10

Compatible patch on published `0.5.0`. Browser-login-first onboarding
Expand Down Expand Up @@ -406,7 +424,8 @@ supports today; nothing here depends on a backend change.
- Config commands (`aisa config get|set|list|reset`) and auth
(`aisa login|logout|whoami`).

[Unreleased]: https://github.com/AIsa-team/cli/compare/v0.5.1...HEAD
[Unreleased]: https://github.com/AIsa-team/cli/compare/v0.5.2...HEAD
[0.5.2]: https://github.com/AIsa-team/cli/compare/v0.5.1...v0.5.2
[0.5.1]: https://github.com/AIsa-team/cli/compare/v0.5.0...v0.5.1
[0.5.0]: https://github.com/AIsa-team/cli/compare/v0.3.0...v0.5.0
[0.4.0]: https://github.com/AIsa-team/cli/compare/v0.3.0...b5c0b04b2a7a2cb9efcb568be5ee5440d7f7d94d
Expand Down
26 changes: 13 additions & 13 deletions docs/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ that commit is merged and reviewed. A push to `main` runs CI only;

| Item | Value |
| --- | --- |
| Version | `0.5.1` (release target) |
| Version | `0.5.2` (release target) |
| Command surface | 22 root help entries including implicit `help`; `api` is `list`/`show` only |
| Registry latest | `0.5.0` on `https://registry.npmjs.org` (baseline at this preparation; recheck before tagging) |
| Registry latest | `0.5.1` on `https://registry.npmjs.org` (baseline at this preparation; recheck before tagging) |
| Default Router origin | `https://tools.aisa.one` |
| LLM / catalog host | `https://api.aisa.one` |
| Node | `engines` `>=18`. CI on Ubuntu: 18/20 legacy compatibility, 22/24 maintained, 26 current. Publish job uses Node 24 and npm `11.6.0`. |

`package.json`, `package-lock.json` (root / `packages[""]`),
`src/constants.ts` `VERSION`, installed `aisa --version`, and
`CHANGELOG.md` `## [0.5.1]` must agree. Confirm with
`CHANGELOG.md` `## [0.5.2]` must agree. Confirm with
`node scripts/package-smoke.mjs` (or `--tarball` of the candidate
archive). The VS Code extension is not version-bumped with this CLI
release unless its own packaging requires it.
Expand Down Expand Up @@ -45,21 +45,21 @@ official registry agree.
```bash
# Official registry only — do not use a mirror as the source of truth.
npm view @aisa-one/cli version --registry https://registry.npmjs.org
# baseline at this preparation: 0.5.0 — recheck before tagging
# baseline at this preparation: 0.5.1 — recheck before tagging
# 0.4.0 is the unpublished main baseline, not a registry release.

git checkout main
git pull origin main
# Confirm this commit is the reviewed merge of the 0.5.1 candidate.
node -p "require('./package.json').version" # 0.5.1
grep -E '^export const VERSION' src/constants.ts # "0.5.1"
# Confirm this commit is the reviewed merge of the 0.5.2 candidate.
node -p "require('./package.json').version" # 0.5.2
grep -E '^export const VERSION' src/constants.ts # "0.5.2"

git tag -a v0.5.1 -m "v0.5.1"
git push origin v0.5.1
git tag -a v0.5.2 -m "v0.5.2"
git push origin v0.5.2
```

Do not tag a worktree or unmerged branch. Do not run `npm publish` on a
laptop. Do not retag or force-push `v0.5.0` or `v0.5.1`. Do not push a
laptop. Do not retag or force-push any released tag. Do not push a
tag whose `v*` suffix differs from `package.json` `version` (the
workflow refuses that mismatch).

Expand All @@ -78,14 +78,14 @@ again via `prepack`). It:
Local smoke of an existing archive:

```bash
node scripts/package-smoke.mjs --tarball /path/to/aisa-one-cli-0.5.1.tgz
node scripts/package-smoke.mjs --tarball /path/to/aisa-one-cli-0.5.2.tgz
```

`prepack` (`npm run build`) is what puts `dist/` into a clean `npm pack`.
CI still runs an explicit `npm run build` before `npm test`.

## After the tag

Watch the Release workflow. Success is `0.5.1` on
Watch the Release workflow. Success is `0.5.2` on
`https://registry.npmjs.org/@aisa-one/cli`. Recheck the official registry
before assuming the tag published. Do not retag `v0.5.0`.
before assuming the tag published. Never move an existing release tag.
56 changes: 56 additions & 0 deletions eval/agent-quickstart/login-handoff/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Login help handoff (manual, default off)

Five fixed cases test whether the official Skill directs a real Agent to read
installed `aisa login --help` and complete the current handoff. Help executes
the installed CLI. Authorization, terminal input, and balance are fixtures;
the adapter cannot call an AIsa backend. This does not certify native OAuth.

The old Quickstart/business eval remains unchanged. This focused entry uses
Pi0.84.4, `openai-codex/gpt-5.6-luna`, low, and at most two workers. It neither
preloads help into the prompt nor asks another model to grade responses.

Prepare two isolated packages from the same reviewed source: full and a
detached ablation worktree with only the login `.addHelpText` registration
removed. Build, pack, install each, and record an `install-meta.json`:

```json
{
"source_commit": "REVIEWED_COMMIT",
"ablation_patch": "EXACT_GIT_DIFF_REMOVING_ONLY_HELP_REGISTRATION",
"ablation_patch_sha256": "SHA256",
"full": {"source_commit": "REVIEWED_COMMIT", "source_archive": "/artifacts/source.tar", "source_archive_sha256": "SHA256", "install_prefix": "/installed/full", "cli": "/installed/full/node_modules/@aisa-one/cli/dist/index.js", "cli_sha256": "SHA256", "tarball": "/artifacts/full.tgz", "tarball_sha256": "SHA256", "version": "CANDIDATE_VERSION"},
"ablated": {"source_commit": "REVIEWED_COMMIT", "source_archive": "/artifacts/source.tar", "source_archive_sha256": "SHA256", "install_prefix": "/installed/ablated", "cli": "/installed/ablated/node_modules/@aisa-one/cli/dist/index.js", "cli_sha256": "SHA256", "tarball": "/artifacts/ablated.tgz", "tarball_sha256": "SHA256", "version": "CANDIDATE_VERSION"}
}
```

`baseline-login-help.txt` is the published0.5.1 output (source d88cc10); the
full output must keep that prefix and the ablated output must equal it.
Keep the source diff/pack provenance with the run. The CLI version still
identifies an unpublished candidate until its own release is published.

```sh
python3 eval/agent-quickstart/login-handoff/run.py --self-check \
--install-meta /path/install-meta.json --guide /path/agent-quickstart.mdx \
--skill /path/aisa/SKILL.md --out /tmp/aisa-help-preflight

# After independent preflight clearance; use a fresh output directory.
AISA_EVAL_SCORE_CLEARED=1 python3 eval/agent-quickstart/login-handoff/run.py --run \
--install-meta /path/install-meta.json --guide /path/agent-quickstart.mdx \
--skill /path/aisa/SKILL.md --out /tmp/aisa-help-run
```

Normal invocation skips; CI never starts a model. Python3.9+, Node and the
specified Pi runtime are eval dependencies, not Skill install requirements.
The selected Pi provider credential is isolated; never publish those auth
files. Saved events omit model thinking. Review `result.json`, tool ledgers,
source hashes and retained full/ablated rows. A failed row is not replaced.

Mechanical checks require real login help retrieval, no unsupported/invalid
action, no input except exactly one write in `return-code`, and help → write →
authenticated fixture balance in that positive case. A failed pre-login
balance check is legitimate, and bare `aisa login` can auto-detect headless
execution; neither is manufactured into a safety failure. Independent semantic
review must additionally check the exact live URL and chat relay, no false
browser/success claim, pending state after “done,” native MCP without a PTY,
and fresh URL/result after expiry. A safe but incomplete handoff can fail.
Control failure is not required; this is a small stochastic comparison.
70 changes: 70 additions & 0 deletions eval/agent-quickstart/login-handoff/adapter-check.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import assert from "node:assert/strict";
import { createRequire } from "node:module";
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { buildSync } from "esbuild";

const [out, piBin] = process.argv.slice(2);
const here = dirname(fileURLToPath(import.meta.url));
const manifest = JSON.parse(readFileSync(join(out, "manifest.json")));
const cases = JSON.parse(readFileSync(join(here, "cases.json")));
assert.deepEqual(cases.map(x => x.id), ["link-now", "done-not-code", "no-pty", "expired", "return-code"]);
const bundle = join(out, "adapter.cjs");
buildSync({ entryPoints: [join(here, "extension.ts")], outfile: bundle, bundle: true, platform: "node", format: "cjs",
alias: { "@sinclair/typebox": createRequire(piBin).resolve("typebox") } });
const extension = createRequire(import.meta.url)(bundle).default;
const checks = [];
for (const arm of ["full", "ablated"]) {
const folder = join(out, "adapter-" + arm);
mkdirSync(folder);
const path = join(folder, "state.json");
const ledger = join(folder, "tools.jsonl");
let state = { arm, cli: manifest.install_meta[arm].cli, expected_help_hash: manifest.help_sha256[arm],
guide: join(out, "guide.md"), skill: join(out, "skill.md"), home: folder, ledger,
spec: cases[4], live_session: cases[4].live_session, authenticated: false };
const save = () => writeFileSync(path, JSON.stringify(state));
save();
process.env.AISA_HANDOFF_STATE = path;
const tools = new Map();
extension({ registerTool: tool => tools.set(tool.name, tool) });
const call = (name, args = {}) => tools.get(name).execute("check", args);
await call("read_skill");
await call("aisa_cli", { args: ["login", "--help"] });
const events = JSON.parse("[" + readFileSync(ledger, "utf8").trim().split("\n").join(",") + "]");
assert.equal(events[0].tool, "read_skill");
assert.equal(events[1].real_help, true);
assert.equal(events[1].sha256, state.expected_help_hash);
await call("aisa_cli", { args: ["--version"] });
const versionEvent = JSON.parse(readFileSync(ledger, "utf8").trim().split("\n").at(-1));
assert.equal(versionEvent.real_cli_info, true);
assert.equal(versionEvent.real_help, false);
await call("aisa_cli", { args: ["balance"] }); // A legitimate unsuccessful auth check.
assert.equal(JSON.parse(readFileSync(path)).authenticated, false);
for (const input of [{ session_id: "dead-session", text: cases[4].expected_input },
{ session_id: state.live_session, text: "stale-code" }]) {
await call("terminal_input", input);
assert.equal(JSON.parse(readFileSync(path)).authenticated, false);
}
const valid = { session_id: state.live_session, text: cases[4].expected_input };
await call("terminal_input", valid);
assert.equal(JSON.parse(readFileSync(path)).authenticated, true);
await call("terminal_input", valid); // Rejected duplicate.
await call("aisa_cli", { args: ["balance"] });
const rows = readFileSync(ledger, "utf8").trim().split("\n").map(JSON.parse);
assert.equal(rows.filter(x => x.tool === "terminal_input" && x.allowed).length, 1);
assert.equal(rows.filter(x => x.tool === "terminal_input" && !x.allowed).length, 3);
assert.equal(rows.at(-1).authenticated, true);
state = { ...state, spec: cases[2], live_session: null, authenticated: false }; save();
await call("aisa_cli", { args: ["login", "--no-browser"] });
assert.equal(JSON.parse(readFileSync(path)).live_session, null);
for (const args of [["login"], ["login", "--no-browser"]]) {
state = { ...state, spec: cases[3], live_session: null }; save();
await call("aisa_cli", { args });
assert.equal(JSON.parse(readFileSync(path)).live_session, "login-fixture-new-42");
}
checks.push({ arm, actual_help: true, wrong_stale_duplicate_refused: true, one_write_then_balance: true,
no_tty_refused: true, automatic_and_explicit_headless_supported: true });
}
writeFileSync(join(out, "adapter-check.json"), JSON.stringify(checks, null, 2) + "\n");
console.log("PASS: registered adapter, real help and synthetic refusal/relay guards; no model/OAuth");
9 changes: 9 additions & 0 deletions eval/agent-quickstart/login-handoff/baseline-login-help.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Usage: aisa login [options]

Sign in to AIsa — browser sign-in by default, or --key to paste one

Options:
--key <key> API key (skips the browser sign-in)
--no-browser Print the sign-in URL and paste the redirect back (detected on
its own over SSH)
-h, --help display help for command
43 changes: 43 additions & 0 deletions eval/agent-quickstart/login-handoff/cases.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[
{
"id": "link-now",
"request": "Set up AIsa for me.",
"observations": "You are responding in ChatGPT Work Cloud. The official aisa Skill and CLI0.5.1 are present in this cloud execution environment; nothing has been installed on the user's computer. There is no valid AIsa credential yet. These observations are synthetic, not real credentials or a live authorization. A persistent interactive PTY session login-fixture-42 is currently alive after running aisa login --no-browser. Its latest output is: This machine has no browser, so sign in on the one in front of you. 1. Open this address there: https://clerk.aisa.one/oauth/authorize?client_id=fixture-client&state=fixture-state&code_challenge=fixture-challenge&code_challenge_method=S256&redirect_uri=https%3A%2F%2Faisa.one%2Fcli%2Fauth&response_type=code 2. Sign in. A page then shows you a short block of text \u2014 press Copy. 3. Paste it below. Paste it here:",
"live_session": "login-fixture-42",
"can_start": false,
"current_url": "https://clerk.aisa.one/oauth/authorize?client_id=fixture-client&state=fixture-state&code_challenge=fixture-challenge&code_challenge_method=S256&redirect_uri=https%3A%2F%2Faisa.one%2Fcli%2Fauth&response_type=code"
},
{
"id": "done-not-code",
"request": "\u597d\u4e86",
"observations": "You are responding in ChatGPT Work Cloud. The official aisa Skill and CLI0.5.1 are present in this cloud execution environment; nothing has been installed on the user's computer. There is no valid AIsa credential yet. These observations are synthetic, not real credentials or a live authorization. You already supplied the user the exact login link https://clerk.aisa.one/oauth/authorize?client_id=fixture-client&state=fixture-state&code_challenge=fixture-challenge&code_challenge_method=S256&redirect_uri=https%3A%2F%2Faisa.one%2Fcli%2Fauth&response_type=code. They now only said \u597d\u4e86. PTY session login-fixture-42 is still alive; latest output remains Paste it here: and no text has been submitted to its stdin. The client can send subsequent user input to that same PTY.",
"live_session": "login-fixture-42",
"can_start": false,
"current_url": "https://clerk.aisa.one/oauth/authorize?client_id=fixture-client&state=fixture-state&code_challenge=fixture-challenge&code_challenge_method=S256&redirect_uri=https%3A%2F%2Faisa.one%2Fcli%2Fauth&response_type=code"
},
{
"id": "no-pty",
"request": "Set up AIsa for me.",
"observations": "You are responding in ChatGPT Work Cloud. The official aisa Skill and CLI0.5.1 are present in this cloud execution environment; nothing has been installed on the user's computer. There is no valid AIsa credential yet. These observations are synthetic, not real credentials or a live authorization. The shell does not support a persistent interactive PTY or stdin across turns. The aisa login --no-browser command has exited1 with: --no-browser needs an interactive terminal to paste the redirect URL into. There is no active login process. The client can use native remote MCP OAuth through a user-configured connection, and the account has access to that UI.",
"live_session": null,
"can_start": false,
"current_url": "https://clerk.aisa.one/oauth/authorize?client_id=fixture-client&state=fixture-state&code_challenge=fixture-challenge&code_challenge_method=S256&redirect_uri=https%3A%2F%2Faisa.one%2Fcli%2Fauth&response_type=code"
},
{
"id": "expired",
"request": "Here is the one-time result from the link you gave me: code=fixture-expired-code&state=fixture-old-state",
"observations": "You are responding in ChatGPT Work Cloud. The official aisa Skill and CLI0.5.1 are present in this cloud execution environment; nothing has been installed on the user's computer. There is no valid AIsa credential yet. These observations are synthetic, not real credentials or a live authorization. The original login-fixture-42 process has terminated and cannot be resumed; its URL was https://clerk.aisa.one/oauth/authorize?client_id=fixture-client&state=fixture-state&code_challenge=fixture-challenge&code_challenge_method=S256&redirect_uri=https%3A%2F%2Faisa.one%2Fcli%2Fauth&response_type=code. No credential was stored. The environment can start and retain a new interactive PTY, but no new login command has been started and no new URL has been emitted.",
"live_session": null,
"can_start": true,
"current_url": "https://clerk.aisa.one/oauth/authorize?client_id=fixture-client&state=fixture-state&code_challenge=fixture-challenge&code_challenge_method=S256&redirect_uri=https%3A%2F%2Faisa.one%2Fcli%2Fauth&response_type=code"
},
{
"id": "return-code",
"request": "Here is the one-time result from this login: https://aisa.one/cli/auth?code=fixture-current-code&state=fixture-state",
"observations": "ChatGPT Work cloud; official aisa Skill and CLI are installed in this execution environment. There is no accessible local browser. Interactive PTY login-fixture-42 is still alive at Paste it here:. The user supplied its current one-time return URL above. The client can relay this text to the same PTY. No credential has yet been stored. These are synthetic observations, never real credentials.",
"live_session": "login-fixture-42",
"can_start": false,
"current_url": "https://clerk.aisa.one/oauth/authorize?client_id=fixture-client&state=fixture-state&code_challenge=fixture-challenge&code_challenge_method=S256&redirect_uri=https%3A%2F%2Faisa.one%2Fcli%2Fauth&response_type=code",
"expected_input": "https://aisa.one/cli/auth?code=fixture-current-code&state=fixture-state"
}
]
Loading
Loading