mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-18 04:41:56 +00:00
feat(acp-registry): switch to uvx distribution, drop npm launcher
The ACP Registry schema supports uvx as a first-class distribution method alongside npx and binary. Pointing the registry directly at the existing hermes-agent PyPI release removes: - the @nousresearch npm scope (we don't own it) - a separate npm publish step on every weekly release - 90 lines of Node launcher + tests in packages/hermes-agent-acp/ The Zed registry now installs Hermes via: uvx --from 'hermes-agent[acp]==<version>' hermes-acp This is the same command the npm launcher was shelling out to anyway, so end-user behavior is unchanged. Registry CI validates the PyPI URL + version-pin exact match automatically. Changes: - acp_registry/agent.json: distribution.npx -> distribution.uvx - delete packages/hermes-agent-acp/ entirely - scripts/release.py: drop npm-launcher bump paths, keep manifest lockstep - tests/acp/test_registry_manifest.py: assert uvx shape + version pin - tests/scripts/test_release_acp_registry.py: rewrite for uvx-only shape - docs (user-guide + dev-guide): drop all npm-launcher references - delete docs/plans/acp-registry-zed-integration.md (stale, npm-shaped) Validated against agentclientprotocol/registry agent.schema.json via jsonschema. hermes-agent==0.13.0 is already live on PyPI.
This commit is contained in:
parent
5af672c753
commit
c8c6ce1731
11 changed files with 56 additions and 360 deletions
|
|
@ -8,8 +8,9 @@
|
|||
"authors": ["Nous Research"],
|
||||
"license": "MIT",
|
||||
"distribution": {
|
||||
"npx": {
|
||||
"package": "@nousresearch/hermes-agent-acp@0.13.0"
|
||||
"uvx": {
|
||||
"package": "hermes-agent[acp]==0.13.0",
|
||||
"args": ["hermes-acp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,97 +0,0 @@
|
|||
# Hermes Agent ACP Registry + Zed Integration Implementation Plan
|
||||
|
||||
> For Hermes: Use subagent-driven-development skill to implement this plan task-by-task.
|
||||
|
||||
Goal: Make Hermes Agent installable from Zed's official ACP Registry, so users can add Hermes from Zed's agent panel without manual custom `agent_servers` settings.
|
||||
|
||||
Architecture: Use the official `agentclientprotocol/registry` flow instead of the deprecated Zed Agent Server Extension path. Ship a registry-compatible launcher distribution, advertise valid ACP auth methods during every handshake, validate against official registry schema and auth CI, then submit a registry PR for `hermes-agent`.
|
||||
|
||||
Tech Stack: Hermes Agent Python package, ACP adapter (`hermes acp` / `hermes-acp`), npm launcher package, official ACP Registry JSON schema, Zed external agent UI.
|
||||
|
||||
---
|
||||
|
||||
## Compliance constraints
|
||||
|
||||
- Zed v0.221.x+ prefers the ACP Registry for external agents; do not use Zed Agent Server Extensions for distribution.
|
||||
- Registry repo layout is top-level `hermes-agent/agent.json` and `hermes-agent/icon.svg`, not `agents/hermes-agent/`.
|
||||
- Registry metadata must use the official schema: `id`, `name`, `version`, `description`, `distribution`, optional `repository`, `website`, `authors`, `license`.
|
||||
- Distribution must be exactly one supported type unless intentionally adding another: `binary`, `npx`, or `uvx`.
|
||||
- Hermes must advertise at least one valid `authMethods` entry on a clean first-run handshake. No-provider/no-auth is not compliant.
|
||||
- Terminal Auth must be explicit and deterministic: `id: hermes-setup`, `type: terminal`, `args: ["--setup"]`.
|
||||
- `icon.svg` must be 16x16, square, monochrome, and use only `currentColor` / `none` for fill/stroke; no gradients, hardcoded colors, or `url(#...)` paints.
|
||||
- ACP server mode must reserve stdout for JSON-RPC only. Diagnostics/logs go to stderr. `--version`, `--check`, and `--setup` are not server mode and may print normally.
|
||||
- Published npm package must exist and be runnable before the upstream registry PR references it.
|
||||
|
||||
---
|
||||
|
||||
## Tasks
|
||||
|
||||
1. Verify/implement ACP auth methods.
|
||||
- Always return terminal setup auth from `initialize()`.
|
||||
- Return configured provider auth in addition when provider credentials are resolvable.
|
||||
- Add tests for provider auth, terminal fallback auth, and authenticate behavior before/after provider setup.
|
||||
|
||||
2. Add non-interactive ACP commands.
|
||||
- `hermes acp --version`
|
||||
- `hermes acp --check`
|
||||
- `hermes acp --setup`
|
||||
- Same behavior through `hermes-acp`.
|
||||
|
||||
3. Build npm launcher package.
|
||||
- Package: `@nousresearch/hermes-agent-acp@<version>`.
|
||||
- Command: `uvx --from 'hermes-agent[acp]==<version>' hermes-acp ...args`.
|
||||
- Fallback: `uv tool run --from ...` when only `uv` exists.
|
||||
- Forward all args, including `--setup`, `--version`, and `--check`.
|
||||
- Preserve stdio in server mode.
|
||||
- Print actionable stderr error when `uv`/`uvx` is missing.
|
||||
|
||||
4. Replace local registry metadata.
|
||||
- Convert `acp_registry/agent.json` from old command-style local format to official registry schema.
|
||||
- Replace `acp_registry/icon.svg` with compliant 16x16 currentColor icon.
|
||||
- Add tests rejecting old fields (`schema_version`, `display_name`, `distribution.type`, `distribution.command`) and unknown distribution keys.
|
||||
|
||||
5. Update docs.
|
||||
- Zed docs show official ACP Registry install first: Add Agent / `zed: acp registry` -> search Hermes Agent -> install.
|
||||
- Manual `agent_servers` JSON remains only as local-development fallback.
|
||||
- Docs include `uv` prerequisite and `hermes acp --check` troubleshooting.
|
||||
- Developer internals mention npm launcher and terminal setup auth.
|
||||
|
||||
6. Validate locally.
|
||||
- `python -m pytest tests/acp/test_auth.py tests/acp/test_server.py tests/acp/test_entry.py tests/acp/test_registry_manifest.py -q`
|
||||
- `(cd packages/hermes-agent-acp && npm test)`
|
||||
- `(cd packages/hermes-agent-acp && npm pack --dry-run)`
|
||||
- `hermes acp --version`
|
||||
- `hermes acp --check`
|
||||
|
||||
7. Validate against official registry tooling before PR.
|
||||
- In a clone/fork of `agentclientprotocol/registry`, copy files into top-level `hermes-agent/`.
|
||||
- Run official dry-run build, e.g. `uv run --with jsonschema .github/workflows/build_registry.py --dry-run`.
|
||||
- Run official auth check if available, e.g. `.github/workflows/scripts/run-registry-docker.sh python3 .github/workflows/verify_agents.py --auth-check`.
|
||||
- Fix any schema/auth issues before submitting.
|
||||
|
||||
8. Publish and submit.
|
||||
- Publish `@nousresearch/hermes-agent-acp@<version>`.
|
||||
- Verify published package:
|
||||
- `npx @nousresearch/hermes-agent-acp@<version> --version`
|
||||
- `npx @nousresearch/hermes-agent-acp@<version> --check`
|
||||
- ACP initialize/authMethods smoke test through the published package.
|
||||
- Open PR to `agentclientprotocol/registry` adding `hermes-agent/agent.json` and `hermes-agent/icon.svg`.
|
||||
|
||||
9. End-to-end Zed verification.
|
||||
- Install Hermes Agent through Zed's ACP Registry.
|
||||
- Start a Hermes thread.
|
||||
- Verify workspace cwd, file tools, terminal tools, tool rendering, and approval prompts.
|
||||
|
||||
---
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- Hermes appears in Zed's official ACP Registry UI.
|
||||
- Install starts Hermes without custom Zed settings.
|
||||
- Registry CI passes schema and auth validation.
|
||||
- ACP stdout remains JSON-RPC only; all logs go to stderr.
|
||||
- `authMethods` are present and valid on clean first run.
|
||||
- Terminal Auth can launch Hermes provider/model setup with `--setup`.
|
||||
- Zed workspace cwd is honored by Hermes file and terminal tools.
|
||||
- Docs describe registry install first and manual custom config second.
|
||||
- Package/release automation prevents registry entries from pointing at unpublished versions.
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
# @nousresearch/hermes-agent-acp
|
||||
|
||||
ACP launcher for Hermes Agent.
|
||||
|
||||
This package is intended for clients such as Zed that install agents through the official ACP Registry. It launches the Python Hermes ACP server with:
|
||||
|
||||
```bash
|
||||
uvx --from 'hermes-agent[acp]==0.13.0' hermes-acp
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- Node.js 18+
|
||||
- `uv` or `uvx` on PATH
|
||||
- Hermes provider credentials configured with `hermes model`, or through Hermes' normal `~/.hermes/.env` / `~/.hermes/config.yaml` setup
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
npx @nousresearch/hermes-agent-acp@0.13.0 --version
|
||||
npx @nousresearch/hermes-agent-acp@0.13.0 --check
|
||||
npx @nousresearch/hermes-agent-acp@0.13.0 --setup
|
||||
npx @nousresearch/hermes-agent-acp@0.13.0
|
||||
```
|
||||
|
||||
Normal no-argument mode reserves stdout for ACP JSON-RPC traffic. Diagnostics are emitted on stderr by Hermes.
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
const { spawn, spawnSync } = require('node:child_process');
|
||||
|
||||
const HERMES_AGENT_VERSION = '0.13.0';
|
||||
const HERMES_SPEC = `hermes-agent[acp]==${HERMES_AGENT_VERSION}`;
|
||||
|
||||
function commandExists(command) {
|
||||
const result = spawnSync(command, ['--version'], { stdio: 'ignore' });
|
||||
return !result.error && result.status === 0;
|
||||
}
|
||||
|
||||
function buildCommand(argv, exists = commandExists) {
|
||||
if (exists('uvx')) {
|
||||
return {
|
||||
command: 'uvx',
|
||||
args: ['--from', HERMES_SPEC, 'hermes-acp', ...argv],
|
||||
};
|
||||
}
|
||||
|
||||
if (exists('uv')) {
|
||||
return {
|
||||
command: 'uv',
|
||||
args: ['tool', 'run', '--from', HERMES_SPEC, 'hermes-acp', ...argv],
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const argv = process.argv.slice(2);
|
||||
const command = buildCommand(argv);
|
||||
|
||||
if (!command) {
|
||||
console.error('Hermes Agent ACP requires uv or uvx to launch the Python package.');
|
||||
console.error('Install uv from https://docs.astral.sh/uv/getting-started/installation/');
|
||||
console.error('Then retry this agent from Zed.');
|
||||
process.exit(127);
|
||||
}
|
||||
|
||||
const child = spawn(command.command, command.args, {
|
||||
stdio: 'inherit',
|
||||
env: process.env,
|
||||
});
|
||||
|
||||
child.on('error', (error) => {
|
||||
console.error(`Failed to start Hermes Agent ACP: ${error.message}`);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
child.on('exit', (code, signal) => {
|
||||
if (signal) {
|
||||
process.kill(process.pid, signal);
|
||||
return;
|
||||
}
|
||||
process.exit(code ?? 0);
|
||||
});
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
||||
module.exports = { buildCommand, HERMES_AGENT_VERSION, HERMES_SPEC };
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"name": "@nousresearch/hermes-agent-acp",
|
||||
"version": "0.13.0",
|
||||
"description": "ACP launcher for Hermes Agent",
|
||||
"bin": {
|
||||
"hermes-agent-acp": "bin/hermes-agent-acp.js"
|
||||
},
|
||||
"files": [
|
||||
"bin/",
|
||||
"README.md"
|
||||
],
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/NousResearch/hermes-agent.git",
|
||||
"directory": "packages/hermes-agent-acp"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node --test"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { buildCommand, HERMES_SPEC } = require('../bin/hermes-agent-acp.js');
|
||||
|
||||
test('uses uvx when available and forwards args', () => {
|
||||
const command = buildCommand(['--version'], (name) => name === 'uvx');
|
||||
|
||||
assert.equal(command.command, 'uvx');
|
||||
assert.deepEqual(command.args, ['--from', HERMES_SPEC, 'hermes-acp', '--version']);
|
||||
});
|
||||
|
||||
test('falls back to uv tool run and forwards setup args', () => {
|
||||
const command = buildCommand(['--setup'], (name) => name === 'uv');
|
||||
|
||||
assert.equal(command.command, 'uv');
|
||||
assert.deepEqual(command.args, ['tool', 'run', '--from', HERMES_SPEC, 'hermes-acp', '--setup']);
|
||||
});
|
||||
|
||||
test('returns null when neither uvx nor uv is available', () => {
|
||||
assert.equal(buildCommand([], () => false), null);
|
||||
});
|
||||
|
|
@ -34,12 +34,10 @@ REPO_ROOT = Path(__file__).resolve().parent.parent
|
|||
VERSION_FILE = REPO_ROOT / "hermes_cli" / "__init__.py"
|
||||
PYPROJECT_FILE = REPO_ROOT / "pyproject.toml"
|
||||
|
||||
# ACP Registry assets that must stay version-locked with pyproject.toml.
|
||||
# tests/acp/test_registry_manifest.py enforces this lockstep, so the release
|
||||
# bump touches all four files atomically.
|
||||
# ACP Registry manifest must stay version-locked with pyproject.toml.
|
||||
# tests/acp/test_registry_manifest.py enforces this lockstep so the release
|
||||
# bump touches both files atomically.
|
||||
ACP_REGISTRY_MANIFEST = REPO_ROOT / "acp_registry" / "agent.json"
|
||||
ACP_NPM_PACKAGE_JSON = REPO_ROOT / "packages" / "hermes-agent-acp" / "package.json"
|
||||
ACP_NPM_LAUNCHER = REPO_ROOT / "packages" / "hermes-agent-acp" / "bin" / "hermes-agent-acp.js"
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
# Git email → GitHub username mapping
|
||||
|
|
@ -1168,38 +1166,23 @@ def update_version_files(semver: str, calver_date: str):
|
|||
|
||||
|
||||
def _update_acp_registry_versions(semver: str) -> None:
|
||||
"""Bump the ACP Registry manifest, npm package, and launcher in lockstep.
|
||||
"""Bump the ACP Registry manifest's version + uvx package pin in lockstep
|
||||
with pyproject.
|
||||
|
||||
Skips silently if any of the files are missing — the ACP Registry assets
|
||||
landed mid-cycle and older release branches may not have them.
|
||||
Skips silently if the manifest is missing — older release branches predate
|
||||
the ACP Registry assets.
|
||||
"""
|
||||
if ACP_REGISTRY_MANIFEST.exists():
|
||||
manifest = json.loads(ACP_REGISTRY_MANIFEST.read_text(encoding="utf-8"))
|
||||
manifest["version"] = semver
|
||||
npx = manifest.get("distribution", {}).get("npx", {})
|
||||
if "package" in npx:
|
||||
npx["package"] = f"@nousresearch/hermes-agent-acp@{semver}"
|
||||
uvx = manifest.get("distribution", {}).get("uvx", {})
|
||||
if "package" in uvx:
|
||||
uvx["package"] = f"hermes-agent[acp]=={semver}"
|
||||
# Preserve trailing newline + 2-space indent the file already uses.
|
||||
ACP_REGISTRY_MANIFEST.write_text(
|
||||
json.dumps(manifest, indent=2) + "\n", encoding="utf-8"
|
||||
)
|
||||
|
||||
if ACP_NPM_PACKAGE_JSON.exists():
|
||||
package = json.loads(ACP_NPM_PACKAGE_JSON.read_text(encoding="utf-8"))
|
||||
package["version"] = semver
|
||||
ACP_NPM_PACKAGE_JSON.write_text(
|
||||
json.dumps(package, indent=2) + "\n", encoding="utf-8"
|
||||
)
|
||||
|
||||
if ACP_NPM_LAUNCHER.exists():
|
||||
launcher = ACP_NPM_LAUNCHER.read_text(encoding="utf-8")
|
||||
launcher = re.sub(
|
||||
r"const HERMES_AGENT_VERSION\s*=\s*'[^']+';",
|
||||
f"const HERMES_AGENT_VERSION = '{semver}';",
|
||||
launcher,
|
||||
)
|
||||
ACP_NPM_LAUNCHER.write_text(launcher, encoding="utf-8")
|
||||
|
||||
|
||||
def build_release_artifacts(semver: str) -> list[Path]:
|
||||
"""Build sdist/wheel artifacts for the current release.
|
||||
|
|
|
|||
|
|
@ -39,36 +39,30 @@ def test_agent_json_matches_official_registry_required_fields():
|
|||
assert set(data["distribution"]) <= ALLOWED_DISTRIBUTIONS
|
||||
|
||||
|
||||
def test_agent_json_uses_npx_distribution_without_local_command_fields():
|
||||
def test_agent_json_uses_uvx_distribution_without_local_command_fields():
|
||||
data = _manifest()
|
||||
|
||||
assert set(data["distribution"]) == {"npx"}
|
||||
assert set(data["distribution"]["npx"]) == {"package"}
|
||||
assert data["distribution"]["npx"]["package"] == (
|
||||
f"@nousresearch/hermes-agent-acp@{data['version']}"
|
||||
)
|
||||
assert set(data["distribution"]) == {"uvx"}
|
||||
uvx = data["distribution"]["uvx"]
|
||||
# Schema allows {package, args, env}; we use {package, args}.
|
||||
assert set(uvx) <= {"package", "args", "env"}
|
||||
assert "package" in uvx
|
||||
assert uvx["package"] == f"hermes-agent[acp]=={data['version']}"
|
||||
assert uvx["args"] == ["hermes-acp"]
|
||||
# Old command-shape fields must not leak back in.
|
||||
assert "type" not in data["distribution"]
|
||||
assert "command" not in data["distribution"]
|
||||
assert "args" not in data["distribution"]
|
||||
|
||||
|
||||
def test_agent_json_version_matches_pyproject():
|
||||
assert _manifest()["version"] == _pyproject_version()
|
||||
|
||||
|
||||
def test_npm_launcher_versions_match_pyproject_and_manifest():
|
||||
version = _pyproject_version()
|
||||
package = json.loads(
|
||||
(ROOT / "packages" / "hermes-agent-acp" / "package.json").read_text(encoding="utf-8")
|
||||
)
|
||||
launcher = (ROOT / "packages" / "hermes-agent-acp" / "bin" / "hermes-agent-acp.js").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
|
||||
assert package["version"] == version
|
||||
assert f"const HERMES_AGENT_VERSION = '{version}';" in launcher
|
||||
assert _manifest()["distribution"]["npx"]["package"] == (
|
||||
f"@nousresearch/hermes-agent-acp@{version}"
|
||||
def test_agent_json_pins_uvx_package_to_pyproject_version():
|
||||
"""The registry CI rejects ``@latest`` and floating pins; the manifest must
|
||||
always reference the exact PyPI version listed in pyproject.toml."""
|
||||
assert _manifest()["distribution"]["uvx"]["package"] == (
|
||||
f"hermes-agent[acp]=={_pyproject_version()}"
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
"""Tests for the ACP Registry version-lockstep bump in scripts/release.py.
|
||||
|
||||
The official ACP Registry manifest, the @nousresearch/hermes-agent-acp npm
|
||||
package, and the npm launcher's HERMES_AGENT_VERSION constant must all match
|
||||
``pyproject.toml`` exactly — ``tests/acp/test_registry_manifest.py`` enforces
|
||||
this at lint time. The release script is the single place that bumps them in
|
||||
lockstep with pyproject; if that bump ever silently breaks, weekly releases
|
||||
fail the manifest test until someone hand-edits four files.
|
||||
The official ACP Registry manifest must match ``pyproject.toml`` exactly —
|
||||
``tests/acp/test_registry_manifest.py`` enforces this at lint time, and the
|
||||
upstream registry CI rejects ``@latest`` / floating pins. The release script
|
||||
is the single place that bumps the manifest in lockstep with pyproject; if
|
||||
that bump ever silently breaks, weekly releases fail the manifest test
|
||||
until someone hand-edits the JSON.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -25,26 +25,14 @@ def _load_release_module(monkeypatch, tmp_root: Path):
|
|||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
# Repoint every REPO_ROOT-derived path at our temp tree.
|
||||
monkeypatch.setattr(module, "REPO_ROOT", tmp_root)
|
||||
monkeypatch.setattr(
|
||||
module, "ACP_REGISTRY_MANIFEST", tmp_root / "acp_registry" / "agent.json"
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
module,
|
||||
"ACP_NPM_PACKAGE_JSON",
|
||||
tmp_root / "packages" / "hermes-agent-acp" / "package.json",
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
module,
|
||||
"ACP_NPM_LAUNCHER",
|
||||
tmp_root / "packages" / "hermes-agent-acp" / "bin" / "hermes-agent-acp.js",
|
||||
)
|
||||
return module
|
||||
|
||||
|
||||
def _write_fixture(root: Path, version: str) -> None:
|
||||
"""Write the three ACP-registry files we expect release.py to bump."""
|
||||
def _write_manifest(root: Path, version: str) -> None:
|
||||
manifest_dir = root / "acp_registry"
|
||||
manifest_dir.mkdir(parents=True)
|
||||
(manifest_dir / "agent.json").write_text(
|
||||
|
|
@ -55,7 +43,10 @@ def _write_fixture(root: Path, version: str) -> None:
|
|||
"version": version,
|
||||
"description": "test",
|
||||
"distribution": {
|
||||
"npx": {"package": f"@nousresearch/hermes-agent-acp@{version}"}
|
||||
"uvx": {
|
||||
"package": f"hermes-agent[acp]=={version}",
|
||||
"args": ["hermes-acp"],
|
||||
}
|
||||
},
|
||||
},
|
||||
indent=2,
|
||||
|
|
@ -64,29 +55,9 @@ def _write_fixture(root: Path, version: str) -> None:
|
|||
encoding="utf-8",
|
||||
)
|
||||
|
||||
package_dir = root / "packages" / "hermes-agent-acp"
|
||||
(package_dir / "bin").mkdir(parents=True)
|
||||
(package_dir / "package.json").write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"name": "@nousresearch/hermes-agent-acp",
|
||||
"version": version,
|
||||
"bin": {"hermes-agent-acp": "bin/hermes-agent-acp.js"},
|
||||
},
|
||||
indent=2,
|
||||
)
|
||||
+ "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(package_dir / "bin" / "hermes-agent-acp.js").write_text(
|
||||
f"const HERMES_AGENT_VERSION = '{version}';\n"
|
||||
f"const HERMES_SPEC = `hermes-agent[acp]==${{HERMES_AGENT_VERSION}}`;\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
|
||||
def test_update_acp_registry_versions_bumps_all_three_files(monkeypatch, tmp_path):
|
||||
_write_fixture(tmp_path, "0.13.0")
|
||||
def test_update_acp_registry_versions_bumps_manifest_and_pin(monkeypatch, tmp_path):
|
||||
_write_manifest(tmp_path, "0.13.0")
|
||||
module = _load_release_module(monkeypatch, tmp_path)
|
||||
|
||||
module._update_acp_registry_versions("0.14.0")
|
||||
|
|
@ -95,41 +66,27 @@ def test_update_acp_registry_versions_bumps_all_three_files(monkeypatch, tmp_pat
|
|||
(tmp_path / "acp_registry" / "agent.json").read_text(encoding="utf-8")
|
||||
)
|
||||
assert manifest["version"] == "0.14.0"
|
||||
assert (
|
||||
manifest["distribution"]["npx"]["package"]
|
||||
== "@nousresearch/hermes-agent-acp@0.14.0"
|
||||
)
|
||||
|
||||
package = json.loads(
|
||||
(
|
||||
tmp_path / "packages" / "hermes-agent-acp" / "package.json"
|
||||
).read_text(encoding="utf-8")
|
||||
)
|
||||
assert package["version"] == "0.14.0"
|
||||
|
||||
launcher = (
|
||||
tmp_path / "packages" / "hermes-agent-acp" / "bin" / "hermes-agent-acp.js"
|
||||
).read_text(encoding="utf-8")
|
||||
assert "const HERMES_AGENT_VERSION = '0.14.0';" in launcher
|
||||
assert "0.13.0" not in launcher
|
||||
assert manifest["distribution"]["uvx"]["package"] == "hermes-agent[acp]==0.14.0"
|
||||
# args stay untouched so we don't accidentally rewrite them.
|
||||
assert manifest["distribution"]["uvx"]["args"] == ["hermes-acp"]
|
||||
|
||||
|
||||
def test_update_acp_registry_versions_is_silent_when_files_missing(
|
||||
def test_update_acp_registry_versions_is_silent_when_manifest_missing(
|
||||
monkeypatch, tmp_path
|
||||
):
|
||||
"""Older release branches predate the ACP Registry assets — must no-op."""
|
||||
"""Older release branches predate the ACP Registry asset — must no-op."""
|
||||
module = _load_release_module(monkeypatch, tmp_path)
|
||||
|
||||
# No fixture written; function should not raise.
|
||||
module._update_acp_registry_versions("0.14.0")
|
||||
|
||||
|
||||
def test_update_version_files_bumps_acp_assets_alongside_pyproject(
|
||||
def test_update_version_files_bumps_manifest_alongside_pyproject(
|
||||
monkeypatch, tmp_path
|
||||
):
|
||||
"""End-to-end: update_version_files() is the function release.py actually
|
||||
calls, so it must drive the ACP bump too."""
|
||||
_write_fixture(tmp_path, "0.13.0")
|
||||
calls, so it must drive the manifest bump too."""
|
||||
_write_manifest(tmp_path, "0.13.0")
|
||||
(tmp_path / "pyproject.toml").write_text(
|
||||
'[project]\nname = "hermes-agent"\nversion = "0.13.0"\n', encoding="utf-8"
|
||||
)
|
||||
|
|
@ -153,7 +110,4 @@ def test_update_version_files_bumps_acp_assets_alongside_pyproject(
|
|||
(tmp_path / "acp_registry" / "agent.json").read_text(encoding="utf-8")
|
||||
)
|
||||
assert manifest["version"] == "0.14.0"
|
||||
assert (
|
||||
manifest["distribution"]["npx"]["package"]
|
||||
== "@nousresearch/hermes-agent-acp@0.14.0"
|
||||
)
|
||||
assert manifest["distribution"]["uvx"]["package"] == "hermes-agent[acp]==0.14.0"
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ hermes acp / hermes-acp / python -m acp_adapter
|
|||
-> acp.run_agent(agent, use_unstable_protocol=True)
|
||||
```
|
||||
|
||||
The Zed ACP Registry path launches the same adapter through `npx @nousresearch/hermes-agent-acp@<version>`, which delegates to `uvx --from 'hermes-agent[acp]==<version>' hermes-acp`.
|
||||
The Zed ACP Registry path launches the same adapter through `uvx --from 'hermes-agent[acp]==<version>' hermes-acp`, pointed at the `hermes-agent` PyPI release.
|
||||
|
||||
Stdout is reserved for ACP JSON-RPC transport. Human-readable logs go to stderr.
|
||||
|
||||
|
|
|
|||
|
|
@ -45,13 +45,13 @@ This installs the `agent-client-protocol` dependency and enables:
|
|||
- `hermes-acp`
|
||||
- `python -m acp_adapter`
|
||||
|
||||
For Zed registry installs, Zed launches Hermes through the official ACP Registry entry. That entry uses the npm launcher package `@nousresearch/hermes-agent-acp`, which runs:
|
||||
For Zed registry installs, Zed launches Hermes through the official ACP Registry entry. That entry uses a `uvx` distribution that runs:
|
||||
|
||||
```bash
|
||||
uvx --from 'hermes-agent[acp]==<version>' hermes-acp
|
||||
```
|
||||
|
||||
Make sure `uv` or `uvx` is available on `PATH` before using the registry install path.
|
||||
Make sure `uv` is available on `PATH` before using the registry install path.
|
||||
|
||||
## Launching the ACP server
|
||||
|
||||
|
|
@ -150,13 +150,13 @@ acp_registry/icon.svg
|
|||
|
||||
The upstream registry PR copies those files into the top-level `hermes-agent/` directory in `agentclientprotocol/registry`.
|
||||
|
||||
The registry entry uses an `npx` distribution:
|
||||
The registry entry uses a `uvx` distribution that points directly at the `hermes-agent` PyPI release:
|
||||
|
||||
```text
|
||||
npx @nousresearch/hermes-agent-acp@<version>
|
||||
uvx --from 'hermes-agent[acp]==<version>' hermes-acp
|
||||
```
|
||||
|
||||
The launcher then runs `hermes-acp` from the matching Python package version.
|
||||
The registry CI verifies that the pinned version exists on PyPI, so the manifest's `version` and uvx `package` pin must always match `pyproject.toml`. `scripts/release.py` keeps them in lockstep automatically.
|
||||
|
||||
## Configuration and credentials
|
||||
|
||||
|
|
@ -207,7 +207,7 @@ Check:
|
|||
- For manual/local development, verify the custom `agent_servers` command points to `hermes acp`.
|
||||
- Hermes is installed and on your PATH.
|
||||
- The ACP extra is installed (`pip install -e '.[acp]'`).
|
||||
- `uv` or `uvx` is installed if launching from the official Zed registry entry.
|
||||
- `uv` is installed if launching from the official Zed registry entry.
|
||||
|
||||
### ACP starts but immediately errors
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue