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
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 thefinish 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.