mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-15 04:12:25 +00:00
docs(AGENTS.md): add curator/cron/delegation/toolsets, fix plugin tree (#20226)
* docs(AGENTS.md): add curator/cron/delegation/toolsets, fix plugin tree, frontmatter, auto-discovery caveat Closes #19101 and #19107 (@pty819). Verified 16 claims from those two issues against current main. 12 were real gaps; 2 were generated/hallucinated (#10 unverified --now flag is actually real and already cited in AGENTS.md; #11 stale PR refs #5587 and #4950 do not appear in AGENTS.md at all); 2 were low-prio nits (memory provider hierarchy, --now scope enumeration) deferred. Changes: - Project tree: add yuanbao to platforms comment; expand plugins/ subtree with real directory names (kanban, hermes-achievements, observability, image_gen) instead of vague '<others>'. - Test-count blurb: 15k/700 Apr → 17k/900 May (verified: 17,375 test defs, 915 files). - Adding New Tools: clarify that auto-discovery wires up schemas but the tool only reaches an agent if its name is added to a toolset in toolsets.py. _HERMES_CORE_TOOLS is not dead code. - Adding Configuration: enumerate top-level config.yaml sections including auxiliary and curator; note auxiliary is per-task overrides for side-LLM work. - SKILL.md frontmatter: add author, license, related_skills. Note top-level tags/category are mirrored from metadata.hermes.*. - New section 'Toolsets' — enumerates the 30 current TOOLSETS keys (including yuanbao, kanban, moa, spotify, safe, debugging). - New section 'Delegation (delegate_task)' — sync semantics, batch mode, leaf vs orchestrator roles, config knobs, durability caveat. - New section 'Curator (skill lifecycle)' — core files, 11 CLI verbs, telemetry sidecar, invariants (pin/delete split after PR #20220, bundled/hub off-limits), curator.* config section. - New section 'Cron (scheduled jobs)' — 4 schedule formats, 7 CLI verbs, per-job fields, 3-min hard interrupt, catchup/grace windows, tick.lock, cron→session isolation. Skipped (invalid claims): - #19107 item 10: --now is real (hermes_cli/skills_hub.py:624/966/1013/1470) - #19107 item 11: no '#5587' or '#4950' or 'async_delegation' in AGENTS.md * docs(AGENTS.md): add Kanban section Adds a Kanban entry alongside Curator / Cron / Delegation so the major durable background systems are all represented. Covers the CLI verbs, the HERMES_KANBAN_TASK-gated worker toolset, the in-gateway dispatcher, plugin assets, and the board/tenant isolation model. Points at the full 742-line user docs for detail.
This commit is contained in:
parent
7530ce04e0
commit
b7bd177105
1 changed files with 195 additions and 9 deletions
204
AGENTS.md
204
AGENTS.md
|
|
@ -37,12 +37,17 @@ hermes-agent/
|
||||||
│ ├── platforms/ # Adapter per platform (telegram, discord, slack, whatsapp,
|
│ ├── platforms/ # Adapter per platform (telegram, discord, slack, whatsapp,
|
||||||
│ │ # homeassistant, signal, matrix, mattermost, email, sms,
|
│ │ # homeassistant, signal, matrix, mattermost, email, sms,
|
||||||
│ │ # dingtalk, wecom, weixin, feishu, qqbot, bluebubbles,
|
│ │ # dingtalk, wecom, weixin, feishu, qqbot, bluebubbles,
|
||||||
│ │ # webhook, api_server, ...). See ADDING_A_PLATFORM.md.
|
│ │ # yuanbao, webhook, api_server, ...). See ADDING_A_PLATFORM.md.
|
||||||
│ └── builtin_hooks/ # Extension point for always-registered gateway hooks (none shipped)
|
│ └── builtin_hooks/ # Extension point for always-registered gateway hooks (none shipped)
|
||||||
├── plugins/ # Plugin system (see "Plugins" section below)
|
├── plugins/ # Plugin system (see "Plugins" section below)
|
||||||
│ ├── memory/ # Memory-provider plugins (honcho, mem0, supermemory, ...)
|
│ ├── memory/ # Memory-provider plugins (honcho, mem0, supermemory, ...)
|
||||||
│ ├── context_engine/ # Context-engine plugins
|
│ ├── context_engine/ # Context-engine plugins
|
||||||
│ └── <others>/ # Dashboard, image-gen, disk-cleanup, examples, ...
|
│ ├── kanban/ # Multi-agent board dispatcher + worker plugin
|
||||||
|
│ ├── hermes-achievements/ # Gamified achievement tracking
|
||||||
|
│ ├── observability/ # Metrics / traces / logs plugin
|
||||||
|
│ ├── image_gen/ # Image-generation providers
|
||||||
|
│ └── <others>/ # disk-cleanup, example-dashboard, google_meet, platforms,
|
||||||
|
│ # spotify, strike-freedom-cockpit, ...
|
||||||
├── optional-skills/ # Heavier/niche skills shipped but NOT active by default
|
├── optional-skills/ # Heavier/niche skills shipped but NOT active by default
|
||||||
├── skills/ # Built-in skills bundled with the repo
|
├── skills/ # Built-in skills bundled with the repo
|
||||||
├── ui-tui/ # Ink (React) terminal UI — `hermes --tui`
|
├── ui-tui/ # Ink (React) terminal UI — `hermes --tui`
|
||||||
|
|
@ -53,7 +58,7 @@ hermes-agent/
|
||||||
├── environments/ # RL training environments (Atropos)
|
├── environments/ # RL training environments (Atropos)
|
||||||
├── scripts/ # run_tests.sh, release.py, auxiliary scripts
|
├── scripts/ # run_tests.sh, release.py, auxiliary scripts
|
||||||
├── website/ # Docusaurus docs site
|
├── website/ # Docusaurus docs site
|
||||||
└── tests/ # Pytest suite (~15k tests across ~700 files as of Apr 2026)
|
└── tests/ # Pytest suite (~17k tests across ~900 files as of May 2026)
|
||||||
```
|
```
|
||||||
|
|
||||||
**User config:** `~/.hermes/config.yaml` (settings), `~/.hermes/.env` (API keys only).
|
**User config:** `~/.hermes/config.yaml` (settings), `~/.hermes/.env` (API keys only).
|
||||||
|
|
@ -289,9 +294,9 @@ registry.register(
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
**2. Add to `toolsets.py`** — either `_HERMES_CORE_TOOLS` (all platforms) or a new toolset.
|
**2. Add to `toolsets.py`** — either `_HERMES_CORE_TOOLS` (all platforms) or a new toolset. **This step is required:** auto-discovery imports the tool and registers its schema, but the tool is only *exposed to an agent* if its name appears in a toolset. `_HERMES_CORE_TOOLS` is not dead code — it's the default bundle every platform's base toolset inherits from.
|
||||||
|
|
||||||
Auto-discovery: any `tools/*.py` file with a top-level `registry.register()` call is imported automatically — no manual import list to maintain.
|
Auto-discovery: any `tools/*.py` file with a top-level `registry.register()` call is imported automatically — no manual import list to maintain. Wiring into a toolset is still a deliberate, manual step.
|
||||||
|
|
||||||
The registry handles schema collection, dispatch, availability checking, and error wrapping. All handlers MUST return a JSON string.
|
The registry handles schema collection, dispatch, availability checking, and error wrapping. All handlers MUST return a JSON string.
|
||||||
|
|
||||||
|
|
@ -313,6 +318,22 @@ The registry handles schema collection, dispatch, availability checking, and err
|
||||||
section is handled automatically by the deep-merge and does NOT require
|
section is handled automatically by the deep-merge and does NOT require
|
||||||
a version bump.
|
a version bump.
|
||||||
|
|
||||||
|
### Top-level `config.yaml` sections (non-exhaustive):
|
||||||
|
|
||||||
|
`model`, `agent`, `terminal`, `compression`, `display`, `stt`, `tts`,
|
||||||
|
`memory`, `security`, `delegation`, `smart_model_routing`, `checkpoints`,
|
||||||
|
`auxiliary`, `curator`, `skills`, `gateway`, `logging`, `cron`, `profiles`,
|
||||||
|
`plugins`, `honcho`.
|
||||||
|
|
||||||
|
`auxiliary` holds per-task overrides for side-LLM work (curator, vision,
|
||||||
|
embedding, title generation, session_search, etc.) — each task can pin
|
||||||
|
its own provider/model/base_url/max_tokens/reasoning_effort. See
|
||||||
|
`agent/auxiliary_client.py::_resolve_auto` for resolution order.
|
||||||
|
|
||||||
|
`curator` holds the background skill-maintenance config —
|
||||||
|
`enabled`, `interval_hours`, `min_idle_hours`, `stale_after_days`,
|
||||||
|
`archive_after_days`, `backup` (nested).
|
||||||
|
|
||||||
### .env variables (SECRETS ONLY — API keys, tokens, passwords):
|
### .env variables (SECRETS ONLY — API keys, tokens, passwords):
|
||||||
1. Add to `OPTIONAL_ENV_VARS` in `hermes_cli/config.py` with metadata:
|
1. Add to `OPTIONAL_ENV_VARS` in `hermes_cli/config.py` with metadata:
|
||||||
```python
|
```python
|
||||||
|
|
@ -519,11 +540,176 @@ niche skills belong in `optional-skills/`.
|
||||||
|
|
||||||
### SKILL.md frontmatter
|
### SKILL.md frontmatter
|
||||||
|
|
||||||
Standard fields: `name`, `description`, `version`, `platforms`
|
Standard fields: `name`, `description`, `version`, `author`, `license`,
|
||||||
(OS-gating list: `[macos]`, `[linux, macos]`, ...),
|
`platforms` (OS-gating list: `[macos]`, `[linux, macos]`, ...),
|
||||||
`metadata.hermes.tags`, `metadata.hermes.category`,
|
`metadata.hermes.tags`, `metadata.hermes.category`,
|
||||||
`metadata.hermes.config` (config.yaml settings the skill needs — stored
|
`metadata.hermes.related_skills`, `metadata.hermes.config` (config.yaml
|
||||||
under `skills.config.<key>`, prompted during setup, injected at load time).
|
settings the skill needs — stored under `skills.config.<key>`, prompted
|
||||||
|
during setup, injected at load time).
|
||||||
|
|
||||||
|
Top-level `tags:` and `category:` are also accepted and mirrored from
|
||||||
|
`metadata.hermes.*` by the loader.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Toolsets
|
||||||
|
|
||||||
|
All toolsets are defined in `toolsets.py` as a single `TOOLSETS` dict.
|
||||||
|
Each platform's adapter picks a base toolset (e.g. Telegram uses
|
||||||
|
`"messaging"`); `_HERMES_CORE_TOOLS` is the default bundle most
|
||||||
|
platforms inherit from.
|
||||||
|
|
||||||
|
Current toolset keys: `browser`, `clarify`, `code_execution`, `cronjob`,
|
||||||
|
`debugging`, `delegation`, `discord`, `discord_admin`, `feishu_doc`,
|
||||||
|
`feishu_drive`, `file`, `homeassistant`, `image_gen`, `kanban`, `memory`,
|
||||||
|
`messaging`, `moa`, `rl`, `safe`, `search`, `session_search`, `skills`,
|
||||||
|
`spotify`, `terminal`, `todo`, `tts`, `video`, `vision`, `web`, `yuanbao`.
|
||||||
|
|
||||||
|
Enable/disable per platform via `hermes tools` (the curses UI) or the
|
||||||
|
`tools.<platform>.enabled` / `tools.<platform>.disabled` lists in
|
||||||
|
`config.yaml`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Delegation (`delegate_task`)
|
||||||
|
|
||||||
|
`tools/delegate_tool.py` spawns a subagent with an isolated
|
||||||
|
context + terminal session. Synchronous: the parent waits for the
|
||||||
|
child's summary before continuing its own loop — if the parent is
|
||||||
|
interrupted, the child is cancelled.
|
||||||
|
|
||||||
|
Two shapes:
|
||||||
|
|
||||||
|
- **Single:** pass `goal` (+ optional `context`, `toolsets`).
|
||||||
|
- **Batch (parallel):** pass `tasks: [...]` — each gets its own subagent
|
||||||
|
running concurrently. Concurrency is capped by
|
||||||
|
`delegation.max_concurrent_children` (default 3).
|
||||||
|
|
||||||
|
Roles:
|
||||||
|
|
||||||
|
- `role="leaf"` (default) — focused worker. Cannot call `delegate_task`,
|
||||||
|
`clarify`, `memory`, `send_message`, `execute_code`.
|
||||||
|
- `role="orchestrator"` — retains `delegate_task` so it can spawn its
|
||||||
|
own workers. Gated by `delegation.orchestrator_enabled` (default true)
|
||||||
|
and bounded by `delegation.max_spawn_depth` (default 2).
|
||||||
|
|
||||||
|
Key config knobs (under `delegation:` in `config.yaml`):
|
||||||
|
`max_concurrent_children`, `max_spawn_depth`, `child_timeout_seconds`,
|
||||||
|
`orchestrator_enabled`, `subagent_auto_approve`, `inherit_mcp_toolsets`,
|
||||||
|
`max_iterations`.
|
||||||
|
|
||||||
|
Synchronicity rule: delegate_task is **not** durable. For long-running
|
||||||
|
work that must outlive the current turn, use `cronjob` or
|
||||||
|
`terminal(background=True, notify_on_complete=True)` instead.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Curator (skill lifecycle)
|
||||||
|
|
||||||
|
Background skill-maintenance system that tracks usage on agent-created
|
||||||
|
skills and auto-archives stale ones. Users never lose skills; archives
|
||||||
|
go to `~/.hermes/skills/.archive/` and are restorable.
|
||||||
|
|
||||||
|
- **Core:** `agent/curator.py` (review loop, auto-transitions, LLM review
|
||||||
|
prompt) + `agent/curator_backup.py` (pre-run tar.gz snapshots).
|
||||||
|
- **CLI:** `hermes_cli/curator.py` wires `hermes curator <verb>` where
|
||||||
|
verbs are: `status`, `run`, `pause`, `resume`, `pin`, `unpin`,
|
||||||
|
`archive`, `restore`, `prune`, `backup`, `rollback`.
|
||||||
|
- **Telemetry:** `tools/skill_usage.py` owns the sidecar
|
||||||
|
`~/.hermes/skills/.usage.json` — per-skill `use_count`, `view_count`,
|
||||||
|
`patch_count`, `last_activity_at`, `state` (active / stale /
|
||||||
|
archived), `pinned`.
|
||||||
|
|
||||||
|
Invariants:
|
||||||
|
- Curator only touches skills with `created_by: "agent"` provenance —
|
||||||
|
bundled + hub-installed skills are off-limits.
|
||||||
|
- Never deletes; max destructive action is archive.
|
||||||
|
- Pinned skills are exempt from every auto-transition and from the
|
||||||
|
LLM review pass.
|
||||||
|
- `skill_manage(action="delete")` refuses pinned skills; patch/edit/
|
||||||
|
write_file/remove_file go through so the agent can keep improving
|
||||||
|
pinned skills.
|
||||||
|
|
||||||
|
Config section (`curator:` in `config.yaml`):
|
||||||
|
`enabled`, `interval_hours`, `min_idle_hours`, `stale_after_days`,
|
||||||
|
`archive_after_days`, `backup.*`.
|
||||||
|
|
||||||
|
Full user-facing docs: `website/docs/user-guide/features/curator.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cron (scheduled jobs)
|
||||||
|
|
||||||
|
`cron/jobs.py` (job store) + `cron/scheduler.py` (tick loop). Agents
|
||||||
|
schedule jobs via the `cronjob` tool; users via `hermes cron <verb>`
|
||||||
|
(`list`, `add`, `edit`, `pause`, `resume`, `run`, `remove`) or the
|
||||||
|
`/cron` slash command.
|
||||||
|
|
||||||
|
Supported schedule formats:
|
||||||
|
- Duration: `"30m"`, `"2h"`, `"1d"`
|
||||||
|
- "every" phrase: `"every 2h"`, `"every monday 9am"`
|
||||||
|
- 5-field cron expression: `"0 9 * * *"`
|
||||||
|
- ISO timestamp (one-shot): `"2026-06-01T09:00:00Z"`
|
||||||
|
|
||||||
|
Per-job fields include `skills` (load specific skills), `model` /
|
||||||
|
`provider` overrides, `script` (pre-run data-collection script whose
|
||||||
|
stdout is injected into the prompt; `no_agent=True` turns the script
|
||||||
|
into the entire job), `context_from` (chain job A's last output into
|
||||||
|
job B's prompt), `workdir` (run in a specific directory with its
|
||||||
|
`AGENTS.md`/`CLAUDE.md` loaded), and multi-platform delivery.
|
||||||
|
|
||||||
|
Hardening invariants:
|
||||||
|
- **3-minute hard interrupt** on cron sessions — runaway agent loops
|
||||||
|
cannot monopolize the scheduler.
|
||||||
|
- Catchup window: half the job's period, clamped to 120s–2h.
|
||||||
|
- Grace window: 120s for one-shot jobs whose fire time was missed.
|
||||||
|
- File lock at `~/.hermes/cron/.tick.lock` prevents duplicate ticks
|
||||||
|
across processes.
|
||||||
|
- Cron sessions pass `skip_memory=True` by default; memory providers
|
||||||
|
intentionally do not run during cron.
|
||||||
|
|
||||||
|
Cron deliveries are **not** mirrored into the target gateway session —
|
||||||
|
they land in their own cron session with a header/footer frame so the
|
||||||
|
main conversation's message-role alternation stays intact.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Kanban (multi-agent work queue)
|
||||||
|
|
||||||
|
Durable SQLite-backed board that lets multiple profiles / workers
|
||||||
|
collaborate on shared tasks. Users drive it via `hermes kanban <verb>`;
|
||||||
|
workers spawned by the dispatcher drive it via a dedicated `kanban_*`
|
||||||
|
toolset so their schema footprint is zero when they're not inside a
|
||||||
|
kanban task.
|
||||||
|
|
||||||
|
- **CLI:** `hermes_cli/kanban.py` wires `hermes kanban` with verbs
|
||||||
|
`init`, `create`, `list` (alias `ls`), `show`, `assign`, `link`,
|
||||||
|
`unlink`, `comment`, `complete`, `block`, `unblock`, `archive`,
|
||||||
|
`tail`, plus less-commonly-used `watch`, `stats`, `runs`, `log`,
|
||||||
|
`assignees`, `heartbeat`, `notify-*`, `dispatch`, `daemon`, `gc`.
|
||||||
|
- **Worker toolset:** `tools/kanban_tools.py` exposes `kanban_show`,
|
||||||
|
`kanban_complete`, `kanban_block`, `kanban_heartbeat`, `kanban_comment`,
|
||||||
|
`kanban_create`, `kanban_link` — gated by `HERMES_KANBAN_TASK` so
|
||||||
|
the schema only appears for processes actually running as a worker.
|
||||||
|
- **Dispatcher:** long-lived loop that (default every 60s) reclaims
|
||||||
|
stale claims, promotes ready tasks, atomically claims, and spawns
|
||||||
|
assigned profiles. Runs **inside the gateway** by default via
|
||||||
|
`kanban.dispatch_in_gateway: true`.
|
||||||
|
- **Plugin assets:** `plugins/kanban/dashboard/` (web UI) +
|
||||||
|
`plugins/kanban/systemd/` (`hermes-kanban-dispatcher.service` for
|
||||||
|
standalone dispatcher deployment).
|
||||||
|
|
||||||
|
Isolation model:
|
||||||
|
- **Board** is the hard boundary — workers are spawned with
|
||||||
|
`HERMES_KANBAN_BOARD` pinned in their env so they can't see other
|
||||||
|
boards.
|
||||||
|
- **Tenant** is a soft namespace *within* a board — one specialist
|
||||||
|
fleet can serve multiple businesses with workspace-path + memory-key
|
||||||
|
isolation.
|
||||||
|
- After ~5 consecutive spawn failures on the same task the dispatcher
|
||||||
|
auto-blocks it to prevent spin loops.
|
||||||
|
|
||||||
|
Full user-facing docs: `website/docs/user-guide/features/kanban.md`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue