import React from "react"; import { StyleSheet, Text, View } from "react-native"; import { radius, text } from "../theme/tokens"; import { useTheme } from "../theme/useTheme"; import { Badge } from "./Badge"; import { Mono } from "./primitives"; import type { AgentEvent } from "../api/client"; /** * One stream-json event of a headless run, rendered by type: assistant text as * prose, tool calls as badges, the result as a cost/turns footer, worker notices * as danger callouts, raw lines verbatim. RN port of the web dashboard's * EventLine (frontend RunsSection). */ export function EventLine({ event }: { event: AgentEvent }) { const { colors } = useTheme(); const p = (event.payload ?? {}) as Record; if (event.type === "system") { return ( ▸ session {p.subtype ?? "event"} {p.session_id ? ` · ${String(p.session_id).slice(0, 8)}` : ""} {Array.isArray(p.tools) ? ` · ${p.tools.length} tools` : ""} ); } if (event.type === "assistant") { const content = p.message?.content; const blocks: any[] = Array.isArray(content) ? content : []; const prose = blocks .filter((b) => b?.type === "text" && b.text) .map((b) => b.text) .join("\n"); const tools = blocks.filter((b) => b?.type === "tool_use"); return ( {prose ? ( {prose} ) : null} {tools.length > 0 ? ( {tools.map((t, i) => ( {`${t.name}${t.input ? `: ${oneLine(t.input)}` : ""}`} ))} ) : null} ); } if (event.type === "result") { const err = Boolean(p.is_error); const meta = [ p.num_turns != null ? `${p.num_turns} turns` : "", p.total_cost_usd != null ? `$${Number(p.total_cost_usd).toFixed(4)}` : "", ] .filter(Boolean) .join(" · "); return ( {err ? "run errored" : "run finished"} {meta ? ( {meta} ) : null} {typeof p.result === "string" && p.result ? ( {p.result} ) : null} ); } if (event.type === "worker") { return ( {p.notice ?? "runner notice"} {p.stderr_tail ? ( {p.stderr_tail} ) : null} ); } if (event.type === "raw") { return ( {typeof p.line === "string" ? p.line.trimEnd() : JSON.stringify(p)} ); } // user (tool results) and anything future: a quiet one-liner, nothing lost, no noise. return ( ▸ {event.type} ); } /* Compact single-line preview of a tool_use input object. */ function oneLine(input: unknown): string { const s = typeof input === "string" ? input : (input as Record)?.command ? String((input as Record).command) : JSON.stringify(input); return s.length > 60 ? `${s.slice(0, 57)}…` : s; } const styles = StyleSheet.create({ xs: { fontSize: 12, lineHeight: 18 }, toolRow: { flexDirection: "row", flexWrap: "wrap", gap: 6, alignItems: "center", }, notice: { borderWidth: 1, borderRadius: radius.md, padding: 10, }, });