This walks through building a minimal Ask Agent chat surface with
@firmly/ask-agent-sdk on top of the Vercel AI SDK UI
(@ai-sdk/svelte’s Chat). Every SDK call below uses the real exported
function names and parameters — nothing here is illustrative pseudocode. A few
host-supplied glue functions (e.g. currentShopper, runMerchantHook) are your
own code, not SDK exports — each is marked inline where it appears.1
Install
@firmly/ask-agent-sdk isn’t on npm yet — it’s available on request. Contact
Firmly for access to the package.package.json
ai (v6) is only required once you actually construct a transport or chat —
a host that only needs defineCapabilities/createToolDispatcher can import
the SDK without it. For this Svelte walkthrough, also install the AI SDK’s
Svelte binding:2
Fetch the merchant's config
fetchAskAgentConfig calls the backend’s /api/embed/capabilities endpoint
and returns only client-safe data — the merchant’s effective tool/component
set and presentation config, never the system prompt or hook implementations.3
Declare capabilities
Tell the SDK which UI components your host can render. It resolves that list
down to the concrete tool names —
supportedTools — that ride on every chat
request. Your declaration can only narrow the merchant’s enabled tool set,
never widen it.4
Bootstrap the session
A standalone host running on the merchant’s own origin needs a first-party
browser-session token before it can call
/api/chat. createSession mints
or refreshes it over the shopper’s existing session cookie — no API keys in
the browser.5
Create the transport
createAskAgentTransport returns an AI SDK v6 ChatTransport that posts to
/api/chat and injects pageContext/shopperContext/supportedTools into
the request body on every turn.getPageContext and getShopperContext are functions you supply, each
returning a plain object (or a Promise of one) fetched fresh per turn —
e.g. the current PDP/category the shopper is on, or { user, orders } from
your own session state.6
Build the tool dispatcher
Some tools are client-executing: the agent asks a question only your
host page can answer (because only the merchant’s own JavaScript has the
shopper’s session), and your dispatcher handlers answer it.
runDataHook({ name, input })— runs the merchant’s data-hook JS for the requested hook name and returns its JSON-serializable result; the SDK feeds it back to the model viaaddToolResult.onCheckout(input)— fires when the agent callsproceed_to_checkout; swap your UI into checkout. The server tool already resolved, so this handler does not calladdToolResult.onBuyNow(input)— resolves abuy_nowcall (variant lookup + cart/checkout handoff) and returns output the SDK feeds back so the model can confirm the action.
7
Wire the chat
Drop the transport and dispatcher into
@ai-sdk/svelte’s Chat:Complete example
Putting the six pieces together in one component:shouldResumeAfterHook is the hooks-only baseline for sendAutomaticallyWhen
— it resumes the turn once every data-hook tool call in the latest message has a
result. Hosts that also intercept custom UI components or buy_now outside the
dispatcher (as Firmly’s own dogfood widget does) may need a richer resume
predicate that additionally accounts for those tool calls.defineCapabilities,
createSession, createAskAgentTransport, and createToolDispatcher calls work
unchanged with React’s useChat or any other AI SDK UI binding — only the chat
hook itself differs.
- Perfect for the headless SDK
- Consider the drop-in embed instead
You need the assistant to match your app’s design system, render inline in a
native (non-iframe) surface, or run inside a framework shell the drop-in
embed can’t reach.
Next steps
Reference
Full reference for capabilities, transport, session, and the tool dispatcher.
Overview
How the headless SDK fits alongside the drop-in embed.