The plugin now monkey-patches HonchoMemoryProvider._do_session_init at plugin load time instead of requiring changes to any file in the Hermes repo. This means: - No modifications to plugins/memory/honcho/__init__.py or any other file - No fork needed — pure plugin approach - Survives Hermes upgrades without merge conflicts - Removed docs/honcho-injector.md (replaced by docs/how-it-works.md) - Updated README with new architecture description Config file at /opt/data/identity-config.json remains the single source of truth for mappings.
3.2 KiB
Architecture: Why monkey-patch instead of patching files?
The identity plugin uses runtime monkey-patching — no files in the Hermes repo are modified. This is the key design decision that eliminates the need for a fork.
The problem
Kanban workers start with NO gateway user identity. The spawn function
runs hermes -p <profile> chat -q "work kanban task <id>". When Honcho
initializes, kwargs["user_id"] is None, so it creates user-default-*
peers.
There is no Hermes plugin hook that fires at agent initialization before Honcho creates the session. The hooks available are:
pre_gateway_dispatch— only fires for gateway sessions (Discord, etc.)on_session_start— fires AFTER Honcho has already initializedpre_tool_call/post_tool_call— fire during tool execution, too late
The solution: runtime monkey-patch
Python allows replacing any method on any class at runtime. The identity
plugin does exactly this in its register() function:
- At plugin load time, it imports
HonchoMemoryProvider - Stores a reference to the original
_do_session_initmethod - Replaces it with a wrapper that checks the identity config first
- If the wrapper finds a peer name, it injects it via
kwargs["user_id"] - Otherwise, it calls the original method unchanged
# In the plugin's register() function:
provider_cls = plugins.memory.honcho.HonchoMemoryProvider
_original_init = provider_cls._do_session_init
@wraps(_original_init)
def wrapper(self, cfg, session_id, **kwargs):
if not kwargs.get("user_id"):
resolved = get_peer_name() # reads config + env vars
if resolved:
kwargs["user_id"] = resolved
return _original_init(self, cfg, session_id, **kwargs)
provider_cls._do_session_init = wrapper
Advantages over file patching
| Aspect | File patching | Monkey-patching |
|---|---|---|
| Repo modifications | Yes (edits plugin file) | Zero |
| Fork needed | Yes (git diff to maintain) | No |
| Upgrades | Merge conflict on every pull | Unchanged |
| Uninstall | Need to revert file changes | Plugin removed → clean |
| Reliability | Works across restarts | Applied at every load |
| Complexity | Simple file edit | Wrapping pattern |
When does the patch apply?
- At plugin discovery time (Hermes startup)
- Before any Honcho session is created
- Before any agent is initialized
- Before any kanban worker is spawned
The Hermes plugin loader calls register() during gateway/agent startup,
which means the patch is in place before any session can begin.
Safety guarantees
-
Non-destructive: The original method is preserved via closure — removing the plugin restores original behavior with zero cleanup.
-
Idempotent:
_apply_honcho_patch()guards against double-patching via the_original_initsentinel. -
Graceful degradation: If the config file is missing, parsing fails, or the kanban DB is unavailable, the wrapper returns None → Honcho falls back to its default behavior (creating
user-default-*peers). -
No data loss: The injected
kwargs["user_id"]only affects what peer name Honcho assigns to the session's messages. It doesn't touch any existing data.