Problem
The hermes driver maintains a set of disabled native tools and exposes
allow-tools so an operator can subtract from it. There is no way to
add to it, so an operator cannot disable a native Hermes tool from
claw-pod.yml.
Setting CLAWDAPUS_DISABLED_TOOLS in the pod's environment: does not work
either: the driver computes its own value and writes it per-service into
compose.generated.yml, overriding whatever the pod supplied.
$ grep CLAWDAPUS_DISABLED_TOOLS compose.generated.yml
CLAWDAPUS_DISABLED_TOOLS: text_to_speech # driver-written, per service
...
CLAWDAPUS_DISABLED_TOOLS: skill_manage # operator anchor, ignored
The runtime side is fine — toolsets.py filters on whatever it receives:
_CLAW_DISABLED_TOOLS = {t.strip() for t in
os.getenv("CLAWDAPUS_DISABLED_TOOLS", "").split(",") if t.strip()}
def _claw_filter_tools(tools):
if not _CLAW_DISABLED_TOOLS:
return tools
return [t for t in tools if t not in _CLAW_DISABLED_TOOLS]
It simply never receives the operator's entries.
Current behaviour
internal/driver/hermes/config.go:436:
func resolveDisabledHermesTools(rc *driver.ResolvedClaw) []string {
if !hasDiscordHandle(rc) && !hasSlackHandle(rc) {
return nil
}
disabled := []string{hermesTextToSpeechTool}
if rc == nil || rc.Hermes == nil || len(rc.Hermes.AllowTools) == 0 {
return disabled
}
// ... drops anything present in AllowTools from `disabled`
}
Consumed at config.go:172 and driver.go:203.
Proposal
Add a disable-tools counterpart alongside the existing allow-tools, in the
same x-claw.hermes block:
services:
my-agent:
x-claw:
hermes:
disable-tools: [skill_manage]
One consistent config surface, no env-var semantics change, no runtime change.
Implementation sketch
-
internal/pod/parser.go:117 — extend rawHermesConfig:
type rawHermesConfig struct {
AllowTools []string `yaml:"allow-tools"`
DisableTools []string `yaml:"disable-tools"`
AllowSilent bool `yaml:"allow-silent"`
}
-
internal/pod/parser.go:995 (parseHermesConfig) — trim/validate
DisableTools the same way AllowTools is handled (reject empty entries
with an indexed error), and include it in the early-return emptiness check
so a block containing only disable-tools still produces a config.
-
internal/driver/types.go:95 — add DisableTools []string to
HermesConfig.
-
internal/driver/hermes/config.go:436 — append rc.Hermes.DisableTools
to disabled before the AllowTools subtraction, deduping.
-
Precedence — suggest allow-tools wins on conflict (subtraction applied
last), matching its current "operator re-enables" meaning; a tool named in
both is enabled. Worth an explicit line in the docs either way.
-
Guard on the handle check — note that resolveDisabledHermesTools
returns nil early when there is no Discord/Slack handle, so today
disable-tools would silently no-op for handle-less hermes services. Either
move the deny list above that check or document the limitation.
Tests
disable-tools alone produces a config (previously nil when allow-tools
was empty and allow-silent false).
- Deny entry reaches
CLAWDAPUS_DISABLED_TOOLS in the generated compose.
- Deny + allow on the same tool resolves per the documented precedence.
- Empty/whitespace entry is rejected with an indexed error.
- Existing
allow-tools behaviour unchanged.
Docs
x-claw reference for hermes: — document disable-tools next to
allow-tools, the precedence rule, and the handle-check caveat.
Motivation
Unattended, scheduled agents. skill_manage lets an agent write into its own
skills directory, and those files are re-read as standing instructions on every
later turn (see #342), so an agent's own conclusion from one turn silently
becomes durable policy with no human in the loop. That is reasonable for an
interactive assistant and risky for a cron-driven one — hence wanting it
configurable per pod rather than removed.
Current workaround is patching toolsets.py from the agent's Clawfile, which
requires an agent image rebuild (neither claw up nor a pull-and-regen
picks it up) and re-breaks whenever that module is reorganized.
Environment
claw version 0.26.0, hermes driver, hermes-base:v2026.6.19-claw.3.
Problem
The hermes driver maintains a set of disabled native tools and exposes
allow-toolsso an operator can subtract from it. There is no way toadd to it, so an operator cannot disable a native Hermes tool from
claw-pod.yml.Setting
CLAWDAPUS_DISABLED_TOOLSin the pod'senvironment:does not workeither: the driver computes its own value and writes it per-service into
compose.generated.yml, overriding whatever the pod supplied.The runtime side is fine —
toolsets.pyfilters on whatever it receives:It simply never receives the operator's entries.
Current behaviour
internal/driver/hermes/config.go:436:Consumed at
config.go:172anddriver.go:203.Proposal
Add a
disable-toolscounterpart alongside the existingallow-tools, in thesame
x-claw.hermesblock:One consistent config surface, no env-var semantics change, no runtime change.
Implementation sketch
internal/pod/parser.go:117— extendrawHermesConfig:internal/pod/parser.go:995(parseHermesConfig) — trim/validateDisableToolsthe same wayAllowToolsis handled (reject empty entrieswith an indexed error), and include it in the early-return emptiness check
so a block containing only
disable-toolsstill produces a config.internal/driver/types.go:95— addDisableTools []stringtoHermesConfig.internal/driver/hermes/config.go:436— appendrc.Hermes.DisableToolsto
disabledbefore theAllowToolssubtraction, deduping.Precedence — suggest
allow-toolswins on conflict (subtraction appliedlast), matching its current "operator re-enables" meaning; a tool named in
both is enabled. Worth an explicit line in the docs either way.
Guard on the handle check — note that
resolveDisabledHermesToolsreturns
nilearly when there is no Discord/Slack handle, so todaydisable-toolswould silently no-op for handle-less hermes services. Eithermove the deny list above that check or document the limitation.
Tests
disable-toolsalone produces a config (previouslynilwhenallow-toolswas empty and
allow-silentfalse).CLAWDAPUS_DISABLED_TOOLSin the generated compose.allow-toolsbehaviour unchanged.Docs
x-clawreference forhermes:— documentdisable-toolsnext toallow-tools, the precedence rule, and the handle-check caveat.Motivation
Unattended, scheduled agents.
skill_managelets an agent write into its ownskills directory, and those files are re-read as standing instructions on every
later turn (see #342), so an agent's own conclusion from one turn silently
becomes durable policy with no human in the loop. That is reasonable for an
interactive assistant and risky for a cron-driven one — hence wanting it
configurable per pod rather than removed.
Current workaround is patching
toolsets.pyfrom the agent's Clawfile, whichrequires an agent image rebuild (neither
claw upnor a pull-and-regenpicks it up) and re-breaks whenever that module is reorganized.
Environment
claw version 0.26.0, hermes driver,hermes-base:v2026.6.19-claw.3.