feat(dash): show active model badge on agent cards (closes #2069)
Read the persisted model name from each agent's harness state file (harness/hyperhive-model) and surface it as a small blue badge on the container row in the SW4RM tab. - container_view.rs: add `active_model: Option<String>` to ContainerView; populated by new `read_active_model` helper that reads harness/hyperhive-model; only set when container is running (stale model info from a stopped agent is misleading) - container_view.rs: add active_model to ContainerView literal in host_stats test helper - tabs.js: render badge-model chip after needs-update, before reminders; add active_model to the row fingerprint so re-renders fire on model change - common.css: add .badge-model (blue, 80% opacity — informational)
This commit is contained in:
parent
c8ae3371e1
commit
4375ab6246
4 changed files with 42 additions and 0 deletions
|
|
@ -85,6 +85,11 @@ code {
|
|||
color: var(--purple); border-color: var(--purple);
|
||||
text-shadow: 0 0 6px color-mix(in srgb, var(--purple) 40%, transparent);
|
||||
}
|
||||
/* Active Claude model badge on dashboard container rows. */
|
||||
.badge-model {
|
||||
color: var(--blue); border-color: var(--blue);
|
||||
opacity: 0.8;
|
||||
}
|
||||
/* Context-window usage badges on dashboard container rows. */
|
||||
.badge-ctx-ok {
|
||||
color: var(--green); border-color: var(--green);
|
||||
|
|
|
|||
|
|
@ -521,6 +521,7 @@ window.marked = marked;
|
|||
needs_login: c.needs_login,
|
||||
needs_update: c.needs_update,
|
||||
pending_reminders: c.pending_reminders,
|
||||
active_model: c.active_model,
|
||||
port: c.port,
|
||||
pending,
|
||||
opRunning,
|
||||
|
|
@ -726,6 +727,12 @@ window.marked = marked;
|
|||
));
|
||||
}
|
||||
|
||||
if (c.active_model) {
|
||||
head.append(el('span',
|
||||
{ class: 'badge badge-model', title: `active claude model: ${c.active_model}` },
|
||||
c.active_model));
|
||||
}
|
||||
|
||||
if (c.pending_reminders && c.pending_reminders > 0) {
|
||||
head.append(el('span',
|
||||
{
|
||||
|
|
|
|||
|
|
@ -44,6 +44,12 @@ pub struct ContainerView {
|
|||
/// in the tree. See `docs/agent-hierarchy.md::Current state`.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub parent: Option<String>,
|
||||
/// The Claude model the agent's harness is currently using, read from
|
||||
/// `harness/hyperhive-model`. `None` when the agent has never started
|
||||
/// a turn or the file is absent. Only meaningful when `running` is
|
||||
/// true; the dashboard skips the badge for stopped containers.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub active_model: Option<String>,
|
||||
}
|
||||
|
||||
/// Build the full container list. Wraps `lifecycle::list()` and
|
||||
|
|
@ -78,6 +84,14 @@ pub async fn build_all(coord: &Coordinator) -> Vec<ContainerView> {
|
|||
let needs_login = running
|
||||
&& (!claude_has_session(&Coordinator::agent_claude_dir(&logical))
|
||||
|| auth_failed_sentinel(&logical));
|
||||
// Read the active model from the harness state file. Only surfaced
|
||||
// when the container is running — stale model info from a stopped
|
||||
// agent is misleading (the model may change on next boot).
|
||||
let active_model = if running {
|
||||
read_active_model(&logical)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
out.push(ContainerView {
|
||||
port: lifecycle::agent_web_port(&logical),
|
||||
running,
|
||||
|
|
@ -88,6 +102,7 @@ pub async fn build_all(coord: &Coordinator) -> Vec<ContainerView> {
|
|||
deployed_sha,
|
||||
pending_reminders,
|
||||
parent,
|
||||
active_model,
|
||||
});
|
||||
}
|
||||
out
|
||||
|
|
@ -190,6 +205,20 @@ pub async fn read_agent_status_live(name: &str) -> (Option<String>, Option<i64>,
|
|||
(text, set_at, true)
|
||||
}
|
||||
|
||||
/// Read the active Claude model from the agent's harness state file
|
||||
/// (`harness/hyperhive-model`). Returns `None` when the file is absent
|
||||
/// or empty — callers should treat `None` as "model not yet known".
|
||||
fn read_active_model(name: &str) -> Option<String> {
|
||||
let path = Coordinator::agent_harness_dir(name).join("hyperhive-model");
|
||||
let s = std::fs::read_to_string(path).ok()?;
|
||||
let trimmed = s.trim();
|
||||
if trimmed.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(trimmed.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
/// Host-side hive + swarm display names, read from the c0re service's
|
||||
/// own process env. The `hive-c0re.nix` module sets these from
|
||||
/// `services.hyperhive.{hiveName, swarmName}`. The agent-side
|
||||
|
|
|
|||
|
|
@ -205,6 +205,7 @@ mod tests {
|
|||
deployed_sha: None,
|
||||
pending_reminders: 0,
|
||||
parent: None,
|
||||
active_model: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue