Watch
0
0
Fork
You've already forked hyperhive
0

swarm-ui: the agent terminal page gets cancel turn, effort, state age and the expand-output setting

The per-agent page had these and the swarm terminal did not; they need no new
backend. A cancel turn button beside send, shown while the agent is in a turn,
posts to the controller's existing cancel route and says whether a turn was
stopped. The terminal's badges add the effort and how long the agent has been in
its turn state. The expand tool output setting moves to the shared package so
both pages render the one component, and the swarm menu gets it. Docs follow.
This commit is contained in:
iris 2026-10-28 20:07:42 +01:00
commit 214d7000d3

View file

@ -8,15 +8,25 @@ import { ApiErrorPanel } from "@hive/shared/api-error-panel.js";
import { readApiError, type ProblemDetails } from "@hive/shared/api-error.js";
import { TextField } from "../../ui/text-field/TextField.js";
import { Button } from "../../ui/button/Button.js";
import { canCancel, cancelNote, cancelPath } from "./cancel.js";
import "./AgentInputForm.css";
type SubmitState =
| { status: "idle" }
| { status: "submitting" }
| { status: "sent" }
| { status: "cancelled"; acted: boolean }
| { status: "error"; problem: ProblemDetails };
export function AgentInputForm({ agentName }: { agentName: string }) {
export function AgentInputForm({
agentName,
turnState,
}: {
agentName: string;
// The agent's turn state when the page has it; the cancel button shows only
// while the agent is in a turn.
turnState?: string;
}) {
const [body, setBody] = useState("");
const [result, setResult] = useState<SubmitState>({ status: "idle" });
@ -43,6 +53,21 @@ export function AgentInputForm({ agentName }: { agentName: string }) {
}
}
async function cancel() {
setResult({ status: "submitting" });
try {
const r = await fetch(cancelPath(agentName), { method: "POST" });
if (!r.ok) {
setResult({ status: "error", problem: await readApiError(r) });
return;
}
const { acted } = (await r.json()) as { acted: boolean };
setResult({ status: "cancelled", acted });
} catch (err) {
setResult({ status: "error", problem: { detail: String(err) } });
}
}
return (
<div class="ui-agent-input">
<form class="ui-agent-input-form" onSubmit={submit}>
@ -60,8 +85,20 @@ export function AgentInputForm({ agentName }: { agentName: string }) {
>
{result.status === "submitting" ? "sending…" : "send"}
</Button>
{canCancel(turnState) && (
<Button
type="button"
onClick={cancel}
disabled={result.status === "submitting"}
>
cancel turn
</Button>
)}
</form>
{result.status === "sent" && <p class="ui-agent-input-sent">sent</p>}
{result.status === "cancelled" && (
<p class="ui-agent-input-sent">{cancelNote(result.acted)}</p>
)}
{result.status === "error" && (
<ApiErrorPanel context="failed to send" problem={result.problem} />
)}