feat(memory,skills): approve/deny gate for memory + skill writes (#38199)

Adds memory.write_mode and skills.write_mode (on|off|approve), applied to
both foreground turns and the background self-improvement review fork — the
source of the unprompted 'wrong assumption' saves users reported.

- on (default): write freely, unchanged behaviour
- off: never write; the tool returns a clean disabled result
- approve: don't commit. Memory foreground writes prompt inline (small,
  reviewable in a chat bubble); background memory writes and ALL skill writes
  stage to a pending store instead (a SKILL.md is too large to review inline,
  and a daemon thread can't block on a prompt)

Review staged writes from CLI or any messaging platform:
  /memory pending|approve|reject|mode
  /skills pending|approve|reject|diff|mode

Skill review respects the size asymmetry: inline you see a one-line gist;
the full unified diff stays out-of-band (/skills diff, dashboard, or the
staged JSON file).

New: tools/write_approval.py (gate + pending store), hermes_cli/
write_approval_commands.py (shared CLI+gateway handlers). Gates wired at the
single entry points memory_tool() and skill_manage(), using the existing
write-origin ContextVar to distinguish foreground from background_review.
This commit is contained in:
Teknium
2026-06-09 21:51:43 -07:00
committed by GitHub
parent fdc90346ea
commit 96af61b6ef
13 changed files with 1270 additions and 11 deletions
@@ -209,8 +209,62 @@ memory:
user_profile_enabled: true
memory_char_limit: 2200 # ~800 tokens
user_char_limit: 1375 # ~500 tokens
write_mode: on # on | off | approve
```
## Controlling memory writes (`write_mode`)
By default the agent saves memory freely — including from the background
self-improvement review that runs after a turn. If you'd rather not have
memory written behind your back, `memory.write_mode` gives you three options
(applied to **both** foreground turns and the background review):
| Mode | Behaviour |
|------|-----------|
| `on` | Write freely (default — current behaviour). |
| `off` | Never write. The memory tool returns a clean "disabled" result; nothing is saved. |
| `approve` | Don't commit writes — review them first. Foreground writes prompt you inline (entries are small enough to read in a chat bubble). Background-review writes are **staged** instead of committed (a background thread can't block on a prompt). |
Review staged writes from the CLI or any messaging platform:
```
/memory pending # list staged memory writes (auto ones tagged [auto])
/memory approve <id> # apply one (or 'all')
/memory reject <id> # drop one (or 'all')
/memory mode approve # change write_mode and persist it
```
This is the answer to "the agent saved a wrong assumption about me": set
`write_mode: approve`, and every save — especially the unprompted background
ones — waits for your yes/no before it ever enters your profile.
## Controlling skill writes (`skills.write_mode`)
Skills use the same three-state gate, but the review UX differs because a
`SKILL.md` is far too large to read in a chat bubble:
```yaml
skills:
write_mode: on # on | off | approve
```
In `approve` mode, skill writes (create / edit / patch / write_file / delete)
always **stage** regardless of origin. You review the one-line gist inline, but
the full diff stays out-of-band:
```
/skills pending # list staged skill writes + a one-line gist each
/skills diff <id> # full unified diff (best viewed in CLI or dashboard)
/skills approve <id> # apply it (or 'all')
/skills reject <id> # drop it (or 'all')
/skills mode approve # change write_mode and persist it
```
On a messaging platform, approve a skill from its gist + metadata, or open
`/skills diff` on the CLI / dashboard / the staged file under
`~/.hermes/pending/skills/<id>.json` when you want to read the whole change.
## External Memory Providers
For deeper, persistent memory that goes beyond MEMORY.md and USER.md, Hermes ships with 8 external memory provider plugins — including Honcho, OpenViking, Mem0, Hindsight, Holographic, RetainDB, ByteRover, and Supermemory.