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.
83 lines
3.2 KiB
Markdown
83 lines
3.2 KiB
Markdown
# 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 initialized
|
|
- `pre_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:
|
|
|
|
1. At plugin load time, it imports `HonchoMemoryProvider`
|
|
2. Stores a reference to the original `_do_session_init` method
|
|
3. Replaces it with a wrapper that checks the identity config first
|
|
4. If the wrapper finds a peer name, it injects it via `kwargs["user_id"]`
|
|
5. Otherwise, it calls the original method unchanged
|
|
|
|
```python
|
|
# 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
|
|
|
|
1. **Non-destructive**: The original method is preserved via closure —
|
|
removing the plugin restores original behavior with zero cleanup.
|
|
|
|
2. **Idempotent**: `_apply_honcho_patch()` guards against double-patching
|
|
via the `_original_init` sentinel.
|
|
|
|
3. **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).
|
|
|
|
4. **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.
|