on it, looking for usb-c cables under $20.
React with a 👀
PassonReaction to buy.turn. Show the emoji as a tapback on the shopper’s bubble.
chat.ts
- A message that starts a search gets it the moment
buy.turnopens, before your model runs. - A call that Agentcard has not answered within 1.5 seconds puts the 👀 on a message that has none yet: a slow
yes, a pick, or a confirm that is still placing the order. The tool sends no text with the 👀.
yes, 2, or a code, a step toward checkout such as ok lets checkout, and an acknowledgement such as its ok.
When the 👀 went out, the model reads shopper_already_told_looking: true in that turn’s answers, and its description tells it not to acknowledge the request again. The reply opens with the answer or the question, not on it. The field reads false on a turn that reacted with nothing, and when onReaction threw. An onReaction that throws costs only the reaction.
With onItem on the turn, the 👀 still goes to onReaction and never joins the turn’s items, because it sits on the shopper’s bubble and not in the agent’s sequence.
Send a short text instead
For SMS, or any chat that shows no reactions, passonWorking and no onReaction, or set ack: 'text':
chat.ts
ack takes 'reaction', 'text', or 'none'. Left out, the tool reacts when the tool or the turn has an onReaction, and sends text through onWorking otherwise. With both callbacks, the shopper gets the 👀 and never the text too. 'none', or false, sends nothing.
In text mode, the tool sends the reply when Agentcard has not answered a new request within 1.5 seconds, so a quick answer arrives alone. It sends one for the calls the model makes in one turn. The reply waits for onWorking, so the shopper reads it before any link and before the model answers. An onWorking that throws costs only the instant reply.
The words rotate through six templates, and one tool never sends the same template twice in a row. A short paraphrase of the request goes in when the tool can read one. It stops before any link, email address, phone number, street address, or long number, and a request that starts with one gets a reply that names nothing, such as
looking for that now.
Pass ack: (ask) => text to write your own text from the shopper’s words, where an empty answer sends none. Pass ackDelayMs to change the 1.5 seconds for either mode.
Acknowledge before the model runs
The model takes several seconds to call the tool after the shopper’s message arrives. Withbuy.turn, the turn acknowledges the moment it opens, and calls instantReaction or instantAck for you. Outside a turn, call buy.instantReaction(message) the moment the message arrives, before your model runs, and put the emoji it returns on the message. In text mode, call buy.instantAck(message) and send the text it returns.
chat.ts
hi, a message that carries the shopper’s own details, or a short reply right after a question the last answer asked. A reply that opens a new request, such as find me black running shoes, or that runs past four words is still a search. When either returns an acknowledgement, the tool sends none of its own for that turn.
The reply never reads the shopper’s details back, and it names the request only when it reads as a short, whole phrase. Otherwise it says on it, give me a moment or another reply that names nothing. Pass ack: 'none' to the tool when you acknowledge yourself.
Show a long wait
A turn that runs long, even past a minute, gets no text line. A search often takes 10 to 40 seconds, so a line at any wait short enough to help would land on most searches and bring back the chatter the reaction replaces. To show a long wait, passonProgress and show its stage as a status line or a typing indicator:
chat.ts
stream: false to wait for each whole answer instead.
Check it in the sandbox
- Send “two packs of AA batteries from Amazon” as a test shopper. Check that the 👀 appears on your message at once, before the model answers, and that the reply opens with the answer.
- Send “yes” after a cart. Check that no 👀 appears at once, and that a slow answer gets one after about 1.5 seconds.
- Send “the first one” after a product list. Check that no reaction or text appears when the turn opens.
- Switch to
ack: 'text'. Check that the text names your request or nothing, and that it never repeats an email address or a street address you typed. - Make
onReactionthrow. Check that the turn still answers and the model readsshopper_already_told_looking: false.