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).
49 lines
2.1 KiB
TypeScript
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;
|
|
}
|