Owned types

The package defines its own structural message types — and the AI SDK's UIMessage satisfies them for free.

The components read messages through types the package owns, in @intentface/chat/typesChatMessage, MessagePart and its variants (TextPart, ReasoningPart, ToolPart, …), and ChatStatus. The package has no dependency on the AI SDK; its only peer is React.

Structural, not nominal

The types are defined so that the AI SDK's UIMessage satisfies them structurally — no adapter, no mapping layer. A UIMessage's parts already have the shape ChatMessage describes, so you pass your SDK messages straight in and they typecheck.

import type { ChatMessage } from "@intentface/chat/types";
import type { UIMessage } from "ai";

// The app's message type flows into the components unchanged.
type AppMessage = UIMessage<{ stopped?: boolean }>;
const messages: ChatMessage[] = appMessages; // structurally assignable

The app keeps a compile-time assertion that its UIMessage still satisfies ChatMessage, so an AI SDK upgrade that broke the shape would fail typecheck loudly rather than silently.

Extend by generic

ChatMessage<Metadata, Part> takes your metadata and part types as generics, so custom parts and metadata carry through the helpers:

type MyMessage = ChatMessage<{ stopped?: boolean }, MyPart>;

Not the AI SDK

Because the contract is structural, the package works with any source that produces the same shape — a different framework, a raw API, your own store. The AI SDK is the common case, not a requirement, and it never appears in the package's dependency graph.