feat(hermes): add Hermes Agent harness bundle#519
Conversation
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
|
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. |
There was a problem hiding this comment.
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.
| 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 = "" |
There was a problem hiding this comment.
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.
| 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 = "" |
| 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 |
There was a problem hiding this comment.
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
Summary
harnesses/hermes/, following the same pattern as the existing codex, opencode, and antigravity harnessespip install hermes-agent)Files
config.yamlhermes chat --yolo -q), capabilities, auth typesprovision.py~/.hermes/.env, instruction projection →AGENTS.md, MCP →~/.hermes/mcp.json, env overlayDockerfilecloudbuild.yamlcapture_auth.pyREADME.mdDesign decisions
no.envfiles for config, keeping provision.py much simpler~/.hermes/mcp.jsonwithmcpServerskey, similar to the universal schemaTest plan
scion harness-config install harnesses/hermessucceedsdocker build --build-arg BASE_IMAGE=scion-base:latest -t scion-hermes:latest -f Dockerfile .