Skip to content

fix(hermes): preload ~/.agentmemory/.env so status reflects service env (closes #250)#253

Merged
rohitg00 merged 1 commit into
mainfrom
fix/250-hermes-status-dotenv-preload
May 9, 2026
Merged

fix(hermes): preload ~/.agentmemory/.env so status reflects service env (closes #250)#253
rohitg00 merged 1 commit into
mainfrom
fix/250-hermes-status-dotenv-preload

Conversation

@rohitg00
Copy link
Copy Markdown
Owner

@rohitg00 rohitg00 commented May 9, 2026

Closes #250.

What

When a user manages agentmemory's runtime config in ~/.agentmemory/.env (the file agentmemory's own README documents) and starts the service via systemd / launchd / docker compose, those values never reach Hermes. Hermes' canonical env source is ~/.hermes/.env (per the Configuration page):

~/.hermes/.env — fallback for env vars; required for secrets (API keys, tokens, passwords)

The "happy path" Hermes expects is hermes plugin configure agentmemory → user enters URL + secret → Hermes writes them to ~/.hermes/.env. But users running agentmemory as an external service skip that step, and hermes memory status then reports the plugin as "not available" + "Missing AGENTMEMORY_URL / AGENTMEMORY_SECRET" — even though the service is healthy and live conversations can use it.

Fix

Preload ~/.agentmemory/.env (the agentmemory-side config) at plugin-import time using os.environ.setdefault. This bridges the two source-of-truths cleanly:

  • Hermes-side priority preserved. ~/.hermes/.env and any explicitly-exported shell vars take precedence — setdefault only fills gaps.
  • agentmemory-side fallback honored. Users who manage config in agentmemory's documented file get correct status without re-typing values into hermes plugin configure agentmemory.
  • Best-effort. Absent / unreadable / malformed .env is silently skipped; the plugin falls back to its existing defaults.
  • Two paths checked. ~/.agentmemory/.env (primary) and $XDG_CONFIG_HOME/agentmemory/.env (XDG fallback).

The parser is intentionally tiny — KEY=VALUE lines, # comments, blank lines, trim matching '/" quotes. No python-dotenv dependency; the rest of integrations/hermes/ is zero-dep too.

Why fix from the plugin side

The cleaner long-term fix would be Hermes' memory status command consulting the provider's is_available() rather than treating env-var presence as a proxy. That's an upstream change. This PR closes the symptom from our side without waiting on it, mirroring the defensive pattern in #252 (**kwargs everywhere because Hermes' runtime contract drifts from its docs).

Test plan

  • Unit: with a fake HOME containing .agentmemory/.env, importing the plugin populates os.environ.
  • Precedence: shell-set AGENTMEMORY_URL is preserved through plugin import; only missing keys are filled from the file.
  • Failure modes: nonexistent file, unreadable file, malformed lines all skip silently.
  • Verified against Hermes' Configuration docs and the documented MemoryProvider interface — fix doesn't conflict with either, and respects the documented Hermes-side env precedence.

Credit to @OptionalCoin for the precise repro.

…nv (closes #250)

When agentmemory runs as a systemd user service (or any process manager
that loads ~/.agentmemory/.env directly), the values never reach the
shell that runs `hermes memory status`. Status then reads os.environ in
the Hermes CLI process, finds AGENTMEMORY_URL / AGENTMEMORY_SECRET
unset, and reports the plugin as "Missing" even though the service is
healthy and live sessions can use it.

Preload the documented config file at plugin-import time using
os.environ.setdefault so anything explicitly set in the shell still
wins. Best-effort: silently skip when the file is absent, unreadable,
or malformed — the plugin falls back to its existing defaults.

Both ~/.agentmemory/.env (primary, per agentmemory's own README) and
$XDG_CONFIG_HOME/agentmemory/.env (fallback) are checked.

Reported by @OptionalCoin.

Tested:
  $ printf 'AGENTMEMORY_URL=http://example\nAGENTMEMORY_SECRET=s\n' \
      > ~/.agentmemory/.env
  $ unset AGENTMEMORY_URL AGENTMEMORY_SECRET
  $ python -c "import sys; sys.path.insert(0, 'integrations'); \
                import hermes; \
                import os; \
                print(os.environ['AGENTMEMORY_URL'])"
  http://example
  $ AGENTMEMORY_URL=http://shell-wins python -c "..."
  http://shell-wins         # explicit shell value still wins
@vercel
Copy link
Copy Markdown

vercel Bot commented May 9, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agentmemory Ready Ready Preview, Comment May 9, 2026 10:28am

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 9, 2026

📝 Walkthrough

Walkthrough

The Hermes integration plugin now preloads AgentMemory runtime configuration from dotenv files at import time. A new helper function discovers files at ~/.agentmemory/.env and $XDG_CONFIG_HOME/agentmemory/.env, parses KEY=VALUE pairs, and populates missing environment variables via os.environ.setdefault(). README documentation explains this behavior and its role in plugin availability detection.

Changes

AgentMemory Dotenv Preloading

Layer / File(s) Summary
Preloader Implementation
integrations/hermes/__init__.py
New _preload_agentmemory_dotenv() function reads standard dotenv locations, parses configuration, and uses os.environ.setdefault() to inject missing AGENTMEMORY_* variables while preserving existing process values; errors are swallowed.
Documentation
integrations/hermes/README.md
Added explanation of import-time .env loading behavior, file discovery paths, setdefault precedence rules, and rationale for plugin availability reporting under process-manager startup.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • rohitg00/agentmemory#107: Introduces the Hermes integration that this PR extends with dotenv preloading and configuration documentation.

Poem

🐰 A dotenv preloader hops in with grace,
Reading config from its default place,
setdefault keeps the shell's command supreme,
While process managers can share their dream—
The plugin's alive, no matter where it came!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: preloading the ~/.agentmemory/.env file in the Hermes plugin to ensure the status command reflects the service's environment configuration. It is concise, directly related to the changeset, and references the closed issue.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/250-hermes-status-dotenv-preload

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@integrations/hermes/__init__.py`:
- Line 84: The current expression value = value.strip().strip('"').strip("'")
removes unmatched or inner quotes; instead trim whitespace first then only
remove a surrounding quote pair when the first and last characters match and are
either " or ', e.g. after value = value.strip() test if len(value) >= 2 and
value[0] == value[-1] and value[0] in ('"', "'") and only then set value =
value[1:-1]; update the code replacing the chained strip call that sets variable
value so it preserves inner quotes and ignores mismatched single-end quotes.

In `@integrations/hermes/README.md`:
- Line 109: Update the README text to clarify that both files are read when
present (both ~/.agentmemory/.env and $XDG_CONFIG_HOME/agentmemory/.env) at
import time and that values are loaded into os.environ via
os.environ.setdefault(), so the first-processed file (currently
~/.agentmemory/.env) takes precedence; also note that shell-exported variables
override these files and mention this affects the output of the hermes memory
status command when agentmemory is started by a process manager.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f7edcafa-1c45-4994-b80e-9a3b78cc609e

📥 Commits

Reviewing files that changed from the base of the PR and between 2c24c90 and 2123895.

📒 Files selected for processing (2)
  • integrations/hermes/README.md
  • integrations/hermes/__init__.py

continue
key, _, value = line.partition("=")
key = key.strip()
value = value.strip().strip('"').strip("'")
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix quote stripping to handle only matching pairs.

The current quote-stripping logic removes quotes from both ends independently, which can corrupt values:

  • FOO="it's" becomes it (removes outer " and inner ')
  • FOO="bar' becomes bar (removes mismatched quotes)
  • FOO=value" becomes value (removes trailing quote)

Only matching quote pairs should be stripped.

🔧 Proposed fix to strip only matching quote pairs
                 key, _, value = line.partition("=")
                 key = key.strip()
-                value = value.strip().strip('"').strip("'")
+                value = value.strip()
+                if len(value) >= 2 and value[0] == value[-1] and value[0] in ('"', "'"):
+                    value = value[1:-1]
                 if key:
                     os.environ.setdefault(key, value)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
value = value.strip().strip('"').strip("'")
key, _, value = line.partition("=")
key = key.strip()
value = value.strip()
if len(value) >= 2 and value[0] == value[-1] and value[0] in ('"', "'"):
value = value[1:-1]
if key:
os.environ.setdefault(key, value)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@integrations/hermes/__init__.py` at line 84, The current expression value =
value.strip().strip('"').strip("'") removes unmatched or inner quotes; instead
trim whitespace first then only remove a surrounding quote pair when the first
and last characters match and are either " or ', e.g. after value =
value.strip() test if len(value) >= 2 and value[0] == value[-1] and value[0] in
('"', "'") and only then set value = value[1:-1]; update the code replacing the
chained strip call that sets variable value so it preserves inner quotes and
ignores mismatched single-end quotes.

| `AGENTMEMORY_URL` | `http://localhost:3111` | agentmemory server URL |
| `AGENTMEMORY_SECRET` | (none) | Auth token for protected instances |

The plugin reads `~/.agentmemory/.env` (or `$XDG_CONFIG_HOME/agentmemory/.env`) at import time and populates any missing values into the process environment via `os.environ.setdefault`. Anything you set in the shell takes precedence; the file is only used to fill gaps. This means `hermes memory status` reports the plugin as available even when the agentmemory service is launched by systemd or another process manager that loads `~/.agentmemory/.env` directly without exporting it to the Hermes CLI shell (#250).
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clarify that both .env files are read when both exist.

The documentation states "or" which suggests only one file is read, but the implementation reads both ~/.agentmemory/.env and $XDG_CONFIG_HOME/agentmemory/.env if both exist. Values from ~/.agentmemory/.env take precedence due to the order in which files are processed and the use of os.environ.setdefault().

📝 Proposed clarification
-The plugin reads `~/.agentmemory/.env` (or `$XDG_CONFIG_HOME/agentmemory/.env`) at import time and populates any missing values into the process environment via `os.environ.setdefault`.
+The plugin reads `~/.agentmemory/.env` and `$XDG_CONFIG_HOME/agentmemory/.env` (if they exist) at import time and populates any missing values into the process environment via `os.environ.setdefault`, with `~/.agentmemory/.env` taking precedence.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
The plugin reads `~/.agentmemory/.env` (or `$XDG_CONFIG_HOME/agentmemory/.env`) at import time and populates any missing values into the process environment via `os.environ.setdefault`. Anything you set in the shell takes precedence; the file is only used to fill gaps. This means `hermes memory status` reports the plugin as available even when the agentmemory service is launched by systemd or another process manager that loads `~/.agentmemory/.env` directly without exporting it to the Hermes CLI shell (#250).
The plugin reads `~/.agentmemory/.env` and `$XDG_CONFIG_HOME/agentmemory/.env` (if they exist) at import time and populates any missing values into the process environment via `os.environ.setdefault`, with `~/.agentmemory/.env` taking precedence. Anything you set in the shell takes precedence; the file is only used to fill gaps. This means `hermes memory status` reports the plugin as available even when the agentmemory service is launched by systemd or another process manager that loads `~/.agentmemory/.env` directly without exporting it to the Hermes CLI shell (`#250`).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@integrations/hermes/README.md` at line 109, Update the README text to clarify
that both files are read when present (both ~/.agentmemory/.env and
$XDG_CONFIG_HOME/agentmemory/.env) at import time and that values are loaded
into os.environ via os.environ.setdefault(), so the first-processed file
(currently ~/.agentmemory/.env) takes precedence; also note that shell-exported
variables override these files and mention this affects the output of the hermes
memory status command when agentmemory is started by a process manager.

@rohitg00 rohitg00 merged commit c2f2618 into main May 9, 2026
5 checks passed
@rohitg00 rohitg00 deleted the fix/250-hermes-status-dotenv-preload branch May 9, 2026 10:41
rohitg00 added a commit that referenced this pull request May 9, 2026
Bug-fix patch focused on search recall correctness and plugin
compatibility. Pins iii-engine to v0.11.2 because v0.11.6 introduces
a new sandbox-everything-via-`iii worker add` model that agentmemory
hasn't been refactored for yet — pin lifts once that refactor lands.
Adds a hard guard against silent vector-index corruption, fixes BM25
indexing for memories saved via memory_save, and lands four Hermes
plugin fixes.

Per AGENTS.md release checklist:
- package.json version 0.9.4 -> 0.9.5
- src/version.ts VERSION constant
- src/types.ts ExportData version union
- src/functions/export-import.ts supportedVersions Set
- test/export-import.test.ts assertion
- plugin/.claude-plugin/plugin.json version
- CHANGELOG.md detailed entries with contributor shoutouts

Headlines (full detail in CHANGELOG):

Fixed:
- BM25 search now indexes memories saved via memory_save (#258, #257)
  Thanks @Nizar-BenHamida for the precise repro.
- Embedding providers no longer silently corrupt the vector index when
  an API returns wrong-dimension vectors (#248, #247, #256)
  Thanks @AmmarSaleh50 for issue + fix + tests.
- Hermes handle_tool_call returns JSON strings, not raw dicts (#255, #254)
  Thanks @KyoMio for the Anthropic-protocol repro.
- Hermes status reflects real service state on systemd installs (#253, #250)
  Thanks @OptionalCoin for tracing it to env-source divergence.
- Hermes hooks accept passthrough kwargs (#252, #249)
  Thanks @OptionalCoin again for the log analysis.
- agentmemory demo now seeds observations correctly (#251, #229)
  Thanks @seishonagon for root-cause analysis.
- LLM compression / summarization timeouts increased (#213)
  Thanks @xuli500177.
- Pi / OpenClaw / Hermes integration plugin fixes (#230)
  Thanks @deepmroot.

Changed:
- iii-engine pinned to v0.11.2 across every install path (#260).
  v0.11.6 introduces a new `iii worker add` sandbox model that
  agentmemory still pre-dates; pin lifts when we refactor agentmemory
  to register as a sandboxed worker. Override with
  AGENTMEMORY_III_VERSION=<version> for users who've migrated manually.
- README documents iii worker add extension surface (#242).
- README iii Console install/launch commands corrected (#243).

Validated: 852/852 tests pass, npm run build clean.
rohitg00 added a commit that referenced this pull request May 9, 2026
Bug-fix patch focused on search recall correctness and plugin
compatibility. Pins iii-engine to v0.11.2 because v0.11.6 introduces
a new sandbox-everything-via-`iii worker add` model that agentmemory
hasn't been refactored for yet — pin lifts once that refactor lands.
Adds a hard guard against silent vector-index corruption, fixes BM25
indexing for memories saved via memory_save, and lands four Hermes
plugin fixes.

Per AGENTS.md release checklist:
- package.json version 0.9.4 -> 0.9.5
- src/version.ts VERSION constant
- src/types.ts ExportData version union
- src/functions/export-import.ts supportedVersions Set
- test/export-import.test.ts assertion
- plugin/.claude-plugin/plugin.json version
- CHANGELOG.md detailed entries with contributor shoutouts

Headlines (full detail in CHANGELOG):

Fixed:
- BM25 search now indexes memories saved via memory_save (#258, #257)
  Thanks @Nizar-BenHamida for the precise repro.
- Embedding providers no longer silently corrupt the vector index when
  an API returns wrong-dimension vectors (#248, #247, #256)
  Thanks @AmmarSaleh50 for issue + fix + tests.
- Hermes handle_tool_call returns JSON strings, not raw dicts (#255, #254)
  Thanks @KyoMio for the Anthropic-protocol repro.
- Hermes status reflects real service state on systemd installs (#253, #250)
  Thanks @OptionalCoin for tracing it to env-source divergence.
- Hermes hooks accept passthrough kwargs (#252, #249)
  Thanks @OptionalCoin again for the log analysis.
- agentmemory demo now seeds observations correctly (#251, #229)
  Thanks @seishonagon for root-cause analysis.
- LLM compression / summarization timeouts increased (#213)
  Thanks @xuli500177.
- Pi / OpenClaw / Hermes integration plugin fixes (#230)
  Thanks @deepmroot.

Changed:
- iii-engine pinned to v0.11.2 across every install path (#260).
  v0.11.6 introduces a new `iii worker add` sandbox model that
  agentmemory still pre-dates; pin lifts when we refactor agentmemory
  to register as a sandboxed worker. Override with
  AGENTMEMORY_III_VERSION=<version> for users who've migrated manually.
- README documents iii worker add extension surface (#242).
- README iii Console install/launch commands corrected (#243).

Validated: 852/852 tests pass, npm run build clean.
rohitg00 added a commit that referenced this pull request May 9, 2026
Bug-fix patch focused on search recall correctness and plugin
compatibility. Pins iii-engine to v0.11.2 because v0.11.6 introduces
a new sandbox-everything-via-`iii worker add` model that agentmemory
hasn't been refactored for yet — pin lifts once that refactor lands.
Adds a hard guard against silent vector-index corruption, fixes BM25
indexing for memories saved via memory_save, and lands four Hermes
plugin fixes.

Per AGENTS.md release checklist:
- package.json version 0.9.4 -> 0.9.5
- src/version.ts VERSION constant
- src/types.ts ExportData version union
- src/functions/export-import.ts supportedVersions Set
- test/export-import.test.ts assertion
- plugin/.claude-plugin/plugin.json version
- CHANGELOG.md detailed entries with contributor shoutouts

Headlines (full detail in CHANGELOG):

Fixed:
- BM25 search now indexes memories saved via memory_save (#258, #257)
  Thanks @Nizar-BenHamida for the precise repro.
- Embedding providers no longer silently corrupt the vector index when
  an API returns wrong-dimension vectors (#248, #247, #256)
  Thanks @AmmarSaleh50 for issue + fix + tests.
- Hermes handle_tool_call returns JSON strings, not raw dicts (#255, #254)
  Thanks @KyoMio for the Anthropic-protocol repro.
- Hermes status reflects real service state on systemd installs (#253, #250)
  Thanks @OptionalCoin for tracing it to env-source divergence.
- Hermes hooks accept passthrough kwargs (#252, #249)
  Thanks @OptionalCoin again for the log analysis.
- agentmemory demo now seeds observations correctly (#251, #229)
  Thanks @seishonagon for root-cause analysis.
- LLM compression / summarization timeouts increased (#213)
  Thanks @xuli500177.
- Pi / OpenClaw / Hermes integration plugin fixes (#230)
  Thanks @deepmroot.

Changed:
- iii-engine pinned to v0.11.2 across every install path (#260).
  v0.11.6 introduces a new `iii worker add` sandbox model that
  agentmemory still pre-dates; pin lifts when we refactor agentmemory
  to register as a sandboxed worker. Override with
  AGENTMEMORY_III_VERSION=<version> for users who've migrated manually.
- README documents iii worker add extension surface (#242).
- README iii Console install/launch commands corrected (#243).

Validated: 852/852 tests pass, npm run build clean.
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.

Hermes memory status shows false negative for agentmemory

1 participant