Hermes Tag
Post the images, add your notes, let the thread run — then @ it. It already has everything that matters.
Use Hermes to bring Claude-Tag-style capability to your Feishu/Lark (and Slack). Drop a few images, add a note, let other people chime in — when you finally @-mention it, it pulls those images (the originals), your note, and the relevant replies — not just your last line, and never your whole history. Silent until mentioned; then it retrieves exactly the few messages that matter.
What it is
Hermes Tag is a Claude-Tag-style context-selection layer for Hermes on Feishu/Lark (and Slack). It overrides Hermes's built-in Feishu platform — it is not a new platform. Each enabled chat gets one shared agent identity, and it answers only when @-mentioned.
What it uniquely adds on top of Hermes's built-in channels:
- Late @, full context. You can scatter context before you ever invoke it — images, a note, other people's replies — and when you finally @-mention it, it reconstructs the right bounded evidence across time and media: the images' originals, your note, and the relevant discussion. Not just your trigger message; not the whole transcript.
- Per-chat memory, by design. Long-term memory is built only from @-mention interactions and stays scoped to that chat — one chat's memory never leaks into another, and never becomes your whole account history. There is no cross-channel "workspace memory"; that isolation is the privacy promise.
- Auditable, with redacted audit events.
/tag admin auditreturns redacted events (scope, time, counts — never message text);/tag admin clear|disableremoves a chat's retained data. Theenabled_chatsallowlist is the only storage and processing boundary.
The ContextSelector chooses bounded evidence with focused_reply, thread, deictic_recent, and plain scopes instead of dumping the transcript. That means no full-history RAG and no ambient auto-answering.
Shipped now vs. roadmap. Shipped: bounded multimodal evidence, Tier-0/Tier-1 memory, per-chat isolation, admin lifecycle, and redacted audit — on Feishu/Lark and Slack. DingTalk is also supported, but with reduced capability: DingTalk bots only receive messages that @-mention them in groups (no equivalent of Feishu's im:message.group_msg), so ambient group context (Tier-0) isn't available there — see docs/dingtalk.md. Roadmap: deeper connector/source-binding parity. The Claude-Tag comparison is the goal we measure against, not a claim that every Claude-Tag feature already ships.
This repository, the Python/pip package, and the manifest name are all hermes-tag.
Security & Risk Warnings (Read Before Use)
-
The
enabled_chatsallowlist is the storage and processing boundary. -
Tier-0 stores message text. Each message the bot receives in an enabled chat is kept as Tier-0 short-term context, including its text. On Feishu this happens only when
granted_scopesincludesim:message.group_msg; on Slack and DingTalk it always happens. Defaults: 500 rows per chat (tier0_max_count). Rows older than 24 hours (tier0_ttl_seconds) are deleted the next time a message is stored for that chat, so a quiet chat can keep them longer. -
Tier-1 stores the question and the reply. Each @-mention turn writes a Tier-1 memory of the form
question=<message text>; conclusion=<reply text>. Every turn in a DM counts as a mention. Once a chat passestier1_max_count(100 by default), older memories are merged. Tier-1 has no time limit. -
Both are kept in a plaintext SQLite file (declared
encryption_posture: plaintext-db-on-local-disk), at<hermes home>/plugin-data/hermes-tag/<platform>-tag.sqlite3or yourdb_path. Attachments on stored messages are copied into the sibling<sqlite file>.mediadirectory. Media from quoted messages is downloaded through the bundled adapter into Hermes's media cache, one new file per download. The plugin deletes all of these files: after the turn, or on clear or eviction if they are kept as Tier-0 context. -
On Feishu it makes these Feishu Open API calls of its own, with the bot's credentials:
application.v6.scope.list, once per process, to check whether the app really holdsim:message.group_msgasgranted_scopesclaims;im.v1.message.getplus message-resource downloads, for quoted messages;- a DM to each configured admin when that check finds a mismatch or an available upgrade.
On Slack and DingTalk it makes no API calls of its own.
-
/tag standing confirm(admin-only) creates a Hermes cron job. The job runs on its schedule and posts to the chat until it is cancelled or paused. -
/tag admin clearand/tag admin disabledelete a chat's Tier-0, Tier-1 and media, and cancel the cron jobs behind its standing jobs. They do not reset the Hermes conversation; send/newfor that. -
Audit events (
/tag admin audit) record startup, storage, admin, standing-job and lifecycle actions: ids, counts and fixed strings, never message text. They are kept after a clear. Rows written by earlier versions that held a context preview are scrubbed when the store opens.
Read SECURITY.md and docs/design/ before enabling a pilot chat.
Requirements / Compatibility
| Item | Requirement |
| --- | --- |
| hermes-agent | >=0.22.5 (tested: v0.22.5) |
| lark-oapi | 1.6.8 |
| Python | 3.14 |
| Required env | FEISHU_APP_ID + FEISHU_APP_SECRET |
Hermes enforces the manifest requirement requires_hermes: ">=0.22.5".
Quickstart (<60s)
Installing with an AI agent? Point it at llms.txt and AGENTS.md — those are written for agent-driven install/config. This README is for humans.
hermes plugins install DanielLi202/hermes-tag
plugins:
enabled:
- hermes-tag
platforms:
feishu:
require_mention: false # so unmentioned group messages reach the adapter for Tier-0 context
extra:
feishu_tag:
enabled: true
enabled_chats: [oc_xxx_pilot_chat]
bot_open_id: ou_xxx_bot_open_id
granted_scopes: [im:message.group_msg]
admins: [ou_xxx_admin_open_id]
encryption_posture: plaintext-db-on-local-disk
bot_open_id and admins must be Feishu open_id values (ou_...). This plugin does not need contact:user.employee_id:readonly; it normalizes sender identity to open_id even when that scope is granted.
On first use, Hermes Tag live-verifies granted_scopes against the Feishu app and DMs configured admins if the config claims im:message.group_msg but the app does not actually hold it. That catches the silent failure mode where non-@ group messages are never delivered, so Tier-0 group context stays @-only.
Full onboarding + live verification: see after-install.md. Slack setup: see docs/slack-setup.md. DingTalk setup + capability limits: see docs/dingtalk.md. Hitting something that looks broken? Check docs/known-limits.md — it maps common symptoms to Feishu-platform / hermes-core limits this plugin can't fix.
Usage
In groups, /tag commands require @-mention.
/tag status-> admin-only; reports the adapter platform, capability flags (tier0_full_ingest/l2_context/tier1_at_memory), and current per-chat metrics./tag admin count-> returns retained-state counts such astier0=... tier1=... standing_jobs=..../tag admin clear|disable|audit-> clears plugin data, disables plugin storage for the chat, or returns redacted audit events./tag standing add <schedule> <timezone> <description>then/tag standing confirm/tag standing list|cancel <id>|pause <id>|enable <id>
| Context scope | Meaning |
| --- | --- |
| focused_reply | Explicit reply -> narrow to that parent as evidence. |
| thread | Real thread without explicit reply -> narrow to that thread as evidence. |
| deictic_recent | "上面那张图" / "the image above" -> nearest recent media. |
| plain | Bounded recent text. |
| Memory tier | Behavior |
| --- | --- |
| Tier-0 | Short-term per-chat buffer, TTL/count evicted. |
| Tier-1 | Long-term, @-derived, consolidated, tombstoned on delete. |
Project Structure
src/hermes_tag/core.py— config, sqlite store, TagEngine, PlatformSeam.src/hermes_tag/context.py— ContextSelector: the bounded-evidence selector.src/hermes_tag/base.py— TagAdapterMixin: the platform-agnostic orchestration.src/hermes_tag/platforms/feishu.py— Feishu binding: mention detection, media fetch/download, registration.src/hermes_tag/platforms/slack.py— Slack binding: mention detection, media buffering, registration.src/hermes_tag/platforms/dingtalk.py— DingTalk binding:is_in_at_listmention detection, registration (reduced capability — see docs/dingtalk.md).src/hermes_tag/i18n.py— locale strings.src/hermes_tag/adapter.py— back-compat re-export shim.
The platform-agnostic base plus a narrow seam means each platform is a thin add; the generic policy is written once for all of them.
Contributing · Changelog · License
- Contributing
- Changelog
- License: MIT, DanielLi202.
README from the reviewed commit 75e1931; it updates when the author re-pins.

DanielLi202