The Girlies — Multi-Persona Gateway Plugin

A Hermes plugin that turns one Discord bot application into a multi-persona presence. Instead of running separate gateway processes per sister (Tai, Rei, Nei, Mai), a single the-girlies profile routes messages to the right persona and sends replies via Discord webhook REST API — no discord.py, no gateway-per-sister.

Status: Active ✅ (deployed 2026-05-31)

Architecture Overview

Discord Channel
     │
     ▼
the-girlies Gateway ─── Hermes Agent ─── Plugin Tools
  (1 bot token)                              │
     │                                       ├── persona_send     (📤)
     │                                       ├── persona_list     (📋)
     │                                       ├── persona_state    (📊)
     │                                       ├── persona_remember (💾)
     │                                       ├── persona_recall   (📖)
     │                                       └── persona_multi    (🔗)
     │
     ▼
Webhook Pool (REST) ──── Persona Router ──── Per-Persona Memory
  LRU cached              MACC v2-style       JSON files (flat)
  10-webhook limit        circuit breaker     auto-sync to shared

One Hermes profile → persona router → webhooks per persona. No bot client, no gateway-per-sister, no cross-process sync.

How It Differs From Independent Profiles

Dimension Independent Profiles (Tai/Rei/Nei) The Girlies Plugin
Bot tokens 4 separate Discord apps 1 shared bot token
Gateway processes 4 systemd services 1 systemd service
Profile configs 4 separate config.yaml 1 profile (the-girlies)
Mnemosyne memory profile_isolation: true Per-persona flat JSON (not Mnemosyne)
Cross-persona sync None (independent) Auto-sync to shared memory pool
Tool routing Each profile has own tools Plugin routes tools by persona_id

Plugin File Map

~/.hermes/plugins/the-girlies/
├── __init__.pyregister(ctx) entry point
├── plugin.yamlmetadata (name, version, kind: tool)
├── plugin.py6 tool registrations + memory helpers
├── webhook_pool.py          ← REST-based webhook management (LRU, 10-webhook guild limit)
├── persona_definitions.py   ← PersonaDefinition + PersonaState dataclasses + JSON persistence
└── persona_router.py        ← MACC v2-style router: name detection, multi-queue, circuit breaker

Personas

Four personas, each with display_name, description, personality, scenario, and avatar_url:

ID Name Role
tai Tai (default) Builder/Operator, primary voice
rei Rei QA/Auditor, evidence-first review
nei Nei Quartermaster, triage, digests
mai Mai Expressive/creative variant

State is persisted to ~/.hermes/profiles/the-girlies/persona_state/<id>.json.

Toolset (6 tools)

Tool Schema What it does
persona_send {channel_id, persona_id, content} Send message as persona via webhook + auto-remember
persona_list {} List all personas + state summary
persona_state {persona_id} Show mood/affinity/stage for one persona
persona_remember {persona_id, content, important} Save a memory tagged to a persona
persona_recall {persona_id, query?, limit?} Recall memories tagged to a persona
persona_multi {message_id} Check multi-persona queue for a message ID

All tools are registered under the persona toolset and surfaced to Discord via platform_toolsets.discord.

Memory Design (Not Mnemosyne)

The-girlies uses per-persona flat JSON files rather than Mnemosyne (which is used by the independent sister profiles). This was a deliberate design choice:

Storage Layout

~/.hermes/profiles/the-girlies/persona_memories/
├── tai.json     # Tai's private memories (max 200)
├── rei.json     # Rei's private memories (max 200)
├── nei.json     # Nei's private memories (max 200)
├── mai.json     # Mai's private memories (max 200)
└── all.json     # Shared memories (max 500)

Auto-Sync Design

Every persona_send call automatically saves a content preview (100 chars) to both the persona’s private file AND all.json — the shared pool. When recalling, private results are merged with shared results and deduplicated by content prefix (80 chars). This gives each persona continuity without Mnemosyne overhead.

Compare with independent profiles where each sister has her own Mnemosyne bank DB at ~/.hermes/mnemosyne/data/banks/<profile>/ with profile_isolation: true and a separate shared_surface for cross-discovery.

Mnemosyne Config Difference

This is the opposite pattern from the independent sister profiles:

  • Independent profiles (Tai/Rei/Nei): profile_isolation: true — each sister has a private bank DB. Shared surface is a separate read-only bridge for cross-discovery.
  • The Girlies: No Mnemosyne at all — uses flat JSON files with an auto-sync-to-shared pattern (persona_remember with persona_id='all').

The-girlies does not use Mnemosyne because its memory is simpler (per-persona + shared pool, no vector search needed). A future upgrade could integrate Mnemosyne via triple facts or per-persona scratchpad banks.

Profile Configuration

config.yaml (minimal)

model:
  default: deepseek-v4-flash
  provider: opencode-go

gateway:
  platforms:
    discord:
      enabled: true
      home_channel:
        chat_id: "<CHANNEL_ID>"
        name: Home
        platform: discord

agent:
  personalities:
    helpful: "You are a multi-persona Discord bot..."

skills:
  external_dirs:
    - /root/.hermes/skills

plugins:
  enabled:
    - the-girlies
    - model-providers/opencode-zen

platform_toolsets:
  discord:
    - hermes-discord
    - persona

.env

DISCORD_BOT_TOKEN=<token>
DISCORD_ALLOWED_USERS=<user_ids>
DISCORD_HOME_CHANNEL=<channel_id>

Gateway Setup

printf 'y\ny\n' | hermes --profile the-girlies gateway install
# Service lands at wrong path — known bug workaround:
cp /root/.hermes/profiles/<active_profile>/home/.config/systemd/user/hermes-gateway-the-girlies.service \
   /root/.config/systemd/user/hermes-gateway-the-girlies.service
systemctl --user daemon-reload
systemctl --user enable hermes-gateway-the-girlies.service
systemctl --user start hermes-gateway-the-girlies.service

Known Limitations

  1. No Mnemosyne integration — Memory is flat JSON files, not vector-searchable. Good for conversation continuity, not for durable long-term knowledge.
  2. Single gateway = single point of failure — If the-girlies gateway goes down, all personas go down with it (vs independent profiles where each gateway is independent).
  3. 10-webhook guild limit — Discord’s webhook limit per guild caps how many persona-channel pairs can be cached simultaneously. LRU eviction with webhook repurposing mitigates this.
  4. No discord.py features — Can’t react, fetch message history, or use voice. Webhook REST API is send-only.
  5. Shared namespace — Persona tools exist in one toolset; no isolation between persona tool calls.
  6. Gateway install bug — The systemd service file lands in the wrong directory and must be manually copied to ~/.config/systemd/user/.

Relationship to MACC (Multi-Agent Coordination)

In the MACC architecture, the-girlies profile acts as the orchestrator with no MACC_AGENT_NAME set and no MACC pre-spawn filter. This means it always responds to every message in the MACC channel — intentionally. The sister gateways (Tai, Rei, Nei) use MACC pre-spawn filters to only respond to @mentions, while the-girlies can route to any persona or deliver multi-persona responses with voice-switching.