Watch
0
0
Fork
You've already forked hyperhive
0

a hive-side pause holds against the swarm's declared state until an operator resumes

the harness writes the reason into the pause marker when it pauses itself and reports it as paused_by_hive in its turn-state header. swarm-controller declares such an agent paused (only if declared up) and shows the reason on the agents status row and the swarm ui card.
This commit is contained in:
atlas 2026-11-09 23:09:14 +01:00 • committed by mara
commit e5c55257f8
3 changed files with 517 additions and 26 deletions

View file

@ -43,6 +43,10 @@ export interface AgentRow {
// no header since swarm-controller started, or an agent that does not
// send the flag — not known, so not "needs login".
needs_login: boolean | null;
// Why the agent's own harness paused its turn loop. `null`: it did not, or
// the harness does not send the field. The controller declares such an agent
// paused, and only setting it up again resumes it.
paused_by_hive: string | null;
// The archetype the agent is built from; `null` for a custom agent.
archetype: { source: string; name: string } | null;
}
@ -61,6 +65,13 @@ export function showsLoginNeeded(row: Pick<AgentRow, "needs_login">): boolean {
return row.needs_login === true;
}
// The reason to show on the "paused by hive" badge; `null` shows no badge.
export function hivePauseReason(
row: Pick<AgentRow, "paused_by_hive">,
): string | null {
return row.paused_by_hive ? row.paused_by_hive : null;
}
// Same tone pairing as HivesCard — one freshness enum shared by both
// endpoints, so the same tone rule applies to both pages — but
// `unknown`'s label diverges deliberately: an agent outside the

View file

@ -23,7 +23,12 @@ import { AgentIcon } from "../AgentIcon.js";
import { ClaudeLoginDialog } from "../claude-login/ClaudeLoginDialog.js";
import { AgentDetail } from "../detail/AgentDetail.js";
import { termPaths } from "../terminal/term-paths.js";
import { FRESHNESS, showsLoginNeeded, type AgentRow } from "../AgentTypes.js";
import {
FRESHNESS,
hivePauseReason,
showsLoginNeeded,
type AgentRow,
} from "../AgentTypes.js";
import type { AgentActions } from "./AgentActions.js";
import { WantedMenu } from "./WantedMenu.js";
import { AgentCardLayout } from "./AgentCardLayout.js";
@ -82,6 +87,13 @@ export function AgentCard({
</>
}
/>
{hivePauseReason(row) !== null ? (
<Badge
tone="warning"
value="paused by hive"
title={`the hive paused this agent: ${hivePauseReason(row)}. Set it up again to resume it.`}
/>
) : null}
{showsLoginNeeded(row) ? (
<span
class="ui-agent-card-login"

View file

@ -5,6 +5,7 @@ import assert from "node:assert/strict";
import { test } from "node:test";
import {
canTurnIntoCustom,
hivePauseReason,
showsLoginNeeded,
} from "../src/agent/AgentTypes.ts";
@ -42,3 +43,13 @@ test("turn into custom is offered to a placed archetype agent only", () => {
false,
);
});
test("an agent its hive paused shows the reason, any other shows none", () => {
assert.equal(
hivePauseReason({ paused_by_hive: "skipped get_loose_ends" }),
"skipped get_loose_ends",
);
assert.equal(hivePauseReason({ paused_by_hive: null }), null);
assert.equal(hivePauseReason({ paused_by_hive: "" }), null);
assert.equal(hivePauseReason({}), null);
});