Watch
0
0
Fork
You've already forked hyperhive
0

agents: remove the hive-level /model control; the swarm editor sets the model

The per-agent web UI no longer sets the model: the `/model` slash
command, the model picker, `POST /api/model`, `Bus::set_model` and the
`hyperhive-model` writer are gone. The model chip is display-only and
shows where the model comes from (`model_source` in `/api/state`). The
model is set on the swarm (swarm UI agents page, `swarmctl agent
inference set`).

The read side is unchanged: with no swarm value an agent runs its
agent.nix model, else an existing `hyperhive-model` file, else haiku.
Existing files are not touched.

With the picker gone, its model list goes too: `available_models` in
`/api/state`, the ACP model-list filter, and the harness's global
`HIVE_AVAILABLE_MODELS`. The subagent daemon keeps its own
`HIVE_AVAILABLE_MODELS` from mcp.nix, and the `availableModels`
assertion stays.

`/effort` (command, picker, route, file) is unchanged.

Refs #5034
This commit is contained in:
atlas 2026-10-10 23:06:15 +02:00
commit 594693f4c7
26 changed files with 244 additions and 425 deletions

View file

@ -65,10 +65,9 @@ in
description = ''
Claude model for this agent. Sets the `HIVE_DEFAULT_MODEL`
environment variable; the harness applies it at boot and it takes
priority over any persisted runtime override. The operator can still
switch the model at runtime via the per-agent web UI — that choice
is tracked in the state dir for the current session but is reset by
any rebuild that changes this option.
priority over a `hyperhive-model` file in the harness dir. A model the
swarm sets for the agent (the swarm UI's agents page, or
`swarmctl agent inference set`) wins over this option.
Valid values are the short model names that `claude --model` accepts:
`"haiku"`, `"sonnet"`, `"opus"` (or any future identifier). Context
@ -91,22 +90,13 @@ in
"opus"
];
description = ''
Models offered in the per-agent web UI's model quick-picker. Rendered
into the `HIVE_AVAILABLE_MODELS` environment variable (comma-separated)
which the harness surfaces to the agent UI, so the picker lists exactly
these models instead of a hardcoded set.
Configure hive-wide by setting a shared default (e.g. in your
`agent.nix` shared defaults) or per-agent to narrow the menu — for example a
haiku-only agent can hide `opus` and `sonnet`. The *current* model is
still set by `services.hyperhive.agent.model` and remains switchable at runtime via the
UI; this option only controls which choices the picker presents.
Models this agent may run. `services.hyperhive.agent.model` must be one
of them, and the subagent daemon refuses a `start` or `continue` on any
other model (rendered into its `HIVE_AVAILABLE_MODELS`) unless the swarm
sets `subagent_models` for the agent. An empty list restricts nothing.
Values are the short model names that `claude --model` accepts:
`"haiku"`, `"sonnet"`, `"opus"` (or any future identifier). On an ACP
agent this same list also filters the model picker, matched against
the model ids opencode reports (`<provider-id>/<model-id>`); a list
that matches none of them shows every model the session offers.
`"haiku"`, `"sonnet"`, `"opus"` (or any future identifier).
'';
};
@ -390,8 +380,8 @@ in
assertion = config.services.hyperhive.agent.model != "";
message = "services.hyperhive.agent.model must not be empty (set it to e.g. \"haiku\" or \"sonnet\")";
}
# The current model must appear in the quick-picker menu, otherwise the
# UI would offer no way back to the model the agent is actually running.
# The current model must be one this agent may run; the subagent daemon
# checks its own picks against the same list.
{
assertion =
config.services.hyperhive.agent.availableModels == [ ]
@ -436,14 +426,10 @@ in
env = lib.mkIf (preset != null) (lib.mkDefault preset.env);
};
# HIVE_DEFAULT_MODEL seeds the initial model selection when no
# persisted model choice exists in the state dir.
# HIVE_DEFAULT_MODEL is the model the agent runs while the swarm sets
# none; it wins over a `hyperhive-model` file.
environment.variables = {
HIVE_DEFAULT_MODEL = config.services.hyperhive.agent.model;
# Comma-separated menu for the per-agent UI model quick-picker
# (see services.hyperhive.agent.availableModels). The harness surfaces it to the
# frontend; an empty value falls back to the built-in default list.
HIVE_AVAILABLE_MODELS = lib.concatStringsSep "," config.services.hyperhive.agent.availableModels;
# Per-agent baseline effort (see services.hyperhive.agent.effortLevel). The
# harness resolves operator-override-file → this env → "medium"
# and passes it to claude --effort at turn launch.

View file

@ -415,13 +415,11 @@ in
HIVE_ACP_API_KEY_ENV = acpApiKeyEnv;
# Same `services.hyperhive.agent.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.
# on a model the operator didn't make available to this agent. `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.services.hyperhive.agent.availableModels == [ ] then
null