fix(lens): retain unassigned actor labels in the thread view

This commit is contained in:
moe-berri 2026-10-06 16:29:59 -07:00
parent 5dd7d8286a
commit e2de21fb5b
3 changed files with 30 additions and 0 deletions

View file

@ -40,6 +40,7 @@ export function ConversationSteps({
export function ConversationStep({ item, onOpenStep }: { item: ConversationItem; onOpenStep: (id: string) => void }) {
return (
<section className="min-w-0 space-y-2" aria-label={`Conversation step ${item.span.name}`}>
{item.span.actor_unassigned && <p className="text-xs text-muted-foreground">Unassigned</p>}
{item.toolResult === undefined && (
<div className="flex min-w-0 items-center justify-between gap-2 text-xs text-muted-foreground">
<div className="flex min-w-0 items-center gap-2">

View file

@ -74,6 +74,34 @@ describe("TraceThread", () => {
expect(screen.getByRole("treeitem", { selected: true })).toHaveAttribute("data-row-id", "tool");
});
it.each(["reply", "tool"])("labels an unassigned %s without attributing it to the root actor", async (kind) => {
const user = userEvent.setup();
const foreign = {
...(kind === "reply" ? llm : tool),
span_id: "foreign",
actor_id: null,
actor_unassigned: true,
agent: "",
};
vi.mocked(agentTraceCall).mockResolvedValue({ ...trace, spans: [root, foreign] } as Trace);
vi.mocked(agentTraceSpanCall).mockImplementation(async (_token, _trace, id) =>
id === "root"
? { ...details.root, output: "" }
: { span_id: id, input: "", output: "Content from an unknown actor", attributes: {} },
);
renderWithProviders(
<RoutedRunView traceId={trace.summary.trace_id} accessToken="test" onBack={vi.fn()} embedded />,
);
await user.click(await screen.findByRole("tab", { name: "Thread" }));
const thread = await screen.findByRole("region", { name: "Trace thread" });
if (kind === "tool") {
await user.click(await within(thread).findByRole("button", { name: /^Worked/ }));
await user.click(within(thread).getByRole("button", { name: "Expand read_file tool call" }));
}
expect(await within(thread).findByText("Content from an unknown actor")).toBeVisible();
expect(within(thread).getByText("Unassigned", { exact: true })).toBeVisible();
});
it("continues native child branches and merges actor coverage across trace pages", async () => {
const user = userEvent.setup();
const spans = [

View file

@ -243,6 +243,7 @@ function ReplyMeta({ turn, onOpenStep }: { turn: ThreadTurn; onOpenStep: (id: st
const tokens = item.span.input_tokens + item.span.output_tokens;
return (
<div className="flex items-center gap-3 px-1 text-xs text-muted-foreground tabular-nums">
{item.span.actor_unassigned && <span>Unassigned</span>}
{item.model && <span className="truncate">{item.model}</span>}
<span>{fmtMs(item.span.duration_ms)}</span>
{tokens > 0 && <span>{tokens.toLocaleString()} tok</span>}