feat: improve edit tool display in web UI

This commit is contained in:
Federico Jaramillo Martinez
2026-05-18 23:05:19 +02:00
parent 547b6e6560
commit ee6f60fea1
15 changed files with 806 additions and 37 deletions
+40 -3
View File
@@ -5,10 +5,13 @@ import {
createAgentSessionFromServices,
createAgentSessionRuntime,
createAgentSessionServices,
createEditToolDefinition,
defineTool,
getAgentDir,
ModelRegistry,
SessionManager,
type CreateAgentSessionRuntimeFactory,
type EditToolDetails,
} from "@earendil-works/pi-coding-agent";
import type { ClientCommand, ClientCommandResult, ClientMessagePage, ClientSession, ClientSessionModel, ClientSessionStatus, ClientThinkingLevel, SessionUiEvent } from "../types.js";
import { pageMessagesAtSafeBoundary } from "./messagePaging.js";
@@ -19,6 +22,7 @@ import { SessionArchiveStore, type ArchivedSessionRecord, type ArchiveSessionInp
import type { ActiveSession } from "./sessionRuntimeStore.js";
import type { AuthChange } from "./authService.js";
import { fallbackSessionName, generateShortSessionName } from "./sessionNameGenerator.js";
import { computeEditPreview, type EditPreviewResult } from "./editPreview.js";
function noop(): void {
// Intentionally empty default unsubscribe callback.
@@ -118,14 +122,39 @@ function defaultCreateAgentRuntime(createRuntime: CreateAgentSessionRuntimeFacto
function createDefaultRuntimeFactory(authStorage: AuthStorage, modelRegistry: ModelRegistryInstance): CreateAgentSessionRuntimeFactory {
return async ({ cwd, agentDir, sessionManager, sessionStartEvent }) => {
const services = await createAgentSessionServices({ cwd, agentDir, authStorage, modelRegistry });
const customTools = [createPiWebEditToolDefinition(cwd)];
const options = sessionStartEvent === undefined
? { services, sessionManager }
: { services, sessionManager, sessionStartEvent };
? { services, sessionManager, customTools }
: { services, sessionManager, sessionStartEvent, customTools };
const result = await createAgentSessionFromServices(options);
return { ...result, services, diagnostics: services.diagnostics };
};
}
type PiWebEditToolDetails = EditToolDetails | { preview: EditPreviewResult } | undefined;
function createPiWebEditToolDefinition(cwd: string) {
const editTool = createEditToolDefinition(cwd);
return defineTool<typeof editTool.parameters, PiWebEditToolDetails>({
name: editTool.name,
label: editTool.label,
description: editTool.description,
...(editTool.promptSnippet === undefined ? {} : { promptSnippet: editTool.promptSnippet }),
...(editTool.promptGuidelines === undefined ? {} : { promptGuidelines: editTool.promptGuidelines }),
parameters: editTool.parameters,
...(editTool.renderShell === undefined ? {} : { renderShell: editTool.renderShell }),
...(editTool.prepareArguments === undefined ? {} : { prepareArguments: editTool.prepareArguments }),
...(editTool.executionMode === undefined ? {} : { executionMode: editTool.executionMode }),
async execute(toolCallId, params, signal, onUpdate, ctx) {
const preview = await computeEditPreview(params.path, params.edits, cwd);
if (signal?.aborted !== true) {
onUpdate?.({ content: [{ type: "text", text: "Edit preview computed." }], details: { preview } });
}
return editTool.execute(toolCallId, params, signal, onUpdate, ctx);
},
});
}
export interface PiSessionServiceDependencies {
archiveStore?: SessionArchiveRepository;
agentDir?: string;
@@ -777,9 +806,13 @@ function toClientEvent(event: unknown): SessionUiEvent {
const args = getProperty(event, "args");
return { type: "tool.start", toolName: getString(event, "toolName") ?? "", toolCallId: getString(event, "toolCallId") ?? "", summary: summarizeToolArgs(args), args };
}
if (eventType === "tool_execution_update") {
const partialResult = getProperty(event, "partialResult");
return { type: "tool.update", toolName: getString(event, "toolName") ?? "", toolCallId: getString(event, "toolCallId") ?? "", text: stringifyToolResult(partialResult), content: toolResultContent(partialResult), details: toolResultDetails(partialResult) };
}
if (eventType === "tool_execution_end") {
const result = getProperty(event, "result");
return { type: "tool.end", toolName: getString(event, "toolName") ?? "", toolCallId: getString(event, "toolCallId") ?? "", text: stringifyToolResult(result), content: toolResultContent(result), isError: getBoolean(event, "isError") === true };
return { type: "tool.end", toolName: getString(event, "toolName") ?? "", toolCallId: getString(event, "toolCallId") ?? "", text: stringifyToolResult(result), content: toolResultContent(result), details: toolResultDetails(result), isError: getBoolean(event, "isError") === true };
}
if (eventType === "agent_start") return { type: "agent.start" };
if (eventType === "agent_end") return { type: "agent.end" };
@@ -822,6 +855,10 @@ function toolResultContent(result: unknown): unknown {
return result;
}
function toolResultDetails(result: unknown): unknown {
return isRecord(result) ? getProperty(result, "details") : undefined;
}
function stringifyToolResult(result: unknown): string {
if (typeof result === "string") return result;
if (Array.isArray(result)) return result.map(stringifyToolResult).filter((text) => text !== "").join("\n");