Skip to content

feat(hermes): add Hermes Agent harness bundle#519

Merged
ptone merged 5 commits into
GoogleCloudPlatform:mainfrom
ptone:scion/hermes-harness
Jun 28, 2026
Merged

feat(hermes): add Hermes Agent harness bundle#519
ptone merged 5 commits into
GoogleCloudPlatform:mainfrom
ptone:scion/hermes-harness

Conversation

@ptone

@ptone ptone commented Jun 28, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds the complete Hermes Agent harness bundle at harnesses/hermes/, following the same pattern as the existing codex, opencode, and antigravity harnesses
  • Hermes Agent is Nous Research's MIT-licensed AI coding agent (pip install hermes-agent)
  • Supports API key auth for Anthropic, OpenAI, and Google AI Studio (no auth-file, oauth, or Vertex AI)

Files

File Purpose
config.yaml Harness config: model aliases, command flags (hermes chat --yolo -q), capabilities, auth types
provision.py Container-side provisioner: API key → ~/.hermes/.env, instruction projection → AGENTS.md, MCP → ~/.hermes/mcp.json, env overlay
Dockerfile Installs Node.js 22, ripgrep, and hermes-agent on scion-base
cloudbuild.yaml Multi-platform Cloud Build for scion-hermes image
capture_auth.py Post-auth credential capture for no-auth flow
README.md Bundle documentation

Design decisions

  • Auth precedence: ANTHROPIC_API_KEY > OPENAI_API_KEY > GOOGLE_API_KEY (Anthropic first since Hermes defaults to Claude)
  • No telemetry: Hermes has Langfuse but no native OTEL, so telemetry support is declared no
  • No TOML editing: Unlike codex, Hermes uses env vars and .env files for config, keeping provision.py much simpler
  • MCP format: Hermes reads ~/.hermes/mcp.json with mcpServers key, similar to the universal schema

Test plan

  • Verify scion harness-config install harnesses/hermes succeeds
  • Verify provision.py handles all auth key combinations correctly
  • Verify MCP server translation produces valid Hermes mcp.json
  • Build Docker image with docker build --build-arg BASE_IMAGE=scion-base:latest -t scion-hermes:latest -f Dockerfile .
  • Run Hermes agent with API key and confirm it starts correctly

Scion Agent (hh-dev) added 4 commits June 27, 2026 16:59
Add the Hermes Agent harness bundle scaffold with:
- config.yaml: harness configuration with API key auth (Anthropic,
  OpenAI, Google AI Studio), model aliases, command flags, and
  capability declarations
- Dockerfile: scion-base overlay installing Node.js 22, ripgrep,
  and hermes-agent via pip
- cloudbuild.yaml: multi-platform Cloud Build config for scion-hermes
Minimal provision.py handling:
- API key auth with ANTHROPIC > OPENAI > GOOGLE precedence,
  written to ~/.hermes/.env
- Instruction projection into AGENTS.md with scion-managed blocks
- MCP server config written to ~/.hermes/mcp.json
- Env overlay with HERMES_YOLO_MODE, HERMES_QUIET, HERMES_ACCEPT_HOOKS,
  and optional HERMES_INFERENCE_MODEL from model alias resolution
- capture_auth.py: credential capture for no-auth flow, reads from
  inputs/capture-auth-config.json and stores via sciontool
- README.md: bundle documentation with install, auth modes, and build
  instructions
- [H1] Add provision_test.py with 16 tests covering auth resolution
  (ANTHROPIC>OPENAI>GOOGLE precedence), instruction projection (compose,
  stale-block cleanup, file removal, malformed-marker safety), and MCP
  entry building (stdio, sse, streamable-http, unknown transport)
- [M1] Add HERMES_HOME=/home/scion/.hermes to env overlay
- [M2] Create .hermes/skills directory in Dockerfile
- [M3] Set 0600 permissions on mcp.json to protect auth headers
- [M4] Clean up pip install fallback with empty default ARG
- [L1] Remove dead HERMES_ENV_FILE constant
@google-cla

google-cla Bot commented Jun 28, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new harness bundle for the Hermes Agent, including its Dockerfile, configuration, a credential capture script, and a container-side provisioner with associated unit tests. The review feedback highlights two key issues in the provisioner script: first, the no_auth behavior is broken because it checks for the emptiness of the candidates dictionary (which always contains metadata) instead of env_keys; second, the script fails to clean up stale mcp.json configurations when no MCP servers are configured.

Comment thread harnesses/hermes/provision.py Outdated
Comment on lines +410 to +413
if not candidates and no_auth_behavior:
print(f"hermes provision: no-auth mode (behavior={no_auth_behavior}), skipping auth setup", file=sys.stderr)
method = "none"
env_key = ""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The no_auth behavior (drop-to-shell) is currently broken because candidates is checked for emptiness (if not candidates). Since auth-candidates.json is always staged by the harness with metadata (such as schema_version), candidates will never be empty, even when no credentials are provided. This causes the provisioner to always attempt to select an API key and fail with an error, preventing the container from starting in no_auth mode.

We should check if env_keys is empty instead, which correctly indicates that no credentials were provided.

Suggested change
if not candidates and no_auth_behavior:
print(f"hermes provision: no-auth mode (behavior={no_auth_behavior}), skipping auth setup", file=sys.stderr)
method = "none"
env_key = ""
if not env_keys and no_auth_behavior:
print(f"hermes provision: no-auth mode (behavior={no_auth_behavior}), skipping auth setup", file=sys.stderr)
method = "none"
env_key = ""

Comment on lines +344 to +361
if not mcp_servers:
return 0

hermes_dir = _expand("~/.hermes")
try:
os.makedirs(hermes_dir, exist_ok=True)
except OSError as exc:
print(f"hermes provision: could not create {hermes_dir}: {exc}", file=sys.stderr)
return 0

config_path = os.path.join(hermes_dir, "mcp.json")
payload = {"mcpServers": mcp_servers}
try:
_write_json(config_path, payload)
os.chmod(config_path, 0o600)
except OSError as exc:
print(f"hermes provision: failed to write mcp.json: {exc}", file=sys.stderr)
return 0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If mcp_servers is empty, the function currently returns early without clearing or removing any existing mcp.json file. This can leave stale MCP server configurations from previous runs, leading to unexpected behavior or errors.

We should ensure that any existing mcp.json is removed when no MCP servers are configured.

    hermes_dir = _expand("~/.hermes")
    config_path = os.path.join(hermes_dir, "mcp.json")

    if not mcp_servers:
        if os.path.isfile(config_path):
            try:
                os.remove(config_path)
            except OSError as exc:
                print(f"hermes provision: failed to remove stale mcp.json: {exc}", file=sys.stderr)
        return 0

    try:
        os.makedirs(hermes_dir, exist_ok=True)
    except OSError as exc:
        print(f"hermes provision: could not create {hermes_dir}: {exc}", file=sys.stderr)
        return 0

    payload = {"mcpServers": mcp_servers}
    try:
        _write_json(config_path, payload)
        os.chmod(config_path, 0o600)
    except OSError as exc:
        print(f"hermes provision: failed to write mcp.json: {exc}", file=sys.stderr)
        return 0

- Fix no-auth check: use 'not env_keys' instead of 'not candidates'
  since auth-candidates.json always contains metadata even with zero
  credentials
- Remove stale mcp.json when no MCP servers are configured, preventing
  leftover config from a previous provision run
- Add test for no-auth mode activating with metadata-only candidates
- Add test for stale mcp.json removal
@ptone ptone merged commit 9324950 into GoogleCloudPlatform:main Jun 28, 2026
7 of 9 checks passed
@ptone ptone deleted the scion/hermes-harness branch June 28, 2026 12:11
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