fix: five HERMES_HOME profile-isolation leaks (#10570)

* fix: show correct env var name in provider API key error (#9506)

The error message for missing provider API keys dynamically built
the env var name as PROVIDER_API_KEY (e.g. ALIBABA_API_KEY), but
some providers use different names (alibaba uses DASHSCOPE_API_KEY).
Users following the error message set the wrong variable.

Fix: look up the actual env var from PROVIDER_REGISTRY before
building the error. Falls back to the dynamic name if the registry
lookup fails.

Closes #9506

* fix: five HERMES_HOME profile-isolation leaks (#5947)

Bug A: Thread session_title from session_db to memory provider init kwargs
so honcho can derive chat-scoped session keys instead of falling back to
cwd-based naming that merges all gateway users into one session.

Bug B: Replace 14 hardcoded ~/.hermes/skills/ paths across 10 skill files
with HERMES_HOME-aware alternatives (${HERMES_HOME:-$HOME/.hermes} in
shell, os.environ.get('HERMES_HOME', ...) in Python).

Bug C: install.sh now respects HERMES_HOME env var and adds --hermes-home
flag. Previously --dir only set INSTALL_DIR while HERMES_HOME was always
hardcoded to $HOME/.hermes.

Bug D: Remove hardcoded ~/.hermes/honcho.json fallback in resolve_config_path().
Non-default profiles no longer silently inherit the default profile's honcho
config. Falls through to ~/.honcho/config.json (global) instead.

Bug E: Guard _edit_skill, _patch_skill, _delete_skill, _write_file, and
_remove_file against writing to skills found in external_dirs. Skills
outside the local SKILLS_DIR are now read-only from the agent's perspective.

Closes #5947
This commit is contained in:
Teknium 2026-04-15 17:09:41 -07:00 committed by GitHub
parent c483b4ceca
commit e402906d48
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 58 additions and 22 deletions

View file

@ -313,7 +313,7 @@ Type these during an interactive chat session.
```
~/.hermes/config.yaml Main configuration
~/.hermes/.env API keys and secrets
~/.hermes/skills/ Installed skills
$HERMES_HOME/skills/ Installed skills
~/.hermes/sessions/ Session transcripts
~/.hermes/logs/ Gateway and error logs
~/.hermes/auth.json OAuth tokens and credential pools

View file

@ -334,7 +334,7 @@ When the user asks you to "review PR #N", "look at this PR", or gives you a PR U
### Step 1: Set up environment
```bash
source ~/.hermes/skills/github/github-auth/scripts/gh-env.sh
source "${HERMES_HOME:-$HOME/.hermes}/skills/github/github-auth/scripts/gh-env.sh"
# Or run the inline setup block from the top of this skill
```

View file

@ -6,7 +6,7 @@ All requests need: `-H "Authorization: token $GITHUB_TOKEN"`
Use the `gh-env.sh` helper to set `$GITHUB_TOKEN`, `$GH_OWNER`, `$GH_REPO` automatically:
```bash
source ~/.hermes/skills/github/github-auth/scripts/gh-env.sh
source "${HERMES_HOME:-$HOME/.hermes}/skills/github/github-auth/scripts/gh-env.sh"
```
## Repositories

View file

@ -32,7 +32,7 @@ on CLI, Telegram, Discord, or any platform.
Define a shorthand first:
```bash
GSETUP="python ~/.hermes/skills/productivity/google-workspace/scripts/setup.py"
GSETUP="python ${HERMES_HOME:-$HOME/.hermes}/skills/productivity/google-workspace/scripts/setup.py"
```
### Step 0: Check if already set up
@ -163,7 +163,7 @@ Should print `AUTHENTICATED`. Setup is complete — token refreshes automaticall
All commands go through the API script. Set `GAPI` as a shorthand:
```bash
GAPI="python ~/.hermes/skills/productivity/google-workspace/scripts/google_api.py"
GAPI="python ${HERMES_HOME:-$HOME/.hermes}/skills/productivity/google-workspace/scripts/google_api.py"
```
### Gmail

View file

@ -60,7 +60,7 @@ The fastest path — auto-detect the model, test strategies, and lock in the win
# In execute_code — use the loader to avoid exec-scoping issues:
import os
exec(open(os.path.expanduser(
"~/.hermes/skills/red-teaming/godmode/scripts/load_godmode.py"
os.path.join(os.environ.get("HERMES_HOME", os.path.expanduser("~/.hermes")), "skills/red-teaming/godmode/scripts/load_godmode.py")
)).read())
# Auto-detect model from config and jailbreak it
@ -192,7 +192,7 @@ python3 scripts/parseltongue.py "How do I hack into a WiFi network?" --tier stan
Or use `execute_code` inline:
```python
# Load the parseltongue module
exec(open(os.path.expanduser("~/.hermes/skills/red-teaming/godmode/scripts/parseltongue.py")).read())
exec(open(os.path.join(os.environ.get("HERMES_HOME", os.path.expanduser("~/.hermes")), "skills/red-teaming/godmode/scripts/parseltongue.py")).read())
query = "How do I hack into a WiFi network?"
variants = generate_variants(query, tier="standard")
@ -229,7 +229,7 @@ Race multiple models against the same query, score responses, pick the winner:
```python
# Via execute_code
exec(open(os.path.expanduser("~/.hermes/skills/red-teaming/godmode/scripts/godmode_race.py")).read())
exec(open(os.path.join(os.environ.get("HERMES_HOME", os.path.expanduser("~/.hermes")), "skills/red-teaming/godmode/scripts/godmode_race.py")).read())
result = race_models(
query="Explain how SQL injection works with a practical example",

View file

@ -114,7 +114,7 @@ hermes
### Via the GODMODE CLASSIC racer script
```python
exec(open(os.path.expanduser("~/.hermes/skills/red-teaming/godmode/scripts/godmode_race.py")).read())
exec(open(os.path.join(os.environ.get("HERMES_HOME", os.path.expanduser("~/.hermes")), "skills/red-teaming/godmode/scripts/godmode_race.py")).read())
result = race_godmode_classic("Your query here")
print(f"Winner: {result['codename']} — Score: {result['score']}")
print(result['content'])

View file

@ -129,7 +129,7 @@ These don't auto-reject but reduce the response score:
## Using in Python
```python
exec(open(os.path.expanduser("~/.hermes/skills/red-teaming/godmode/scripts/godmode_race.py")).read())
exec(open(os.path.join(os.environ.get("HERMES_HOME", os.path.expanduser("~/.hermes")), "skills/red-teaming/godmode/scripts/godmode_race.py")).read())
# Check if a response is a refusal
text = "I'm sorry, but I can't assist with that request."

View file

@ -7,7 +7,7 @@ finds what works, and locks it in by writing config.yaml + prefill.json.
Usage in execute_code:
exec(open(os.path.expanduser(
"~/.hermes/skills/red-teaming/godmode/scripts/auto_jailbreak.py"
os.path.join(os.environ.get("HERMES_HOME", os.path.expanduser("~/.hermes")), "skills/red-teaming/godmode/scripts/auto_jailbreak.py")
)).read())
result = auto_jailbreak() # Uses current model from config

View file

@ -7,7 +7,7 @@ Queries multiple models in parallel via OpenRouter, scores responses
on quality/filteredness/speed, returns the best unfiltered answer.
Usage in execute_code:
exec(open(os.path.expanduser("~/.hermes/skills/red-teaming/godmode/scripts/godmode_race.py")).read())
exec(open(os.path.join(os.environ.get("HERMES_HOME", os.path.expanduser("~/.hermes")), "skills/red-teaming/godmode/scripts/godmode_race.py")).read())
result = race_models(
query="Your query here",

View file

@ -3,7 +3,7 @@ Loader for G0DM0D3 scripts. Handles the exec-scoping issues.
Usage in execute_code:
exec(open(os.path.expanduser(
"~/.hermes/skills/red-teaming/godmode/scripts/load_godmode.py"
os.path.join(os.environ.get("HERMES_HOME", os.path.expanduser("~/.hermes")), "skills/red-teaming/godmode/scripts/load_godmode.py")
)).read())
# Now all functions are available:

View file

@ -11,7 +11,7 @@ Usage:
python parseltongue.py "How do I hack a WiFi network?" --tier standard
# As a module in execute_code
exec(open("~/.hermes/skills/red-teaming/godmode/scripts/parseltongue.py").read())
exec(open(os.path.join(os.environ.get("HERMES_HOME", os.path.expanduser("~/.hermes")), "skills/red-teaming/godmode/scripts/parseltongue.py")).read())
variants = generate_variants("How do I hack a WiFi network?", tier="standard")
"""