Align live tool event rendering

This commit is contained in:
Federico Jaramillo Martinez
2026-05-07 14:03:12 +02:00
parent 0b7872b479
commit 886c0fcde9
3 changed files with 42 additions and 6 deletions
@@ -1,6 +1,7 @@
import { api, type CommandResult, type SessionActivity, type SessionInfo, type SessionStatus } from "../api";
import { appendText, normalizeMessages, textMessage } from "../chatMessages";
import { GlobalSessionSocket, SessionSocket, type SessionUiEvent } from "../sessionSocket";
import type { ChatLine, ChatPart } from "../components/shared";
import type { GetState, SetState, UpdateUrl } from "./types";
export class SessionController {
@@ -139,9 +140,9 @@ export class SessionController {
if (event.type === "assistant.delta") {
this.setState({ messages: appendText(messages, "assistant", event.text) });
} else if (event.type === "tool.start") {
this.setState({ messages: [...messages, { role: "tool", parts: [{ type: "toolCall", toolName: event.toolName, summary: "" }] }] });
this.setState({ messages: appendPart(messages, "assistant", { type: "toolCall", toolName: event.toolName, summary: event.summary }) });
} else if (event.type === "tool.end") {
this.setState({ messages: [...messages, textMessage("tool", `${event.isError ? "✖" : "✓"} ${event.toolName}`)] });
this.setState({ messages: [...messages, { role: "tool", parts: [{ type: "toolResult", toolName: event.toolName, text: event.text, isError: event.isError }] }] });
} else if (event.type === "status.update") {
this.applyStatus(event.status);
} else if (event.type === "activity.update") {
@@ -153,3 +154,9 @@ export class SessionController {
}
}
}
function appendPart(messages: ChatLine[], role: ChatLine["role"], part: ChatPart): ChatLine[] {
const last = messages.at(-1);
if (last?.role === role) return [...messages.slice(0, -1), { ...last, parts: [...last.parts, part] }];
return [...messages, { role, parts: [part] }];
}
+2 -2
View File
@@ -2,8 +2,8 @@ import { globalSessionEvents, sessionEvents, type SessionActivity, type SessionS
export type SessionUiEvent =
| { type: "assistant.delta"; text: string }
| { type: "tool.start"; toolName: string }
| { type: "tool.end"; toolName: string; isError: boolean }
| { type: "tool.start"; toolName: string; summary: string }
| { type: "tool.end"; toolName: string; text: string; isError: boolean }
| { type: "status.update"; status: SessionStatus }
| { type: "activity.update"; activity: SessionActivity }
| { type: "command.output"; level: "info" | "success" | "error"; message: string }
+31 -2
View File
@@ -245,13 +245,42 @@ function toClientEvent(event: any): unknown {
return { type: "assistant.delta", text: event.assistantMessageEvent.delta };
}
if (event.type === "tool_execution_start") {
return { type: "tool.start", toolName: event.toolName, toolCallId: event.toolCallId };
return { type: "tool.start", toolName: event.toolName, toolCallId: event.toolCallId, summary: summarizeToolArgs(event.args) };
}
if (event.type === "tool_execution_end") {
return { type: "tool.end", toolName: event.toolName, toolCallId: event.toolCallId, isError: event.isError };
return { type: "tool.end", toolName: event.toolName, toolCallId: event.toolCallId, text: stringifyToolResult(event.result), isError: event.isError };
}
if (event.type === "agent_start") return { type: "agent.start" };
if (event.type === "agent_end") return { type: "agent.end" };
if (event.type === "message_end") return { type: "message.end" };
return { type: "pi.event", eventType: event.type };
}
function summarizeToolArgs(args: any): string {
if (!args || typeof args !== "object") return args == null ? "" : String(args);
if (typeof args.command === "string") return args.command;
if (typeof args.path === "string") return args.path;
if (typeof args.oldText === "string" && typeof args.newText === "string") return "edit text replacement";
if (Array.isArray(args.edits)) return `${args.edits.length} edit${args.edits.length === 1 ? "" : "s"}`;
const entries = Object.entries(args).filter(([, value]) => value != null).slice(0, 3);
return entries.map(([key, value]) => `${key}: ${shortToolValue(value)}`).join(" · ");
}
function shortToolValue(value: unknown): string {
if (typeof value === "string") return value.length > 80 ? `${value.slice(0, 77)}` : value;
if (typeof value === "number" || typeof value === "boolean") return String(value);
if (Array.isArray(value)) return `${value.length} item${value.length === 1 ? "" : "s"}`;
if (typeof value === "object" && value) return "object";
return "";
}
function stringifyToolResult(result: unknown): string {
if (typeof result === "string") return result;
if (Array.isArray(result)) return result.map(stringifyToolResult).filter(Boolean).join("\n");
if (result && typeof result === "object") {
const text = (result as any).text ?? (result as any).content ?? (result as any).output;
if (typeof text === "string") return text;
return JSON.stringify(result, null, 2);
}
return result == null ? "" : String(result);
}