Skip to main content
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.
Returns a frozen object:
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.
Returns a 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.
Returns 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.
Routing:
  • proceed_to_checkout → calls handlers.onCheckout, then returns (no addToolResult).
  • buy_now → calls handlers.onBuyNow and feeds its output back via addToolResult. If handlers.onBuyNow isn’t provided, dispatch calls addToolResult with { error: 'buy_now_unsupported' }.
  • Any data-hook tool (isHookTool(name) is true) → calls handlers.runDataHook and feeds its output back via addToolResult. If handlers.runDataHook isn’t provided, dispatch calls addToolResult with { 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.
If a handler throws, dispatch catches it and calls addToolResult with { error: String(err.message ?? err) } rather than propagating — a broken hook implementation degrades to an error result, it doesn’t crash the chat.

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.
Returns: booleantrue 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.
Sends the request with 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).
Returns { 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 backing defineCapabilities/the dispatcher — reach for these if you need the raw tool catalog directly.
(name: string) => boolean
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.
Constants:
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.
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:
Returned when the m query parameter is missing:
Never returned (server-only): the system prompt, custom-instruction text, and data-hook snippet code. This endpoint mirrors the same gating the chat route applies when building the tool list, so the capability set a host sees always matches what the agent will actually be given — but it never leaks the implementation behind a hook or the merchant’s prompt customization.