This page documents the exact public surface of
@firmly/ask-agent-sdk — every symbol it exports
and the one backend endpoint (/api/embed/capabilities) the SDK’s fetchAskAgentConfig calls.
Nothing here is illustrative; every name, parameter, and field matches the source.Capabilities
defineCapabilities({ components })
Declares which UI components/capabilities your host can render. Resolves the declared list down to
the concrete tool names that ride on every chat request.
string[]
required
Capability keys your host supports (e.g.
'product-card', 'followups', 'checkout',
'data-hooks'). Throws if components isn’t an array, or if any key isn’t a recognized capability.string[]
Copy of the capability keys you declared.
string[]
Tool names resolved from
components — this is what you pass as supportedTools to
createAskAgentTransport.allCapabilities()
Returns every capability key a host may declare, as a fresh array. Useful for docs or a “support
everything” declaration.
Returns: string[]
Transport
createAskAgentTransport(options)
Returns an AI SDK v6 ChatTransport that posts to the Ask Agent chat endpoint and injects
pageContext, shopperContext, and supportedTools into the request body on every turn. Drop it
straight into @ai-sdk/svelte’s Chat or React’s useChat.
string
default:"/api/chat"
Chat endpoint, origin-relative on the merchant’s own site.
string
Merchant domain; appended to
api as ?m= so the backend resolves scope.string[]
default:"[]"
Resolved from
defineCapabilities(...).supportedTools.() => object | Promise<object>
Returns the current page payload, called fresh on every turn. Omitted from the request body if not
provided.
() => object | Promise<object>
Returns
{ user, orders } (or your own shopper-shaped payload), called fresh on every turn.
Omitted from the request body if not provided.RequestCredentials
default:"include"
Sent with every request so the first-party session cookie/JWT rides along.
HeadersInit
Extra headers merged into every chat request.
typeof fetch
Custom fetch implementation — e.g. one that attaches a bearer token.
The
ai package is an optional peer dependency, lazily imported (import('ai')) the first
time the returned transport actually sends a message — not when the factory is called or the
module is imported. This lets a host that only needs defineCapabilities/createToolDispatcher
import the SDK without installing ai at all.ChatTransport-shaped object: { prepareSendMessagesRequest, sendMessages, reconnectToStream }.
Tool dispatcher
createToolDispatcher({ handlers })
Builds the function you wire into your chat hook’s onToolCall. Routes each incoming tool call to
the right handler based on the tool’s kind in the catalog.
(ctx: { name: string, input: any }) => any | Promise<any>
Executes a merchant data-hook tool (
is_user_logged_in, get_orders, get_order_details,
get_loyalty_points, or a merchant’s custom_hook_*) and returns its JSON-serializable result.(input: any) => void
Called when the agent triggers
proceed_to_checkout. The server tool already resolved, so this
handler does not call addToolResult.(input: any) => any | Promise<any>
Resolves a
buy_now call — variant lookup plus the cart/checkout handoff — on the client, and
returns output fed back to the model.dispatch({ toolCall, addToolResult }), an async function:
object
required
The tool call from your chat hook’s
onToolCall (AI SDK v6 shape — static or dynamic tool).(r: { tool?: string, toolCallId: string, output: any }) => any
required
Your chat instance’s
addToolResult, used to feed a handler’s output back to resume the turn.proceed_to_checkout→ callshandlers.onCheckout, then returns (noaddToolResult).buy_now→ callshandlers.onBuyNowand feeds its output back viaaddToolResult. Ifhandlers.onBuyNowisn’t provided, dispatch callsaddToolResultwith{ error: 'buy_now_unsupported' }.- Any data-hook tool (
isHookTool(name)is true) → callshandlers.runDataHookand feeds its output back viaaddToolResult. Ifhandlers.runDataHookisn’t provided, dispatch callsaddToolResultwith{ error: 'data_hook_unsupported' }. - All other UI tools (
recommend_add_to_cart,show_product_picks,scroll_to_section,suggest_followups) → no-op; the server tool already returned and the component renders from the streamed part.
shouldResumeAfterHook(messages)
Predicate for your chat hook’s sendAutomaticallyWhen: resumes the agent turn once every data-hook
tool call in the latest assistant message has a result. This is the hooks-only baseline — hosts
that also intercept custom UI components or buy_now outside the dispatcher may need a richer
predicate.
Array<{ role?: string, parts?: any[] }>
required
The chat’s current message list.
boolean — true only if the last message is from the assistant, contains at least one
data-hook tool-call part, and every such part already has a result.
Config
fetchAskAgentConfig({ merchant, api?, fetch? })
Fetches the merchant’s effective Ask Agent configuration from the backend.
string
required
Merchant domain. Throws if omitted.
string
default:"/api/embed/capabilities"
Capabilities endpoint path.
typeof fetch
Custom fetch implementation. Defaults to
globalThis.fetch; throws if neither is available.credentials: 'include' and Accept: application/json. Throws if the
response isn’t ok.
Returns a Promise resolving to the config object — see Capabilities endpoint
below for the exact field list, which this function returns verbatim.
Session
createSession({ merchant, serverOrigin?, storage?, fetch?, now? })
First-party browser-session bootstrap. Mints/refreshes the Firmly browser-session JWT by POSTing to
the merchant’s own origin, so the shopper’s existing first-party session cookie rides along — no API
keys in the browser.
string
required
Merchant domain, used for the
?m= scope. Throws if omitted.string
default:"''"
Origin of the Ask Agent backend. Empty string means same-origin.
Storage | null
Defaults to
globalThis.localStorage (falls back to null if unreachable, e.g. SSR). Pass your
own for testing or a non-browser storage strategy.typeof fetch
Defaults to
globalThis.fetch.() => number
Epoch-seconds clock, injectable for tests. Defaults to
Math.floor(Date.now() / 1000).{ ensureSession, getSession }:
() => Promise<object>
Reads the stored session; if it’s missing, expired, or expiring within 300 seconds, mints/refreshes
it (coalescing concurrent calls into one in-flight request) and persists the result to storage. On
mint failure, warns and falls back to returning the current (possibly stale) session.
() => { accessToken: string | null, deviceId: string | null, expiresAt: number | null }
Read-only synchronous snapshot of the current stored session, without triggering a refresh.
Catalog helpers
Low-level helpers backingdefineCapabilities/the dispatcher — reach for these if you need the raw
tool catalog directly.
Is this tool
kind: 'hidden' — server/metadata only, never rendered?(name: string) => boolean
Is this a merchant “custom component” tool (name starts with
custom_component_)?(name: string) => boolean
Is this a client-executing merchant data-hook — one of
STANDARD_HOOK_TOOLS, a custom_hook_*
name, or catalog kind: 'client-executing'?(name: string) => ToolDescriptor | undefined
Look up a tool’s full catalog descriptor by name.
(capabilities: string[]) => string[]
Resolve capability keys to their backing tool names (sorted, de-duplicated). Throws on any unknown
capability key.
() => string[]
Same function re-exported from
capabilities.js — every capability key a host may declare.ToolDescriptor[]
The full tool catalog — every tool’s
name, kind, capability, component, description,
input, hostHandlers, and terminator metadata.string[]
['is_user_logged_in', 'get_orders', 'get_order_details', 'get_loyalty_points'] — the standard
merchant data-hook tool names.string
'data-hooks' — the capability key that unlocks the standard data-hook tools.string[]
Every host-declarable capability key, including
data-hooks.string[]
Tool names never gated by the host allowlist — every
progress and hidden tool in the catalog.Capabilities endpoint
GET /api/embed/capabilities
Returns the merchant’s effective Ask Agent capability set — the same endpoint fetchAskAgentConfig
calls. Client-safe only.
string
required
Merchant domain (leading
www. is stripped server-side).Response — enabled
string
The merchant domain echoed back.
boolean
true when Ask Agent is configured and enabled for this merchant.string[]
Capability keys this merchant has turned on.
followups is always included; product-card,
product-carousel, and buy-now depend on show_product_card; scroll-to-section depends on
enable_page_navigation; checkout depends on checkout_mode !== 'merchant'; data-hooks is
included only if the merchant has at least one enabled hook.string[]
Concrete tool names backing
enabledComponents, plus this merchant’s actual data-hook tool names
(standard hooks and/or custom_hook_*).string[]
Names of the merchant’s enabled data-hook tools (standard and/or
custom_hook_*).string | null
The merchant’s configured accent color, or
null if unset.string | null
The merchant’s configured welcome message, or
null if unset.string
'firmly' or 'merchant' — mirrors whether checkout is in enabledComponents.boolean
Whether the merchant has voice turned on.
200 — Ask Agent disabled for this merchant
200 — Ask Agent disabled for this merchant
When the merchant’s config exists but Ask Agent isn’t enabled, the endpoint still returns
200
with a minimal body — no component/tool/presentation fields:400 — missing_merchant
400 — missing_merchant
Returned when the
m query parameter is missing: