Two corrections and one finding from the same night. voice-studio: operator ruled the stack out of service. It existed for the dots mint/audition loop and dots was decommissioned 2026-09-06 when Breeze took the fleet seat, so its reason to exist went with it - which is also why nine days of breakage alerted nobody. No v11 rebuild. The gate one-liner was applied minutes before the retraction landed and was left in place rather than reverted, since the value it replaced was a dead address and reverting is another recreate of a stack that is going away. Container not stopped: it was already running, and 'down for now' arrived as a relayed paraphrase rather than an instruction. The two host-level facts survive the stack. Containers on irv-ml1 cannot resolve nh3.internal at all, so on that host the DNS name is the WRONG fix for a dead-IP bug - it swaps a dead address for an unresolvable one. Confirm resolution from inside the container before recommending a name. And a stale link can have more than one drift behind it: voice-studio had three stacked, two of them invisible from the host compose file. Hermes: svos_miranda is installed and enabled in config but the gateway was NOT restarted, so it is not live. agent.disabled_toolsets as specified by svos-dev is not scoped to api_server - it is a strict end-of-pipeline subtraction applied to every session on every platform. Measured: a default session goes 46 tools to 20, losing memory, file, terminal, web, browser and more. It is also unnecessary: platform_toolsets.api_server alone resolves an api_server session to exactly the 8 svos_miranda tools. The line buys only SVOS's startup check, which reads a global endpoint to verify a per-platform property. Left commented out with the measurement inline so an incidental restart cannot gut the assistant.
106 lines
5.8 KiB
Markdown
106 lines
5.8 KiB
Markdown
# svos_miranda Hermes plugin — validation pass (2026-09-15)
|
|
|
|
svos-dev asked infra-ops to run `hermes plugins validate` → `doctor` → `compat`
|
|
on `/home/lkraven/development/svos/hermes_plugin/` and report before enabling.
|
|
Hermes Agent v0.21.1 (2026.9.7), local `b88e6776`, on nh3-dev.
|
|
|
|
## The blocker (found, fixed by svos-dev at `c964e64`)
|
|
|
|
Three absolute intra-package imports — `from hermes_plugin._vendored`, `.forward`,
|
|
`.jwt` — pinned the package to its **source directory name**. The documented
|
|
install renames it to `svos_miranda`, and Hermes loads directory plugins under the
|
|
`hermes_plugins.<dir>` namespace; in neither case does a top-level `hermes_plugin`
|
|
exist. Fix: three relative imports.
|
|
|
|
⚠ **The harness hid this from three different readers.** My first `validate` passed
|
|
the import only because my cwd was the SVOS repo root. svos-dev's test suite
|
|
imports `hermes_plugin.*` from that same root, and their editable install resolves
|
|
the name from anywhere on the box — it only reproduced for them once `sys.path` was
|
|
stripped. Same class as `feedback_filters_that_silently_narrow_the_window`: the
|
|
instrument carried the result.
|
|
|
|
## ⚠ Two of the three commands CANNOT pass this plugin, ever
|
|
|
|
Neither is fixable from the plugin side. Both are now documented in its README.
|
|
|
|
- **`validate`** — two independent causes. Its `RecordingContext.get_config`
|
|
(`hermes_cli/plugin_validate.py:219-222`) returns the **default for every key**,
|
|
ignoring `config.yaml` entirely, so `dispatch_key` is always `""`. And its
|
|
`register_tool` returns `None`, which the plugin's INV-P6 guard correctly reads
|
|
as a name collision — so even with a key supplied it raises on the first tool.
|
|
- **`doctor`** — runs `register()` under a **temp `HERMES_HOME`** with sockets
|
|
blocked, so no config exists there either.
|
|
|
|
## Tool-level gotchas worth remembering
|
|
|
|
- ⚠ **`hermes plugins doctor` exits 0 even when it prints ERROR.** Needs `--ci`.
|
|
- ⚠ **`hermes plugins compat <nonexistent-path>` prints ✓ and exits 0.** A typo'd
|
|
path reads as a pass. (The instrument itself is sound — verified with a throwaway
|
|
plugin importing a real deprecated path, which it flagged with file:line, exit 1.)
|
|
- ⚠ **`doctor`'s sandbox registry starts EMPTY — 0 entries, no built-ins.** So
|
|
doctor cannot detect tool-name collisions at all. `validate`'s separate static
|
|
"built-in tool collisions" check is what covers that.
|
|
- The real `PluginContext.register_tool` (`hermes_cli/plugins.py:449-491`) returns
|
|
a truthy `PluginRegistration` on success — confirmed against the live runtime.
|
|
|
|
## Roster verified another way
|
|
|
|
Since neither command can supply config, a probe mirroring validate's context but
|
|
returning real settings and a truthy handle gave: **8 tools** with
|
|
`repo_read_enabled: true`, **7** with false or omitted, names matching
|
|
`plugin.yaml` exactly, zero hooks/middleware/commands. All nine settings-validation
|
|
controls (quoted booleans, `"90 s"`, zero/negative timeouts, empty/whitespace
|
|
strings) raise errors naming their own key.
|
|
|
|
## A false finding I caught on myself
|
|
|
|
A probe registering `read_file` got back a `PluginRegistration` instead of the
|
|
expected refusal — which looked like the plugin's collision reading was wrong. It
|
|
was not: doctor's sandbox holds no built-ins, so nothing was claimed and **my
|
|
positive case was not positive.** Reported as untested rather than as a finding.
|
|
|
|
## ENABLED in config 2026-09-15 (operator-directed) — but NOT activated
|
|
|
|
Installed to `~/.hermes/plugins/svos_miranda`; `plugins.enabled`, the settings block
|
|
(dispatch key pulled from the vault, verified byte-equal), and
|
|
`platform_toolsets.api_server: [svos_miranda]` all set. Config backed up to
|
|
`config.yaml.bak-20260915-svos-miranda-enable`; diffed against it, only the intended
|
|
non-comment lines changed. `hermes plugins list` → `svos_miranda enabled 0.1.0 user`.
|
|
|
|
**The Hermes gateway was NOT restarted**, so the plugin is not live: the running
|
|
process loaded its plugin set at 2026-09-14 12:35 and `/v1/toolsets` still reports 28
|
|
without `svos_miranda`.
|
|
|
|
## ⚠⚠ `agent.disabled_toolsets` is GLOBAL — it would have cost 26 tools fleet-wide
|
|
|
|
svos-dev's install instructions specify `agent.disabled_toolsets = <the 28 rows minus
|
|
svos_miranda>`. **That key is not scoped to api_server.** It is a strict
|
|
end-of-pipeline subtraction applied to every session on every platform
|
|
(`model_tools.py:216` "subtracted after enabling", applied `:332`; `cli.py:2743` reads
|
|
the same key for the CLI).
|
|
|
|
Measured, not derived:
|
|
|
|
default session WITHOUT the line : 46 tools
|
|
default session WITH the line : 20 tools
|
|
lost 26: memory, read_file, write_file, patch, search_files, terminal,
|
|
process_manage, web_search, web_extract, browser_exec, execute_code,
|
|
computer_use, delegate_task, vision_analyze, video_analyze,
|
|
session_search, skills_*, todo_list, text_to_speech, image_generate, ha_*
|
|
|
|
⭐ **And it is not needed for the security property.** Measured:
|
|
`enabled_toolsets=['svos_miranda'], disabled_toolsets=None` resolves to **exactly the
|
|
8** svos_miranda tools. `platform_toolsets.api_server: [svos_miranda]` already scopes
|
|
Miranda correctly on its own; the global subtraction adds no safety on top.
|
|
|
|
The only thing it buys is satisfying **SVOS's startup roster check, which reads the
|
|
GLOBAL `GET /v1/toolsets` to verify a PER-PLATFORM property.** Raised with svos-dev
|
|
with three options (verify against the api_server surface; accept the cost with
|
|
explicit operator sign-off; or relax the check to "svos_miranda present, write-klass
|
|
five absent"). Left **commented out** in the config with the measurement inline, so an
|
|
incidental Hermes restart cannot gut the operator's assistant.
|
|
|
|
⚠ Minor: `stt` appears in `/v1/toolsets`'s 28 rows but resolving it logs
|
|
`Unknown toolset: stt` — an exact-set comparison pinned to that endpoint can fail for
|
|
reasons unrelated to the plugin.
|