link_sent: true.
Deliver each link
PasssendLink to buy.turn. The tool hands it each link and waits for it, so the shopper holds the link before the model answers.
chat.ts
kind says which step it is:
expiresAt is an ISO 8601 timestamp, or null when Agentcard did not say. Send each url exactly as the link carries it, alone in its own message or behind one button. A Vault or approval link with anything appended to it fails verification and opens the Vault’s sign-in page instead of the step.
Keep the link out of the model’s words
The model’s description tells it never to write a link and to name each link by what it is for: the link to add a card, to sign in to the store, or to approve the payment. When the shopper asks for a link again, the model calls the tool, and the tool sends it again.Send a link in a text chat
In SMS or iMessage, send theurl alone in its own message after the sentence that asks for it. iMessage shows a URL that stands alone in a message as a preview card, and it mangles one with words attached.
With onItem on the turn, the turn holds each step link until the message that asks for it went out, then sends the link right after it. The shopper reads “add your card to place the order” and then taps. A held link also goes out when the turn’s next call starts, or after 20 seconds, so a model that writes nothing still sends it. “Answering each shopper message in its own turn” shows onItem.
Read the links without sendLink
Without sendLink, result.links still carries each Vault, approval, and store sign-in link for your code, and the model still never reads one. Read them from turn.links or from the onResult callback.
Open each step in a pop-up
Each step link carries a stableid, such as vault_1x9k2qd. It is the same every time the same link is handed over, on your server and in the browser, and it has no part of the URL in it, so it is safe as a DOM key or in a log. actionLinkId(link) reads the id from a link that has none.
actionWindows from @agent-cards/commerce-sdk/browser opens a step in a pop-up window centered over the chat, so the shopper never leaves it, and closes the pop-up the moment the message that ends the step arrives. The browser entry imports no Node module, so any bundler takes it.
chat-ui.ts
settle closes each open pop-up whose step the message ends and returns [{ step, outcome }]. A pop-up the shopper closes before the step ends reaches onClosed, checked every 500 milliseconds. The step is still open, so keep its link on screen, and a tap opens it again. A tap on a step whose pop-up is open brings that window to the front instead of opening a second one.
The pop-up is 480 by 760 pixels by default, kept inside the screen. Pass width and height to change it. A phone’s browser opens the pop-up as a tab, and settle closes that tab the same way. dispose() stops watching and leaves every pop-up open, so a chat that remounts never shuts the page where a shopper is typing a card.
Know which message ends which step
Each step ends with a message from Agentcard.actionLinkOutcome(link, message) returns how a message ended the step, or null when it left the step open. ACTION_LINK_ENDINGS holds the whole table:
A reminder, such as
card_link_unfinished or approval_expiring, leaves the step open. An approval is completed once the shopper approved, even when the order then failed or the payment was refused, because the approval page has nothing more to do. The message tells the shopper what happened.
A message whose facts name another link of the same kind (facts.approvalUrl or facts.cardLinkUrl) leaves this step open. So does a message about another store: DoorDash’s store_connected never ends a Locale sign-in the shopper has open beside it.
Handle a failed send
AsendLink that throws on a Vault or approval link makes the call throw, and the model never reads that a link went out. Catch the error, tell the shopper something went wrong, and let them ask again. A link your sendLink throws on stays out of turn.links.
Check it in the sandbox
- Send “two packs of AA batteries from Amazon” as a test shopper who has no card. Check that your app receives a
vaultlink and that the model’s reply says a link is waiting, without containing it. - Ask again. Check that you receive the same link, because Agentcard returns the link it already opened while it has time left.
- Open the link and add a card with the code
111111. Check that the pop-up closes whencard_addedarrives. - Close the pop-up before you finish. Check that
onClosedfires and the link stays on screen. - Search the model’s replies for
https://. Check that none is there.