subagent: gate the daemon's model against availableModels

The subagent daemon put `model` straight onto claude's argv with no
validation, so an agent could spawn nested sessions on any model the
operator had deliberately kept off its harness. Forward the existing
`hyperhive.availableModels` onto the daemon unit as
HIVE_AVAILABLE_MODELS (same rail `HIVE_TOOL_GROUPS` uses) and check
`start`/`continue` against it before building the config.

Default open: an absent var restricts nothing, so an agent deployed
before this keeps working. An omitted `model` is always allowed — it
lets claude pick its own default rather than naming one.

Refs #4436
This commit is contained in:
atlas 2026-09-15 23:27:02 +02:00
commit 46a6317f13
2 changed files with 113 additions and 3 deletions

View file

@ -388,6 +388,20 @@ in
# `null` when the agent has no groups declared, which systemd drops
# — the same "absent" the harness itself would see.
HIVE_TOOL_GROUPS = config.systemd.services.hive-agent.environment.HIVE_TOOL_GROUPS or null;
# Same `hyperhive.availableModels` the harness's own assertions gate
# the primary session's model against, so a subagent can't be spawned
# on a model the operator didn't make available to this agent. The
# option renders into the *global* environment for the web UI's
# quick-picker, which a systemd unit doesn't inherit, so set it here
# from the option directly. `null` for an empty list, which systemd
# drops: the daemon reads an absent var as "no restriction" (this is
# a safety rail, not a security boundary), matching the harness
# assertion that an empty list waives too.
HIVE_AVAILABLE_MODELS =
if config.hyperhive.availableModels == [ ] then
null
else
lib.concatStringsSep "," config.hyperhive.availableModels;
# HYPERHIVE_HARNESS_DIR / HYPERHIVE_STATE_DIR: see
# `hive-bash-daemon`'s own comment above — same global injection,
# same reasoning.