Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/frontend/packages/shared/src/terminal/termMsg.ts
iris 593923375c swarm-ui: read-only agent terminal page consuming the swarm term stream
Moves the TermMsg rendering pipeline (Row.tsx, termMsg.ts, linkify.tsx,
markdown.ts) from @hive/agent into @hive/shared, so swarm-ui becomes a
second consumer of it instead of forking a copy -- CSS was already
shared (@hive/shared/terminal.css). marked+dompurify move from
@hive/agent's deps to @hive/shared's; swarm-ui picks them up
transitively, no new direct dep there.

New swarm-ui route /agents/:name/term (AgentTermPage), linked from
AgentsPage's detail panel via a "terminal" badge next to "link matrix
account". Consumes GET /api/agents/{name}/term/stream: unlike
@hive/agent's own useLiveStream (TermEnvelope-wrapped, history/backfill
dance), the swarm relay forwards one bare TermMsg per SSE event with no
envelope and no history endpoint -- useSwarmTermStream is a much
smaller hook for that shape (EventSource -> parse -> coalesce, nothing
to buffer/dedupe/backfill against).

Verified against a live SSE mock (screenshots in /agents/iris/state/screenshots/
3801-agents-detail-panel-terminal-badge.png and
3801-agent-term-page-live-rows.png -- real rows rendering through the
shared Row component, not just a build/typecheck pass).
2026-09-13 20:23:51 +02:00

49 lines
2.1 KiB
TypeScript

// Wire types for a classified terminal row — mirrors hive-agent's
// `term_msg.rs`/`web_ui/stream.rs` field-for-field. The frontend renders
// a `TermMsg` close to as-is (see Row.tsx); there's no separate
// client-side row model or classification step any more — mara:
// "StreamRow should now match what the server sends in TermMsg."
//
// Shared between `@hive/agent` (the per-hive local stream, wrapped in
// `TermEnvelope`s) and `@hive/swarm-ui` (the swarm relay's `GET
// /api/agents/{name}/term/stream`, which forwards one bare `TermMsg`
// per SSE event — no envelope, no `seq`, no history endpoint).
export type Level = "debug" | "info" | "warn" | "error";
export interface TermMsg {
/** When the event this row came from happened — ISO 8601 UTC, e.g.
* "2026-09-13T12:35:03Z". The source event's own time, so a row replayed
* from history reads as when it happened rather than when it was fetched. */
ts: string;
icon?: string;
level: Level;
summary: string;
body?: string;
body_format?: "markdown" | "diff";
coalesce_key?: string;
}
/** One SSE frame / history array entry — `hive-agent`'s `TermEnvelope`.
* `seq` is the live per-event dedup counter (`BusEvent::seq`); absent on
* history-replayed envelopes, see useLiveStream.ts's backfill dance. It is
* the only field left: the event's time rides on each row's own `ts`, where
* a consumer reading a bare row (the swarm queue's) can also see it.
*
* Local to the per-hive stream — the swarm relay carries no envelope. */
export interface TermEnvelope {
seq?: number;
msgs: TermMsg[];
}
/** A `TermMsg` plus the bookkeeping Preact needs to render a list —
* stable identity for coalescing/keys, and whether it came from history
* replay vs. the live tail. Not a separate model: everything content-wise
* is still exactly the wire shape. */
export interface TermRow extends Omit<TermMsg, "ts"> {
/** Absent on the rows the client invents for itself — the history/live
* separators, the connection note, a locally echoed prompt — which answer
* to no server event and so have no event time to carry. */
ts?: string;
key: string;
fromHistory: boolean;
}