subagents publish their model and effort as headers on every row, and the swarm shows them
The subagent daemon stamps Swarm-Model and Swarm-Effort on each row it publishes. The list endpoint reads them off the newest row it already fetches, so a long-running subagent keeps them past the stream's 24 hour window, and the terminal page's menu and the agent panel's list show them beside the name.
This commit is contained in:
parent
ae9c3444bc
commit
da5fcbfd9b
4 changed files with 219 additions and 25 deletions
|
|
@ -12,7 +12,7 @@ import { useEffect, useState } from "preact/hooks";
|
|||
import { ApiErrorPanel } from "@hive/shared/api-error-panel.js";
|
||||
import { Badge } from "@hive/shared/badge.js";
|
||||
import { AgentTermPreview } from "./AgentTermPreview.js";
|
||||
import { STALE_S, stillListed } from "./subagent-list.js";
|
||||
import { STALE_S, runLabel, stillListed } from "./subagent-list.js";
|
||||
import { useSubagentList } from "./useSubagentList.js";
|
||||
import { RelativeTime } from "../../ui/relative-time/RelativeTime.js";
|
||||
import "./SubagentTerms.css";
|
||||
|
|
@ -41,7 +41,9 @@ export function SubagentTerms({ agentName }: { agentName: string }) {
|
|||
<section class="ui-subagent-terms" aria-label={`${agentName}'s subagents`}>
|
||||
<div class="ui-subagent-terms-list">
|
||||
<span class="ui-subagent-terms-heading">subagents</span>
|
||||
{subagents?.map(({ name, last_row_unix }) => {
|
||||
{subagents?.map((sub) => {
|
||||
const { name, last_row_unix } = sub;
|
||||
const runs = runLabel(sub);
|
||||
const stale =
|
||||
last_row_unix !== null && nowS - last_row_unix > STALE_S;
|
||||
return (
|
||||
|
|
@ -60,6 +62,7 @@ export function SubagentTerms({ agentName }: { agentName: string }) {
|
|||
}
|
||||
onClick={() => setPicked(name === selected ? null : name)}
|
||||
/>
|
||||
{runs && <span class="ui-subagent-terms-age">{runs}</span>}
|
||||
{last_row_unix !== null && (
|
||||
<span class="ui-subagent-terms-age">
|
||||
<RelativeTime epochMs={last_row_unix * 1000} />
|
||||
|
|
|
|||
|
|
@ -45,3 +45,9 @@
|
|||
.ui-subagents-menu-back {
|
||||
color: var(--blue);
|
||||
}
|
||||
|
||||
/* What it runs sits after the name, not centred between name and age. */
|
||||
.ui-subagents-menu-run {
|
||||
margin-right: auto;
|
||||
margin-left: 0.6em;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,12 @@ import { Panel } from "../../ui/panel/Panel.js";
|
|||
import { Popover } from "../../ui/popover/Popover.js";
|
||||
import { RelativeTime } from "../../ui/relative-time/RelativeTime.js";
|
||||
import { termPaths } from "./AgentTermPreview.js";
|
||||
import { runningLabel, splitRunning, type Subagent } from "./subagent-list.js";
|
||||
import {
|
||||
runLabel,
|
||||
runningLabel,
|
||||
splitRunning,
|
||||
type Subagent,
|
||||
} from "./subagent-list.js";
|
||||
import { useSubagentList } from "./useSubagentList.js";
|
||||
import "./SubagentsMenu.css";
|
||||
|
||||
|
|
@ -41,6 +46,11 @@ function Entry({
|
|||
aria-current={current ? "page" : undefined}
|
||||
>
|
||||
<span class="ui-subagents-menu-name">{sub.name}</span>
|
||||
{runLabel(sub) && (
|
||||
<span class="ui-subagents-menu-age ui-subagents-menu-run">
|
||||
{runLabel(sub)}
|
||||
</span>
|
||||
)}
|
||||
{sub.last_row_unix !== null && (
|
||||
<span class="ui-subagents-menu-age">
|
||||
<RelativeTime epochMs={sub.last_row_unix * 1000} />
|
||||
|
|
|
|||
|
|
@ -8,6 +8,19 @@ export interface Subagent {
|
|||
name: string;
|
||||
/** Unix seconds of its newest terminal row, `null` when not readable. */
|
||||
last_row_unix: number | null;
|
||||
/** The model it runs; `null` on the harness's default or when not readable. */
|
||||
model: string | null;
|
||||
/** Its reasoning effort; `null` when not readable. */
|
||||
effort: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* What a subagent runs, as one short label: `opus · high`. Whichever of the
|
||||
* two the controller could not read is left out, and an empty string means
|
||||
* there is nothing to show.
|
||||
*/
|
||||
export function runLabel(sub: Pick<Subagent, "model" | "effort">): string {
|
||||
return [sub.model, sub.effort].filter((v) => v).join(" · ");
|
||||
}
|
||||
|
||||
/** A subagent quiet for longer than this is no longer counted as running. */
|
||||
|
|
|
|||
Loading…
Reference in a new issue