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

@ -1,6 +1,7 @@
// <StatusChips> — the agent page's status row: the consolidated status
// badge (alive/thinking/paused/etc., with pause/resume + cancel-turn in
// its dropdown), the model + effort pickers, and context/cost usage.
// its dropdown), the model (display-only: the swarm sets it), the effort
// picker, and context/cost usage.
// This is the concrete fix for the design guide's own named anti-example
// (the old page's model/effort *pickers* lived in the `⋯` overflow menu
// while the current model/effort only showed as a disconnected chip) —
@ -13,6 +14,7 @@
import { useRef, useState } from "preact/hooks";
import { Badge, type BadgeTone } from "@hive/shared/badge.js";
import { Dropdown, type DropdownOption } from "@hive/shared/dropdown.js";
import type { ModelSource } from "../types.js";
import "./StatusChips.css";
export interface StatusChipsProps {
@ -25,8 +27,8 @@ export interface StatusChipsProps {
* operator can tell what an alias resolved to. `undefined` before any
* turn has completed. */
resolvedModel?: string;
availableModels: string[];
onSelectModel: (name: string) => void;
/** Where `model` comes from (`/api/state` `model_source`). */
modelSource?: ModelSource;
effort: string;
availableEfforts: string[];
onSelectEffort: (level: string) => void;
@ -50,10 +52,11 @@ export interface StatusChipsProps {
onLogout: () => Promise<void>;
}
const MODEL_DESCRIPTIONS: Record<string, string> = {
haiku: "fast",
sonnet: "balanced",
opus: "powerful",
const SOURCE_LABEL: Record<ModelSource, string> = {
swarm: "set on the swarm",
agent_nix: "from agent.nix",
local_file: "from its own earlier pick",
default: "the built-in default",
};
const EFFORT_DESCRIPTIONS: Record<string, string> = {
low: "",
@ -235,8 +238,7 @@ export function StatusChips({
statusTooltip,
model,
resolvedModel,
availableModels,
onSelectModel,
modelSource,
effort,
availableEfforts,
onSelectEffort,
@ -263,13 +265,17 @@ export function StatusChips({
onNewSession={onNewSession}
onLogout={onLogout}
/>
<Picker
<Badge
label="model"
value={model}
options={availableModels}
descriptions={MODEL_DESCRIPTIONS}
onSelect={onSelectModel}
title={resolvedModel ? `resolved to ${resolvedModel}` : undefined}
title={
[
modelSource ? SOURCE_LABEL[modelSource] : null,
resolvedModel ? `resolved to ${resolvedModel}` : null,
]
.filter(Boolean)
.join(" · ") || undefined
}
/>
{availableEfforts.length ? (
<Picker