Drop classifyEvent.ts, render TermMsg directly
Per review: StreamRow was meant to match what the server sends in TermMsg, not be a separate model needing a translation step. - classifyEvent.ts and streamRow.ts deleted; termMsg.ts holds the wire types (TermMsg/TermEnvelope) plus TermRow, a TermMsg with just the key/fromHistory bookkeeping Preact needs for list rendering. - Row.tsx renders a TermRow directly: level -> CSS class, empty summary + markdown body -> flat row, everything else with a body -> expandable details gated by the operator's preference. No separate classification step. - useLiveStream.ts drops ClassifyCtx (a single incrementing key counter didn't need a whole context object) and maps envelopes to rows inline. - docs/terminal-rendering.md trimmed substantially — was documenting more implementation detail than useful; points at stream_enrich.rs for the per-tool specifics instead of duplicating them in prose.
This commit is contained in:
parent
5eefaa951d
commit
cebf3c6ced
6 changed files with 156 additions and 436 deletions
|
|
@ -1,70 +0,0 @@
|
|||
// Thin adapter: turns one server-classified `TermEnvelope` (hive-agent's
|
||||
// `web_ui/stream.rs`) into zero or more `StreamRow`s. Almost all
|
||||
// classification now happens server-side (`hive-agent/src/term_msg.rs` +
|
||||
// `stream_enrich.rs`) — this file used to be a large per-tool/per-event
|
||||
// dispatch tree (see git history pre mara's terminal-message redesign);
|
||||
// now it's just a shape translation.
|
||||
import type { StreamRow } from './streamRow.js';
|
||||
import { getExpandDetailsPref } from '@hive/shared/prefs.js';
|
||||
|
||||
/** One terminal row as served by `GET /api/events/{history,stream}` —
|
||||
* mirrors `hive-agent/src/term_msg.rs::TermMsg` field-for-field. */
|
||||
export interface TermMsg {
|
||||
icon?: string;
|
||||
level: 'debug' | 'info' | 'warn' | 'error';
|
||||
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. */
|
||||
export interface TermEnvelope {
|
||||
ts: number;
|
||||
seq?: number;
|
||||
msgs: TermMsg[];
|
||||
}
|
||||
|
||||
export interface ClassifyCtx {
|
||||
keySeq: { current: number };
|
||||
}
|
||||
|
||||
export function createClassifyCtx(): ClassifyCtx {
|
||||
return { keySeq: { current: 0 } };
|
||||
}
|
||||
|
||||
function nextKey(ctx: ClassifyCtx): string {
|
||||
ctx.keySeq.current += 1;
|
||||
return 'r' + ctx.keySeq.current;
|
||||
}
|
||||
|
||||
/** `TermEnvelope` → zero or more `StreamRow`s (one per `TermMsg`). */
|
||||
export function classifyEvent(env: TermEnvelope, fromHistory: boolean, ctx: ClassifyCtx): StreamRow[] {
|
||||
return env.msgs.map((m) => termMsgToRow(m, fromHistory, ctx));
|
||||
}
|
||||
|
||||
function termMsgToRow(msg: TermMsg, fromHistory: boolean, ctx: ClassifyCtx): StreamRow {
|
||||
const cssClass = 'level-' + msg.level;
|
||||
const base = { key: nextKey(ctx), cssClass, fromHistory, icon: msg.icon, coalesceKey: msg.coalesce_key };
|
||||
|
||||
if (msg.body == null) {
|
||||
return { ...base, text: msg.summary };
|
||||
}
|
||||
|
||||
// Empty summary + markdown body → the old `.text` row: no prefix line,
|
||||
// the body itself is the whole row (assistant text). Every other
|
||||
// bodied row is an expandable details row, gated uniformly by the
|
||||
// operator's expand-tool-output preference — no server-side per-tool
|
||||
// override any more (mara: "client pref covers every message type
|
||||
// uniformly, no server override even for send/ask/answer/recv").
|
||||
if (msg.body_format === 'markdown' && msg.summary === '') {
|
||||
return { ...base, markdownBody: msg.body };
|
||||
}
|
||||
|
||||
const opened = { ...base, text: msg.summary, details: true, defaultOpen: getExpandDetailsPref() };
|
||||
if (msg.body_format === 'diff') return { ...opened, diffBody: msg.body };
|
||||
if (msg.body_format === 'markdown') return { ...opened, markdownBody: msg.body };
|
||||
return { ...opened, plainBody: msg.body };
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
// Row model for the live event stream. One `StreamRow` = one rendered
|
||||
// line/panel in the terminal pane; `classifyEvent` (classifyEvent.ts)
|
||||
// turns a server-classified `TermMsg` (hive-agent's `term_msg.rs`) into
|
||||
// one of these, and `<Row>` (components/Row.tsx) renders one. Kept as
|
||||
// plain data (not JSX) so the append/coalesce bookkeeping in
|
||||
// useLiveStream.ts stays pure — see docs/terminal-rendering.md for the
|
||||
// taxonomy this mirrors.
|
||||
//
|
||||
// Post mara's terminal-message redesign, `cssClass` is always exactly
|
||||
// `level-debug|info|warn|error` (derived 1:1 from the wire `level`, see
|
||||
// classifyEvent.ts) rather than a free-text per-row-kind class — the
|
||||
// server no longer tells the client "this is a turn-start" or "this is a
|
||||
// tool call", only "this is icon+level+summary+body". `meta`/`childText`
|
||||
// (turn-time, duration, unread-count spans) are gone with them: the
|
||||
// signal that let the client single out a turn-boundary row to attach
|
||||
// them to (the old `kind` tag) no longer exists on the wire, by design —
|
||||
// see term_msg.rs's module doc.
|
||||
|
||||
export interface StreamRow {
|
||||
/** Stable across re-renders; reused in place when a row is coalesced
|
||||
* (e.g. the thinking-token counter) so Preact updates rather than
|
||||
* remounts it. */
|
||||
key: string;
|
||||
/** Always `level-debug` / `level-info` / `level-warn` / `level-error` —
|
||||
* see @hive/shared/terminal.css's level-colour rules. */
|
||||
cssClass: string;
|
||||
icon?: string;
|
||||
fromHistory: boolean;
|
||||
/** When set, an event that maps to the same coalesceKey and lands
|
||||
* while this row is still the last one in the list replaces it in
|
||||
* place instead of appending a new row (thinking-token ticks, status
|
||||
* ticks, plugin_install started→completed). */
|
||||
coalesceKey?: string;
|
||||
/** `false`/absent → flat `<div class="row">`; `true` → `<details
|
||||
* class="row">` with `.summary-text` + optional body. */
|
||||
details?: boolean;
|
||||
defaultOpen?: boolean;
|
||||
/** Flat-row prefix text, or the details `<summary>` text. Linkified,
|
||||
* never markdown. */
|
||||
text?: string;
|
||||
/** Sanitized-markdown body: appended under a flat row (assistant
|
||||
* text, no `text` set) or inside an open details row (tool bodies). */
|
||||
markdownBody?: string;
|
||||
/** Plain `<pre>` body inside a details row (generic long tool output). */
|
||||
plainBody?: string;
|
||||
/** `+`/`-`/context diff body inside a details row (Edit tool). */
|
||||
diffBody?: string;
|
||||
}
|
||||
33
frontend/packages/agent/src/lib/termMsg.ts
Normal file
33
frontend/packages/agent/src/lib/termMsg.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// Wire types for the agent's terminal stream — mirrors hive-agent's
|
||||
// `term_msg.rs`/`web_ui/stream.rs` field-for-field. The frontend renders
|
||||
// a `TermMsg` close to as-is (see components/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."
|
||||
export type Level = 'debug' | 'info' | 'warn' | 'error';
|
||||
|
||||
export interface TermMsg {
|
||||
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. */
|
||||
export interface TermEnvelope {
|
||||
ts: number;
|
||||
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 TermMsg {
|
||||
key: string;
|
||||
fromHistory: boolean;
|
||||
}
|
||||
Loading…
Reference in a new issue