Skip to main content
If your front end already uses the Vercel AI SDK’s UI hooks (useChat from @ai-sdk/react, and the Vue, Svelte and Angular equivalents), you can keep them and run a Lousho agent behind the route. Three functions, exported from the package root, do the translation. They import nothing from ai, use no node:* module, and work on Node, Workers and any Fetch-based runtime.

Route handler

Without a session, pass every message instead: toUIMessageStreamResponse(agent.stream(fromUIMessages(messages))).

Client

Event mapping

Events with no UI counterpart (retries, compaction, permission decisions, guardrails, …) and the events of sub-agents are not forwarded. The finish chunk’s finishReason is the AI SDK’s (stop, length, content-filter, tool-calls, error; anything else, such as awaiting-approval or max-steps, is other).

Usage and cost

Usage goes in the finish chunk’s messageMetadata, so message.metadata on the finished assistant message has it:
usage is the run’s AgentEventUsage (absent when the run failed); loushoFinishReason is the run’s own finish reason, unmapped.

Approvals and ask_question

A tool that needs approval, or an ask_question call, pauses the run: the stream ends (finish with loushoFinishReason: "awaiting-approval") after a custom data part, which useChat shows as a part of type data-lousho-approval:
kind and question are present only for an ask_question. Render the part with your own buttons and answer it with the session API’s approvals route (POST /chat/:sessionId/approvals/:approvalId, see Sessions and Approvals): { "approved": true } or { "approved": false, "note": "..." } for a tool, { "answer": "prod" } for a question. The response is the continuation as an event stream, which you can read with the UI bindings’ parser or ignore and call useChat’s sendMessage again; or, in your own route, call agent.approvals.streamResolve() / streamAnswer() and return toUIMessageStreamResponse() of the result, so the continuation renders in the same chat.

Inbound conversion

fromUIMessages() maps text parts to text, image/* file parts to image parts and other file parts to file parts (the part’s url, a data: or http(s) URL, becomes the data). Tool, reasoning, data, source and unknown parts are ignored, and messages that end up empty are dropped. A dedicated @lousho/build-ai-agent/ai-sdk-ui subpath may follow; for now the functions come from the package root.