160 lines
5.2 KiB
TypeScript
160 lines
5.2 KiB
TypeScript
// <AgentDetail> — one agent's details: its status, wanted state, hive, config
|
|
// PR, inference settings, resource caps and linked accounts, plus the actions on them. The
|
|
// body of the detail panel on `/agents` (where `withTerminal` adds the live
|
|
// terminal and subagents below it) and of the detail dialog on a terminal
|
|
// page (which is already beside the terminal, so omits it).
|
|
//
|
|
// Takes the agent's *live* row and the shared `AgentActions`, so a state
|
|
// change or a link made here shows wherever else the same row is drawn.
|
|
import { ApiErrorPanel } from "@hive/shared/api-error-panel.js";
|
|
import { Badge } from "@hive/shared/badge.js";
|
|
import { RelativeTime } from "../../ui/relative-time/RelativeTime.js";
|
|
import type { AgentActions } from "../roster/AgentActions.js";
|
|
import { AgentInference } from "./AgentInference.js";
|
|
import { AgentInstructions } from "./AgentInstructions.js";
|
|
import { AgentLimits } from "./AgentLimits.js";
|
|
import { AgentPermissions } from "./AgentPermissions.js";
|
|
import { AllowedDomains } from "./AllowedDomains.js";
|
|
import { AgentStateRow } from "./AgentStateRow.js";
|
|
import { AgentTermPreview } from "../terminal/AgentTermPreview.js";
|
|
import { FRESHNESS, type AgentRow } from "../AgentTypes.js";
|
|
import { LinkAccountMenu } from "./LinkAccountMenu.js";
|
|
import { LinkedAccounts } from "./LinkedAccounts.js";
|
|
import { SubagentTerms } from "../terminal/SubagentTerms.js";
|
|
import { WantedMenu } from "../roster/WantedMenu.js";
|
|
import "./AgentDetail.css";
|
|
|
|
export function AgentDetail({
|
|
row,
|
|
actions,
|
|
withTerminal = false,
|
|
}: {
|
|
row: AgentRow;
|
|
actions: AgentActions;
|
|
withTerminal?: boolean;
|
|
}) {
|
|
const { tone, label } = FRESHNESS[row.freshness];
|
|
const declareError = actions.actionErrors.get(row.name);
|
|
return (
|
|
<div class="ui-agent-detail">
|
|
<dl class="ui-agent-detail-fields">
|
|
<dt>status</dt>
|
|
<dd>
|
|
<Badge
|
|
tone={tone}
|
|
value={
|
|
<>
|
|
{label}
|
|
{row.last_seen_unix !== null ? (
|
|
<>
|
|
{" "}
|
|
(<RelativeTime epochMs={row.last_seen_unix * 1000} />)
|
|
</>
|
|
) : null}
|
|
</>
|
|
}
|
|
/>
|
|
</dd>
|
|
<dt>message</dt>
|
|
<dd>{row.snapshot?.status_text ?? "—"}</dd>
|
|
<dt>wanted</dt>
|
|
<dd>
|
|
{/* Full menu (destroy included) — mara: "destroy is already
|
|
available via wanted state", no separate button needed. */}
|
|
<WantedMenu
|
|
row={row}
|
|
pending={actions.pendingAgents.has(row.name)}
|
|
onSelectUp={actions.selectUp}
|
|
onSelectOffline={actions.selectOffline}
|
|
onSelectPaused={actions.selectPaused}
|
|
onDestroy={actions.requestDestroy}
|
|
/>
|
|
{/* A failed declaration shows here too: on a terminal page's
|
|
dialog there is no roster card to carry the "failed" badge. */}
|
|
{declareError ? (
|
|
<ApiErrorPanel
|
|
context="the declaration failed"
|
|
problem={declareError}
|
|
/>
|
|
) : null}
|
|
</dd>
|
|
<dt>state</dt>
|
|
<dd>
|
|
<AgentStateRow key={row.name} agent={row.name} />
|
|
</dd>
|
|
<dt>hive</dt>
|
|
<dd>{row.hive ?? "—"}</dd>
|
|
<dt>config PR</dt>
|
|
<dd>
|
|
{row.config_pr ? (
|
|
<Badge
|
|
tone="warning"
|
|
value={
|
|
row.config_pr.html_url ? (
|
|
<a
|
|
href={row.config_pr.html_url}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
>
|
|
#{row.config_pr.pr_number}
|
|
</a>
|
|
) : (
|
|
`#${row.config_pr.pr_number}`
|
|
)
|
|
}
|
|
/>
|
|
) : (
|
|
"—"
|
|
)}
|
|
</dd>
|
|
<dt>inference</dt>
|
|
<dd>
|
|
<AgentInference key={row.name} agent={row.name} />
|
|
</dd>
|
|
<dt>instructions</dt>
|
|
<dd>
|
|
<AgentInstructions key={row.name} agent={row.name} />
|
|
</dd>
|
|
<dt>resource caps</dt>
|
|
<dd>
|
|
<AgentLimits key={row.name} agent={row.name} />
|
|
</dd>
|
|
<dt>permissions</dt>
|
|
<dd>
|
|
<AgentPermissions key={row.name} agent={row.name} />
|
|
</dd>
|
|
<dt>allowed domains</dt>
|
|
<dd>
|
|
<AllowedDomains key={row.name} agent={row.name} />
|
|
</dd>
|
|
<dt>accounts</dt>
|
|
<dd>
|
|
{row.hive ? (
|
|
<LinkedAccounts
|
|
key={`${row.hive}/${row.name}`}
|
|
hive={row.hive}
|
|
agent={row.name}
|
|
version={actions.linkVersion}
|
|
/>
|
|
) : (
|
|
"—"
|
|
)}
|
|
</dd>
|
|
</dl>
|
|
<div class="ui-agent-detail-actions">
|
|
<LinkAccountMenu
|
|
agent={row.name}
|
|
hasHive={row.hive !== null}
|
|
onSelect={(kind) => actions.openLink(row, kind)}
|
|
/>
|
|
</div>
|
|
{withTerminal ? (
|
|
<>
|
|
{/* The agent's live terminal and its input box. */}
|
|
<AgentTermPreview agentName={row.name} />
|
|
<SubagentTerms key={row.name} agentName={row.name} />
|
|
</>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|