initial: identity resolution plugin
- Plugin manifest (plugin.yaml) with pre_gateway_dispatch + on_session_start hooks - /identity slash command for config management - Honcho injector patch docs (6 lines in plugins/memory/honcho/__init__.py) - Config file at /opt/data/identity-config.json for persistence - Kanban task body convention: context_peer: <name> - Sample config with thierry/catherine mappings Architecture: user-installed plugin (hooks + CLI) + 6-line Honcho bundled plugin change
This commit is contained in:
35
docs/enforcement.md
Normal file
35
docs/enforcement.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Enforcing context_peer in the kanban-worker skill
|
||||
|
||||
Profiles (Claire, Ashley, Finn, Matt) must include `context_peer: <name>` in
|
||||
every `kanban_create` call. This document covers how to enforce it.
|
||||
|
||||
## Option 1: Profile system prompt (recommended, no code change)
|
||||
|
||||
Add to each profile's system prompt or skill config:
|
||||
|
||||
> When creating a kanban task with `kanban_create`, you MUST include
|
||||
> a `context_peer: <name>` metadata block in the task body. The peer
|
||||
> must be the intended user's Honcho peer name (e.g., `thierry`,
|
||||
> `catherine`), not your own profile name.
|
||||
>
|
||||
> Format:
|
||||
> ```metadata
|
||||
> context_peer: thierry
|
||||
> ```
|
||||
|
||||
## Option 2: kanban-create wrapper tool
|
||||
|
||||
Create a custom tool in `/opt/data/hermes-tools/` that wraps `kanban_create`
|
||||
and rejects calls without `context_peer` in the body.
|
||||
|
||||
This approach requires the persistent tools volume (already used for QET,
|
||||
Gitea, Ollama tools) and a tool registration in the Docker entrypoint.
|
||||
|
||||
## Option 3: Slash command validation
|
||||
|
||||
The `/identity` command includes a `validate` subcommand that checks recent
|
||||
kanban tasks for missing `context_peer` fields.
|
||||
|
||||
```bash
|
||||
/identity validate
|
||||
```
|
||||
116
docs/honcho-injector.md
Normal file
116
docs/honcho-injector.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# Honcho Injector Patch
|
||||
|
||||
The identity plugin cannot directly inject `runtime_user_peer_name` into
|
||||
Honcho from a user-installed hook (the hook system only allows message
|
||||
rewrite/skip, not user_id modification). Instead, we add ~6 lines in the
|
||||
bundled Honcho plugin's session initialization.
|
||||
|
||||
## File
|
||||
|
||||
`plugins/memory/honcho/__init__.py` — line ~362
|
||||
|
||||
## The change
|
||||
|
||||
### Before
|
||||
|
||||
```python
|
||||
runtime_user_peer_name=kwargs.get("user_id") or None,
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
```python
|
||||
runtime_user_peer_name=kwargs.get("user_id") or _resolve_identity_peer(),
|
||||
```
|
||||
|
||||
### Add this function in the same file (or import from the identity plugin)
|
||||
|
||||
```python
|
||||
def _resolve_identity_peer() -> str | None:
|
||||
"""Resolve peer name from env vars and identity config.
|
||||
|
||||
Priority: HERMES_HONCHO_PEER_NAME → kanban task body context_peer
|
||||
→ kanban board config → None (falls through to Honcho default).
|
||||
"""
|
||||
import json, os, sqlite3, re
|
||||
from pathlib import Path
|
||||
|
||||
# 1. Explicit env override
|
||||
explicit = os.environ.get("HERMES_HONCHO_PEER_NAME")
|
||||
if explicit:
|
||||
return explicit
|
||||
|
||||
# 2. Kanban worker: read task body for context_peer
|
||||
task_id = os.environ.get("HERMES_KANBAN_TASK")
|
||||
db_path = os.environ.get("HERMES_KANBAN_DB")
|
||||
if task_id and db_path:
|
||||
db = Path(db_path)
|
||||
if db.exists():
|
||||
try:
|
||||
conn = sqlite3.connect(str(db))
|
||||
conn.row_factory = sqlite3.Row
|
||||
try:
|
||||
row = conn.execute(
|
||||
"SELECT body FROM tasks WHERE id = ?", (task_id,)
|
||||
).fetchone()
|
||||
if row and row["body"]:
|
||||
m = re.search(r"context_peer:\s*(\S+)", row["body"])
|
||||
if m:
|
||||
return m.group(1)
|
||||
finally:
|
||||
conn.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 3. Identity config file
|
||||
cfg_path = Path("/opt/data/identity-config.json")
|
||||
if cfg_path.exists():
|
||||
try:
|
||||
cfg = json.loads(cfg_path.read_text())
|
||||
# Check kanban board config
|
||||
board = os.environ.get("HERMES_KANBAN_BOARD")
|
||||
if board and board in cfg.get("boards", {}):
|
||||
return cfg["boards"][board]
|
||||
# Use fallback
|
||||
return cfg.get("fallback_peer")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return None
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
1. When a **gateway session** starts, `kwargs["user_id"]` carries the platform
|
||||
ID (Discord snowflake, Telegram UID). The identity config file maps these
|
||||
to canonical peer names. The injector is bypassed — normal flow.
|
||||
|
||||
2. When a **kanban worker** starts, `kwargs["user_id"]` is None (no gateway).
|
||||
The injector kicks in:
|
||||
|
||||
a. Checks `HERMES_HONCHO_PEER_NAME` env var (set by a future dispatcher
|
||||
enhancement or manually).
|
||||
|
||||
b. Reads the kanban task body from the SQLite database, extracts
|
||||
`context_peer: <name>` from the body.
|
||||
|
||||
c. Falls back to the board-level config from identity-config.json.
|
||||
|
||||
d. If nothing resolves → returns None → Honcho creates `user-default-*`
|
||||
as today (safe fallback).
|
||||
|
||||
## Why not modify the kanban dispatcher?
|
||||
|
||||
The kanban dispatcher (`hermes_cli/kanban_db.py:_default_spawn`) is core
|
||||
Hermes code. We avoid touching it. Instead, the Honcho injector reads the
|
||||
task directly from the kanban DB using env vars that are already set
|
||||
(`HERMES_KANBAN_TASK`, `HERMES_KANBAN_DB`, `HERMES_KANBAN_BOARD`).
|
||||
|
||||
This adds ~10 microseconds to worker startup — negligible.
|
||||
|
||||
## Compatibility
|
||||
|
||||
- If the identity plugin is removed, the injector function returns None and
|
||||
Honcho behaves exactly as before.
|
||||
- If the config file is missing, same safe fallback.
|
||||
- No data loss risk.
|
||||
Reference in New Issue
Block a user