import type { UIMessage } from "ai"; /** * Normalized view over an ai-sdk message part that represents a tool call. * * ai-sdk v6 replaced the single `{ type: "tool-invocation", toolInvocation }` * part with typed parts of shape `{ type: "tool-${toolName}", toolCallId, state, * input, output }`. Some of our code and persisted chats still carry the * legacy shape, so we accept both and expose a single normalized structure. */ export interface NormalizedToolPart { /** Tool name, e.g. "replaceSelection". */ toolName: string; /** "result" when the call has produced output, otherwise the in-flight state. */ state: string; /** Arguments passed to the tool (v5: part.toolInvocation.args, v6: part.input). */ input?: unknown; result?: unknown; } type AnyPart = UIMessage["parts"][number]; /** Returns true if the part represents a tool call (v5 or v6 shape). */ export function isToolPart(part: AnyPart | undefined | null): boolean { if (!part) return false; const type = (part as { type?: string }).type; if (!type) return false; return type === "tool-invocation" || type.startsWith("tool-"); } /** Extract tool name + state from either v5 or v6 tool parts. */ export function normalizeToolPart(part: AnyPart): NormalizedToolPart | null { const anyPart = part as Record; const type = anyPart.type as string | undefined; if (!type) return null; if (type === "tool-invocation") { const inv = anyPart.toolInvocation as | { toolName?: string; state?: string; args?: unknown; result?: unknown } | undefined; if (!inv?.toolName) return null; return { toolName: inv.toolName, state: inv.state ?? "call", input: inv.args, result: inv.result, }; } if (type.startsWith("tool-")) { const toolName = type.slice("tool-".length); const state = (anyPart.state as string | undefined) ?? "input-available"; // Normalize v6 states into the v5 "result" marker expected by UI labels. const normalized = state === "output-available" || state === "output-error" ? "result" : state; return { toolName, state: normalized, input: anyPart.input, result: anyPart.output, }; } return null; }