@agent-cards/commerce-sdk 0.10.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +169 -17
- package/dist/ack.d.ts +29 -0
- package/dist/ack.js +48 -1
- package/dist/action-links.d.ts +64 -0
- package/dist/action-links.js +77 -0
- package/dist/browser.d.ts +106 -0
- package/dist/browser.js +118 -0
- package/dist/buy-tool.d.ts +168 -26
- package/dist/buy-tool.js +529 -41
- package/dist/client.d.ts +51 -1
- package/dist/client.js +79 -7
- package/dist/definition.js +31 -3
- package/dist/index.d.ts +6 -5
- package/dist/index.js +5 -4
- package/dist/message.d.ts +10 -2
- package/dist/messages.d.ts +9 -9
- package/dist/messages.js +6 -6
- package/dist/model-view.d.ts +55 -0
- package/dist/model-view.js +92 -6
- package/dist/shopper-message.d.ts +15 -0
- package/dist/shopper-message.js +11 -0
- package/package.json +5 -1
package/dist/client.d.ts
CHANGED
|
@@ -91,6 +91,8 @@ export interface BuyRequest {
|
|
|
91
91
|
confirm?: string | true;
|
|
92
92
|
/** Continue the shopper's newest open conversation instead of starting one. */
|
|
93
93
|
resume?: true;
|
|
94
|
+
/** The conversation a confirm of a cart hash belongs to, in place of `resume`. */
|
|
95
|
+
conversation_id?: string;
|
|
94
96
|
/** Drop the cart on the table and start a new order. */
|
|
95
97
|
new_order?: true;
|
|
96
98
|
/**
|
|
@@ -117,8 +119,23 @@ export interface BuyStore {
|
|
|
117
119
|
export interface BuyCartLine {
|
|
118
120
|
name: string;
|
|
119
121
|
qty: number;
|
|
122
|
+
/** One unit's price, as the store lists it. */
|
|
120
123
|
priceCents?: number;
|
|
124
|
+
/**
|
|
125
|
+
* What the line costs: its price with every add-on, times its quantity, before the store's own fees.
|
|
126
|
+
* The lines' totals add up to the cart's `subtotalCents`. Null when its price is unknown.
|
|
127
|
+
*/
|
|
128
|
+
lineTotalCents: number | null;
|
|
129
|
+
/** The choices made on the line, by the store's names ("Carnitas", "Extra Guacamole"). Empty when none. */
|
|
130
|
+
options: string[];
|
|
131
|
+
/** The variant the line is, by the store's name ("Birthday (Medium)"), or null when the store names none. */
|
|
132
|
+
variant: string | null;
|
|
121
133
|
product_id: string | null;
|
|
134
|
+
/**
|
|
135
|
+
* The id of the product card this line is, as `BuyProduct.ref` names it: a shown product whose `ref`
|
|
136
|
+
* (else `id`) equals it is in the cart. Null from an older API.
|
|
137
|
+
*/
|
|
138
|
+
ref: string | null;
|
|
122
139
|
/** The product's page at its store, as an http or https address, or null when Agentcard sent none. */
|
|
123
140
|
url: string | null;
|
|
124
141
|
}
|
|
@@ -172,9 +189,16 @@ export interface BuyCatalogItem {
|
|
|
172
189
|
ref: string | null;
|
|
173
190
|
name: string | null;
|
|
174
191
|
priceCents: number | null;
|
|
192
|
+
/** True when `priceCents` is a "from" price: the lowest price of a product priced by a required choice, such as a burger's patty count or a drink's size. */
|
|
193
|
+
price_from?: true;
|
|
175
194
|
/** The ISO 4217 code the store prices the product in, or null when Agentcard sent none. */
|
|
176
195
|
currency: string | null;
|
|
177
196
|
image_url: string | null;
|
|
197
|
+
/**
|
|
198
|
+
* Every photo of the product Agentcard sent, `image_url` first, each once, at most eight. One entry when the
|
|
199
|
+
* product has one photo, and none when it has none. An older API sends one photo at most.
|
|
200
|
+
*/
|
|
201
|
+
image_urls: string[];
|
|
178
202
|
store: BuyStore | null;
|
|
179
203
|
/** The product's page at its store, as an http or https address, or null when Agentcard sent none. */
|
|
180
204
|
url: string | null;
|
|
@@ -209,7 +233,7 @@ export interface BuyStoreChoice {
|
|
|
209
233
|
eta_minutes: number | null;
|
|
210
234
|
image_url: string | null;
|
|
211
235
|
}
|
|
212
|
-
/** The stores an answer's reply presents to choose from, at most
|
|
236
|
+
/** The stores an answer's reply presents to choose from, at most eight, in the order the reply names them. */
|
|
213
237
|
export interface BuyStores {
|
|
214
238
|
merchant: string;
|
|
215
239
|
merchant_name: string;
|
|
@@ -236,6 +260,15 @@ export interface BuyPaymentSource {
|
|
|
236
260
|
* not know reads as null.
|
|
237
261
|
*/
|
|
238
262
|
export type BuyNextAction = 'shipping_details' | 'guest_details' | 'store_login' | 'add_card' | 'confirm' | 'verification_code' | 'approve';
|
|
263
|
+
/**
|
|
264
|
+
* A cart the store's checkout is still building after an answer went out, at `store`, since `started_at`: its cart and
|
|
265
|
+
* total reach the shopper as a message of their own (purchase.message `cart_ready`), and only the shopper's yes to that
|
|
266
|
+
* total confirms it.
|
|
267
|
+
*/
|
|
268
|
+
export interface BuyCartBuilding {
|
|
269
|
+
store: string;
|
|
270
|
+
started_at: string;
|
|
271
|
+
}
|
|
239
272
|
/** One turn's answer, every field read for its type; a field an older API does not send reads as null. */
|
|
240
273
|
export interface BuyTurn {
|
|
241
274
|
conversation_id: string;
|
|
@@ -273,6 +306,8 @@ export interface BuyTurn {
|
|
|
273
306
|
stores: BuyStores | null;
|
|
274
307
|
/** The store this turn's `pick.store_id` named, or an answer such as `the second one` picked; null without one. */
|
|
275
308
|
store_pick: BuyStorePick | null;
|
|
309
|
+
/** The cart the store's checkout is still building after this answer; null when none is, and on an older API. */
|
|
310
|
+
cart_building: BuyCartBuilding | null;
|
|
276
311
|
/** The answer as Agentcard sent it. */
|
|
277
312
|
raw: Record<string, unknown>;
|
|
278
313
|
}
|
|
@@ -307,6 +342,8 @@ export interface BuyConversation {
|
|
|
307
342
|
conversation_id: string;
|
|
308
343
|
status: 'open' | 'closed';
|
|
309
344
|
turn_in_progress: boolean;
|
|
345
|
+
/** The cart the store's checkout is still building on this conversation; null when none is, and on an older API. */
|
|
346
|
+
cart_building: BuyCartBuilding | null;
|
|
310
347
|
last_checkout: BuyLastCheckout | null;
|
|
311
348
|
orders: BuyOrder[];
|
|
312
349
|
carts: BuyCart[];
|
|
@@ -342,6 +379,8 @@ export declare class AgentcardApiError extends Error {
|
|
|
342
379
|
}
|
|
343
380
|
/** A turn as Agentcard answers it. */
|
|
344
381
|
export declare function turnOf(body: Record<string, unknown>): BuyTurn;
|
|
382
|
+
/** A cart being built, as an answer, a refusal or the conversation read names it. */
|
|
383
|
+
export declare function cartBuildingOf(value: unknown): BuyCartBuilding | null;
|
|
345
384
|
/** The client every shopper's buy tool shares. Build one per set of credentials and keep it. */
|
|
346
385
|
export declare class AgentcardClient {
|
|
347
386
|
readonly baseUrl: string;
|
|
@@ -394,6 +433,17 @@ export declare class AgentcardClient {
|
|
|
394
433
|
* shopper: the listener and the buy tool share one client in one process.
|
|
395
434
|
*/
|
|
396
435
|
delivered(externalUserId: string, message: ShopperMessage): void;
|
|
436
|
+
/**
|
|
437
|
+
* Notes that your agent answers `message` with a turn of its own (`buy.nextStep`) instead of posting
|
|
438
|
+
* its `text`, so the shopper never reads that text: `delivered` skips it from now on, and no later
|
|
439
|
+
* `buy.turn` hands it to the model as words the shopper read. `buy.nextStep` calls it.
|
|
440
|
+
*/
|
|
441
|
+
answered(externalUserId: string, message: ShopperMessage): void;
|
|
442
|
+
/**
|
|
443
|
+
* Takes back `answered` for a message your agent's own turn could not answer (`BuyToolTurn.release` calls it): once you
|
|
444
|
+
* post its text after all, `delivered` keeps it, and a later `buy.turn` hands it to the model as words the shopper read.
|
|
445
|
+
*/
|
|
446
|
+
unanswered(externalUserId: string, message: ShopperMessage): void;
|
|
397
447
|
/**
|
|
398
448
|
* The messages kept for this shopper, oldest first: each one that reached their chat and that no
|
|
399
449
|
* history `turn.messages` read has held yet. `buy.turn` reads them when it opens.
|
package/dist/client.js
CHANGED
|
@@ -76,6 +76,30 @@ function pageUrl(v) {
|
|
|
76
76
|
return null;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
+
/** The most photos one product carries. */
|
|
80
|
+
const IMAGES_MAX = 8;
|
|
81
|
+
/**
|
|
82
|
+
* A product's photos: `main` first, then each https address in `list`, in its order, each once, at most
|
|
83
|
+
* IMAGES_MAX. A value that is no https address, or that names a user before its host, is left out.
|
|
84
|
+
*/
|
|
85
|
+
function imagesOf(main, list) {
|
|
86
|
+
// The main photo passes the same check as the others: an https address without credentials. It stays as `imageUrl` spells
|
|
87
|
+
// it, so the list still starts with that address, and a later copy of it is skipped by its normalized form.
|
|
88
|
+
const mainPage = pageUrl(main);
|
|
89
|
+
const ok = !!main && !!mainPage && mainPage.startsWith('https:');
|
|
90
|
+
const out = ok ? [main] : [];
|
|
91
|
+
const seen = new Set(ok ? [mainPage] : []);
|
|
92
|
+
for (const v of Array.isArray(list) ? list : []) {
|
|
93
|
+
if (out.length >= IMAGES_MAX)
|
|
94
|
+
break;
|
|
95
|
+
const page = pageUrl(v);
|
|
96
|
+
if (page && page.startsWith('https:') && !seen.has(page)) {
|
|
97
|
+
seen.add(page);
|
|
98
|
+
out.push(page);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
79
103
|
function storeOf(v) {
|
|
80
104
|
if (!isRecord(v) || !str(v.id))
|
|
81
105
|
return null;
|
|
@@ -94,13 +118,22 @@ function cartOf(v) {
|
|
|
94
118
|
merchant: v.merchant,
|
|
95
119
|
merchant_name: str(v.merchant_name) ?? v.merchant,
|
|
96
120
|
store: storeOf(v.store),
|
|
97
|
-
items: Array.isArray(v.items) ? v.items.filter(isRecord).map((i) =>
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
121
|
+
items: Array.isArray(v.items) ? v.items.filter(isRecord).map((i) => {
|
|
122
|
+
const qty = num(i.qty) ?? 1;
|
|
123
|
+
const price = num(i.priceCents);
|
|
124
|
+
return {
|
|
125
|
+
name: str(i.name) ?? '',
|
|
126
|
+
qty,
|
|
127
|
+
...(price !== null ? { priceCents: price } : {}),
|
|
128
|
+
// An older API sends no line total: the line's price times its quantity is it.
|
|
129
|
+
lineTotalCents: num(i.lineTotalCents) ?? (price !== null ? price * qty : null),
|
|
130
|
+
options: Array.isArray(i.options) ? i.options.filter((o) => typeof o === 'string' && o.trim() !== '') : [],
|
|
131
|
+
variant: str(i.variant),
|
|
132
|
+
product_id: str(i.product_id),
|
|
133
|
+
ref: str(i.ref),
|
|
134
|
+
url: pageUrl(i.url),
|
|
135
|
+
};
|
|
136
|
+
}) : [],
|
|
104
137
|
subtotalCents: num(v.subtotalCents),
|
|
105
138
|
serviceFeesCents: num(v.serviceFeesCents) ?? 0,
|
|
106
139
|
merchantFeeLines: feeLinesOf(v.merchantFeeLines),
|
|
@@ -138,8 +171,10 @@ function catalogOf(v) {
|
|
|
138
171
|
ref: str(i.ref),
|
|
139
172
|
name: str(i.name),
|
|
140
173
|
priceCents: num(i.priceCents),
|
|
174
|
+
...(i.price_from === true && num(i.priceCents) !== null ? { price_from: true } : {}),
|
|
141
175
|
currency: str(i.currency),
|
|
142
176
|
image_url: str(i.image_url),
|
|
177
|
+
image_urls: imagesOf(str(i.image_url), i.image_urls),
|
|
143
178
|
...(flight ? { flight } : {}),
|
|
144
179
|
store: storeOf(i.store),
|
|
145
180
|
url: pageUrl(i.url),
|
|
@@ -194,15 +229,21 @@ export function turnOf(body) {
|
|
|
194
229
|
pick: isRecord(body.pick) && str(body.pick.product_id) ? { product_id: body.pick.product_id, ref: str(body.pick.ref), in_cart: body.pick.in_cart === true } : null,
|
|
195
230
|
stores: storesOf(body.stores),
|
|
196
231
|
store_pick: isRecord(body.store_pick) && str(body.store_pick.store_id) ? { store_id: body.store_pick.store_id, name: str(body.store_pick.name), selected: body.store_pick.selected === true } : null,
|
|
232
|
+
cart_building: cartBuildingOf(body.cart_building),
|
|
197
233
|
raw: body,
|
|
198
234
|
};
|
|
199
235
|
}
|
|
236
|
+
/** A cart being built, as an answer, a refusal or the conversation read names it. */
|
|
237
|
+
export function cartBuildingOf(value) {
|
|
238
|
+
return isRecord(value) && str(value.store) ? { store: value.store, started_at: str(value.started_at) ?? '' } : null;
|
|
239
|
+
}
|
|
200
240
|
function conversationOf(body) {
|
|
201
241
|
const last = isRecord(body.last_checkout) ? body.last_checkout : null;
|
|
202
242
|
return {
|
|
203
243
|
conversation_id: str(body.conversation_id) ?? '',
|
|
204
244
|
status: body.status === 'closed' ? 'closed' : 'open',
|
|
205
245
|
turn_in_progress: body.turn_in_progress === true,
|
|
246
|
+
cart_building: cartBuildingOf(body.cart_building),
|
|
206
247
|
last_checkout: last ? {
|
|
207
248
|
at: str(last.at) ?? '',
|
|
208
249
|
merchant: str(last.merchant) ?? '',
|
|
@@ -516,6 +557,37 @@ export class AgentcardClient {
|
|
|
516
557
|
if (this.inbox.size > DELIVERED_SHOPPERS)
|
|
517
558
|
this.inbox.delete(this.inbox.keys().next().value);
|
|
518
559
|
}
|
|
560
|
+
/**
|
|
561
|
+
* Notes that your agent answers `message` with a turn of its own (`buy.nextStep`) instead of posting
|
|
562
|
+
* its `text`, so the shopper never reads that text: `delivered` skips it from now on, and no later
|
|
563
|
+
* `buy.turn` hands it to the model as words the shopper read. `buy.nextStep` calls it.
|
|
564
|
+
*/
|
|
565
|
+
answered(externalUserId, message) {
|
|
566
|
+
if (!str(externalUserId))
|
|
567
|
+
throw new TypeError('Pass externalUserId: your own id for the shopper the message is for.');
|
|
568
|
+
if (!isRecord(message))
|
|
569
|
+
throw new TypeError('Pass the message onMessage got.');
|
|
570
|
+
if (str(message.eventId)) {
|
|
571
|
+
this.deliveredIds.add(message.eventId);
|
|
572
|
+
if (this.deliveredIds.size > DELIVERED_IDS_KEPT)
|
|
573
|
+
this.deliveredIds.delete(this.deliveredIds.values().next().value);
|
|
574
|
+
}
|
|
575
|
+
const kept = this.inbox.get(externalUserId);
|
|
576
|
+
if (kept)
|
|
577
|
+
this.forgetMessages(externalUserId, kept.filter((m) => m === message || (!!message.eventId && m.eventId === message.eventId)));
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Takes back `answered` for a message your agent's own turn could not answer (`BuyToolTurn.release` calls it): once you
|
|
581
|
+
* post its text after all, `delivered` keeps it, and a later `buy.turn` hands it to the model as words the shopper read.
|
|
582
|
+
*/
|
|
583
|
+
unanswered(externalUserId, message) {
|
|
584
|
+
if (!str(externalUserId))
|
|
585
|
+
throw new TypeError('Pass externalUserId: your own id for the shopper the message is for.');
|
|
586
|
+
if (!isRecord(message))
|
|
587
|
+
throw new TypeError('Pass the message onMessage got.');
|
|
588
|
+
if (str(message.eventId))
|
|
589
|
+
this.deliveredIds.delete(message.eventId);
|
|
590
|
+
}
|
|
519
591
|
/**
|
|
520
592
|
* The messages kept for this shopper, oldest first: each one that reached their chat and that no
|
|
521
593
|
* history `turn.messages` read has held yet. `buy.turn` reads them when it opens.
|
package/dist/definition.js
CHANGED
|
@@ -10,9 +10,11 @@ export const BUY_TOOL_NAME = 'buy';
|
|
|
10
10
|
export const BUY_TOOL_DESCRIPTION = [
|
|
11
11
|
'Buy what this shopper asks for at real stores.',
|
|
12
12
|
"Put the shopper's own words in `ask`.",
|
|
13
|
+
'When the shopper sends a link to a product, such as a buy.trychannel3.com link, put the whole link in `ask` exactly as they sent it, query string included, with their words beside it: Agentcard buys that exact product from the link, so never describe the link in words instead.',
|
|
13
14
|
'Tell the shopper what `reply` says, in your own voice, except the products and stores it lists that reach the shopper as cards.',
|
|
14
|
-
"When `shopper_already_told_looking` is true, the shopper
|
|
15
|
+
"When `shopper_already_told_looking` is true, the shopper already knows you are working on their request, from a reply or a 👀 on their message, so never acknowledge it again: open with the answer or the question.",
|
|
15
16
|
'Show the shopper the cart before you confirm it.',
|
|
17
|
+
'When `cart_building` is set, the store is still building that cart: tell the shopper in one short line that its total is on its way, never ask them to confirm it, and confirm it only after they say yes to the total in the message Agentcard sends next.',
|
|
16
18
|
'Ask the shopper for exactly one thing per message: the step `next_action` names, even when `reply` lists more. Never mention a later step, and never ask for the next one until the shopper has done this one.',
|
|
17
19
|
'When `next_action` is null and `reply` is about something other than a cart, ask for no checkout step: a cart from an earlier request gets one short line at most, after the answer to what the shopper just asked, and never a question about confirming or placing it.',
|
|
18
20
|
'Set `confirm` to true only when the shopper just said yes to the cart in your last result, asks to check out or pay for a cart that waits, says they added the card for a cart that waits, says they approved, or asks for the approval link again.',
|
|
@@ -21,17 +23,43 @@ export const BUY_TOOL_DESCRIPTION = [
|
|
|
21
23
|
'Each link reaches the shopper on its own.',
|
|
22
24
|
'When `link_sent` is true, tell the shopper a link is waiting for them.',
|
|
23
25
|
'Product links reach the shopper on their own, as cards that show each product\'s name, price and picture. When `product_links_sent` is above zero, the shopper already sees those products, so never list them again, by name, price or number, even when `reply` lists them: write one short line, such as which one they want, plus only what the cards do not show, such as why one is the pick. When the shopper asks for links, tell them the links are there; never write or invent a URL.',
|
|
26
|
+
"A product's `price` that reads `from $10.69` is the lowest price it comes at: a size or another choice the shopper makes sets the rest. Say it as `from $10.69`, and never call a price a placeholder or say it shows $0.",
|
|
27
|
+
'A fact the cards do not show belongs to one product, such as a table time, a class time or where a place is, so name that product with it, by its `name` ("Delfina has 7:45"), never "one of these", "the other two" or "one of the spots i sent": the shopper cannot tell which card that means.',
|
|
24
28
|
'A `placing` status means the order is going through, so never ask for another approval.',
|
|
25
29
|
'Tell the shopper they need not reply only when `shopper_need_not_reply` is true.',
|
|
26
30
|
'After `needs_card` with a cart, Agentcard keeps the cart, so set `confirm` to true once the shopper says they added the card.',
|
|
27
31
|
"After `needs_card` with no cart, call this tool again with the shopper's request once they say they added a card.",
|
|
28
|
-
'Set `new_order` to true only when the shopper drops the cart.',
|
|
32
|
+
'Set `new_order` to true only when the shopper drops the cart. When `carts` holds more than one cart and the shopper drops one of them, keep `new_order` false and say in `ask` which cart goes: the other carts stay.',
|
|
33
|
+
'The shopper can buy from more than one store at once: each store keeps its own cart in `carts`, every cart is its own order, and the shopper confirms each one on its own. Never tell the shopper an order can only come from one store.',
|
|
29
34
|
"Never tell the shopper that you or Agentcard can't see their name, phone number or address: Agentcard may use the ones saved on their Agentcard account for an order, and `reply` says when it does.",
|
|
30
35
|
"When the shopper picks one of the `products` in your last result, by its name, its place in the list or a tap that names it, set `product_id` to that product's `product_id`, so Agentcard adds exactly that product; otherwise set it to null.",
|
|
36
|
+
"When the shopper's words fit more than one of the `products`, such as \"both\" or \"add dune\" while you or `reply` still asks which of two editions, set `product_id` to null and pass their words in `ask`: Agentcard adds what they settled and asks about the rest. Never add every product their words could mean.",
|
|
31
37
|
"When the shopper picks one of the `stores` in your last result, by its name, its place in the list or a tap that names it, set `store_id` to that store's `store_id`, so Agentcard orders from exactly that store; otherwise set it to null. Never set both `product_id` and `store_id`.",
|
|
32
38
|
'Store links reach the shopper on their own, as cards that show each store\'s name and delivery time. When `store_links_sent` is above zero, never list those stores again: ask which one in one short line.',
|
|
33
39
|
'When `store_links_sent` is 0 and you offer the `stores`, name each one by its `name`, never only "their links are there".',
|
|
34
40
|
'When `pick.in_cart` is false, the product the shopper picked is not in the cart: tell the shopper what `reply` says, and never ask them to confirm the cart.',
|
|
41
|
+
"Talk to the shopper only about their order, in their terms: what is in the cart, what was found and what they can do next. Leave out how it was done, even when `reply` says it: never mention this tool, `reply`, a field, a system, \"my end\" or what you can or can't see, the searches run, the stores checked or not, a timeout, account setup, or a link that did not go out. When parts of the result disagree, say what you are sure of and ask, never which part said what.",
|
|
42
|
+
'Never tell the shopper what has not happened yet or where things stand inside, such as "nothing\'s been charged yet", "your order isn\'t placed yet" or "the address isn\'t locked in", even when `reply` says it: ask for what is needed instead, such as "should it ship to 1 ferry plz?".',
|
|
43
|
+
'When `next_action` is `store_login`, `add_card` or `approve`, the message that asks for it is one short line and nothing else, even when `reply` says more, such as "sign in to doordash to place your order", "add your card to place the order" or "approve the payment to place your order (up to $19.02)": no recap of the cart, the address or the price, nothing about tax, delivery, shipping, a hold or what was charged, no word on when the link runs out, and never a request to tell you once it is done. On `approve`, put the cart\'s `max_charge` in the parentheses when it is set, and no other amount. Only when the shopper\'s last message picked what went in the cart does a line about the cart come first, as its own message; after an address, a phone number, a yes or a saved card, the ask is the whole reply.',
|
|
44
|
+
'When `next_action` is `confirm`, ask in one short message: the items, one total, and to say yes to place it, such as "1 carolina black socks pack from walmart, about $9.57. say yes to place it". Leave out the fees one by one, the hold, and the address unless the shopper just changed it.',
|
|
45
|
+
'Every line you write reaches the shopper, a line before you call this tool too: write to them as "you", never about "the shopper" or "the user", and never say what you are about to do with this tool, such as "I\'ll add them now".',
|
|
46
|
+
'Say as fact only what the result shows: never promise what a store will do later, such as confirm a size when the order goes through.',
|
|
47
|
+
'Never tell the shopper to remove, delete or replace a card or to change a setting, even when `reply` says to.',
|
|
48
|
+
'Name each link by what it is for, from `next_action`: the link to add a card, to sign in to the store, or to approve the payment.',
|
|
49
|
+
'Within a step, ask for one detail per message, such as the delivery address before the name for the label.',
|
|
50
|
+
'Write every sentence in your own style, casing included, even where `reply` is written another way.',
|
|
51
|
+
"When the shopper asks what you can do or which stores you shop at, answer without calling this tool and with no line saying you are looking: one message that names at most four stores plus the kinds of things you can do, such as food delivery and restaurant reservations, then one that asks what they need. Every shopper can buy from Amazon, Walmart, Target and Best Buy, so name those, and no other store unless a result showed it.",
|
|
52
|
+
'Each paragraph you write reaches the shopper as its own message: give each one job and keep it under about 20 words. On a cart turn, write the confirmation and the one next step, such as "added a little cheeseburger from five guys, about $12.17 before delivery and tax" and then "next, sign in to doordash with the link so i can place it": one total, never a price breakdown your app\'s cart already shows (an app that shows no cart lists each line with its price) and never a fee by its name, rate or formula, such as an order fee, card processing or 2.9% + $0.30, even when `reply` or `fee_lines` names it, unless the shopper asks what the fees are, no promise to show the final total later, and nothing about passkeys, master passwords or Agentcard not seeing the card number unless the shopper asks.',
|
|
53
|
+
'Never listing products again is for offering choices: when you confirm what is in the cart, name each line by its item, such as "1 Dawn Ultra dish soap, 38 oz", never "the one you picked, shown in the card".',
|
|
54
|
+
'Say how long a link works only when the shopper asks, with `link_expires_in`, such as "it works for about 24 hours", never as a raw date and time or in UTC, such as `approval_expires_at` holds; when `link_expires_in` is null, say nothing about when it runs out.',
|
|
55
|
+
'Never call a product the shopper was sent "the card", in any language ("la tarjeta", "o cartão"): to the shopper a card is how they pay. Call it by its name, or "the one i sent".',
|
|
56
|
+
'When the cart has `total_leaves_out`, its `total` is before those: say so, such as "about $18.41 before tax and shipping", and never say it is with or includes tax, shipping or delivery.',
|
|
57
|
+
'When the shopper gave a budget, compare it with the cart\'s estimated `total`, never with `max_charge`: `max_charge` is a temporary hold, and what the order does not use is released. Warn about the budget only when `total`, or a shipping cost the result shows, is above it.',
|
|
58
|
+
"Never say how or on whose account an order is placed, and never send the shopper to their Agentcard dashboard, settings or account or ask them to link an account there, even when `reply` says to. When they ask for something an order can't do, such as Prime shipping, say plainly that it doesn't apply and what that means for the order, such as \"prime shipping doesn't apply on these orders, so amazon may add shipping on carts under about $35\".",
|
|
59
|
+
'Say what was found, never how searching works, even when `reply` says it: never how much of a store\'s stock shows, what a page or a link showed, or whether a store picked something. Say the outcome and the next step, such as "i couldn\'t find it at best buy. want me to check amazon?". Only when the shopper asks what the search covers, answer in one plain line.',
|
|
60
|
+
'Agentcard charges nothing for a reservation, but the restaurant may take a card on its own page: say what it takes only from the cart\'s item line, which names a "card hold", a "deposit" or "prepaid" when there is one, the same way every time, and never give a reservation a most-you-could-be-charged amount.',
|
|
61
|
+
'Ask the shopper to "let me know", never "text me", unless your app is a text-message chat.',
|
|
62
|
+
'Name only the stores and products that reach the shopper as cards in this result, never others in passing, such as "five guys and mcdonald\'s deliver there too", even when `reply` names them: to offer more, ask "want other options?". This is for what you suggest: when you show the cart or ask for a yes to it, name what is in it.',
|
|
35
63
|
].join(' ');
|
|
36
64
|
/** A fresh copy of the tool's definition, so a caller that edits one never changes another. */
|
|
37
65
|
export function buyToolDefinition() {
|
|
@@ -46,7 +74,7 @@ export function buyToolDefinition() {
|
|
|
46
74
|
type: 'boolean',
|
|
47
75
|
description: 'True only when the shopper just said yes to the cart in your last result, asks to check out or pay for a cart that waits, says they added the card for a cart that waits, says they approved, or asks for the approval link again. False otherwise.',
|
|
48
76
|
},
|
|
49
|
-
new_order: { type: 'boolean', description: 'True only when the shopper drops the cart to start a new order. False otherwise.' },
|
|
77
|
+
new_order: { type: 'boolean', description: 'True only when the shopper drops the cart to start a new order. False when they drop one of several carts, and otherwise.' },
|
|
50
78
|
product_id: {
|
|
51
79
|
type: ['string', 'null'],
|
|
52
80
|
description: "The `product_id` of the product the shopper picked from the `products` in your last result. Null when they picked none.",
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
export { verifyAgentcardWebhook, AgentcardSignatureError, type AgentcardSignatureReason, type AgentcardEvent, type VerifyOptions, } from './verify.js';
|
|
2
2
|
export { readPurchaseMessage, type PurchaseMessage, type PurchaseMessageKind, type PurchaseFacts, } from './message.js';
|
|
3
|
-
export { readShopperMessage, isStale, SHOPPER_EVENT_TYPES, type ShopperMessage, type ShopperMessageKind, type ShopperFacts, type ShopperEventType, type OrderStatus, } from './shopper-message.js';
|
|
3
|
+
export { readShopperMessage, isStale, SHOPPER_EVENT_TYPES, NEXT_STEP_KINDS, type NextStepKind, type ShopperMessage, type ShopperMessageKind, type ShopperFacts, type ShopperEventType, type OrderStatus, } from './shopper-message.js';
|
|
4
4
|
export { agentcardWebhook, purchaseAgentWebhook, type AgentcardWebhookOptions, type AgentcardWebhookHandler, type PurchaseAgentWebhookOptions, type WebhookFetchRequest, type WebhookRequest, type WebhookResponse, } from './handler.js';
|
|
5
|
-
export { AgentcardClient, AgentcardApiError, BUY_TURN_TIMEOUT_MS, DEFAULT_BASE_URL, type AgentcardClientOptions, type FetchLike, type FetchLikeResponse, type BuyProgress, type BuyProgressStage, type BuyCatalogUpdate, type BuyStreamHandlers, type Shopper, type BuyRequest, type BuyDeliveryAddress, type BuyTurn, type BuyStatus, type BuyCart, type BuyCartLine, type BuyCartFeeLine, type BuyStore, type BuyCatalog, type BuyCatalogItem, type BuyFlightItinerary, type BuyPaymentSource, type BuyConversation, type BuyLastCheckout, type BuyOrder, type VaultLink, type BuyNextAction, type BuyPick, type BuyStoreChoice, type BuyStorePick, type BuyStores, } from './client.js';
|
|
5
|
+
export { AgentcardClient, AgentcardApiError, BUY_TURN_TIMEOUT_MS, DEFAULT_BASE_URL, type AgentcardClientOptions, type FetchLike, type FetchLikeResponse, type BuyProgress, type BuyProgressStage, type BuyCatalogUpdate, type BuyStreamHandlers, type Shopper, type BuyRequest, type BuyDeliveryAddress, type BuyTurn, type BuyStatus, type BuyCart, type BuyCartBuilding, type BuyCartLine, type BuyCartFeeLine, type BuyStore, type BuyCatalog, type BuyCatalogItem, type BuyFlightItinerary, type BuyPaymentSource, type BuyConversation, type BuyLastCheckout, type BuyOrder, type VaultLink, type BuyNextAction, type BuyPick, type BuyStoreChoice, type BuyStorePick, type BuyStores, } from './client.js';
|
|
6
6
|
export { BUY_TOOL_NAME, BUY_TOOL_DESCRIPTION, buyToolDefinition, parseBuyToolInput, type BuyToolDefinition, type BuyToolInput, type BuyToolInputSchema, type BuyToolInputCheck, type BuyToolStandardSchema, type OpenAIBuyTool, } from './definition.js';
|
|
7
7
|
export { toMessages, messageStream, typingDelay, type MessagePace, type MessageStream, type MessageStreamOptions, } from './messages.js';
|
|
8
|
-
export { buyTool, type BuyTool, type BuyToolOptions, type BuyToolResult, type BuyToolStatus, type BuyToolLink, type BuyToolProduct, type BuyToolStore, type BuyToolTurn, type BuyToolTurnItem, type BuyToolTurnOptions, type BuyToolTurnMessage, } from './buy-tool.js';
|
|
9
|
-
export { instantAck, ackFor, type AckForOptions } from './ack.js';
|
|
10
|
-
export { forModel, forModelError, stripLinks, type BuyToolModelView, type BuyToolModelCart, type BuyToolModelProduct, type BuyToolModelStore, } from './model-view.js';
|
|
8
|
+
export { buyTool, nextStepPrompt, type BuyTool, type BuyToolOptions, type BuyToolResult, type BuyToolStatus, type BuyToolLink, type BuyToolProduct, type BuyToolProductAction, productAction, type BuyToolStore, type BuyToolTurn, type BuyToolTurnItem, type BuyToolTurnOptions, type BuyToolTurnMessage, type BuyAckMode, } from './buy-tool.js';
|
|
9
|
+
export { instantAck, instantReaction, ackFor, keepsYes, ACK_REACTION, type AckForOptions } from './ack.js';
|
|
10
|
+
export { forModel, forModelError, replyAsWritten, stripLinks, type BuyToolModelView, type BuyToolModelCart, type BuyToolModelProduct, type BuyToolModelStore, } from './model-view.js';
|
|
11
11
|
export { toAnthropicTool, toOpenRouterTool, type AnthropicBuyTool, type OpenRouterToolkit, type ZodLike, } from './adapters.js';
|
|
12
12
|
export { listenToAgentcard, type AgentcardListener, type ListenOptions, type ListenCursorStore, type StreamFetch, } from './listen.js';
|
|
13
|
+
export { ACTION_LINK_ENDINGS, ACTION_LINK_KINDS, actionLinkId, actionLinkOutcome, isActionLink, type ActionLinkKind, type ActionLinkMessage, type ActionLinkOutcome, } from './action-links.js';
|
|
13
14
|
export { AGENTCARD_EVENT_TYPES, type AgentcardEventType, } from './events.js';
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
export { verifyAgentcardWebhook, AgentcardSignatureError, } from './verify.js';
|
|
2
2
|
export { readPurchaseMessage, } from './message.js';
|
|
3
|
-
export { readShopperMessage, isStale, SHOPPER_EVENT_TYPES, } from './shopper-message.js';
|
|
3
|
+
export { readShopperMessage, isStale, SHOPPER_EVENT_TYPES, NEXT_STEP_KINDS, } from './shopper-message.js';
|
|
4
4
|
export { agentcardWebhook, purchaseAgentWebhook, } from './handler.js';
|
|
5
5
|
export { AgentcardClient, AgentcardApiError, BUY_TURN_TIMEOUT_MS, DEFAULT_BASE_URL, } from './client.js';
|
|
6
6
|
export { BUY_TOOL_NAME, BUY_TOOL_DESCRIPTION, buyToolDefinition, parseBuyToolInput, } from './definition.js';
|
|
7
7
|
export { toMessages, messageStream, typingDelay, } from './messages.js';
|
|
8
|
-
export { buyTool, } from './buy-tool.js';
|
|
9
|
-
export { instantAck, ackFor } from './ack.js';
|
|
10
|
-
export { forModel, forModelError, stripLinks, } from './model-view.js';
|
|
8
|
+
export { buyTool, nextStepPrompt, productAction, } from './buy-tool.js';
|
|
9
|
+
export { instantAck, instantReaction, ackFor, keepsYes, ACK_REACTION } from './ack.js';
|
|
10
|
+
export { forModel, forModelError, replyAsWritten, stripLinks, } from './model-view.js';
|
|
11
11
|
export { toAnthropicTool, toOpenRouterTool, } from './adapters.js';
|
|
12
12
|
export { listenToAgentcard, } from './listen.js';
|
|
13
|
+
export { ACTION_LINK_ENDINGS, ACTION_LINK_KINDS, actionLinkId, actionLinkOutcome, isActionLink, } from './action-links.js';
|
|
13
14
|
export { AGENTCARD_EVENT_TYPES, } from './events.js';
|
package/dist/message.d.ts
CHANGED
|
@@ -12,12 +12,20 @@ import { type AgentcardEvent } from './verify.js';
|
|
|
12
12
|
* - `order_unconfirmed`: the store's reply to the order was lost, so the order may exist; Agentcard is confirming it.
|
|
13
13
|
* - `approval_expiring`: the approval link expires soon; `text` carries the link.
|
|
14
14
|
* - `approval_expired`: the approval expired before the shopper answered, and nothing was charged.
|
|
15
|
+
* - `approval_declined`: the shopper declined the approval, and nothing was charged.
|
|
16
|
+
* - `payment_declined`: the shopper approved, and the payment was refused before the order went in; `facts.reason` says why. Nothing was charged.
|
|
15
17
|
* - `card_link_unfinished`: the shopper has not added a card from their card link; `text` carries the link again.
|
|
16
18
|
* - `card_link_expired`: the card link expired before the shopper added a card.
|
|
17
19
|
* - `cart_idle`: a cart sat without being sent for approval; `facts.nudgeNumber` counts the nudges.
|
|
18
20
|
* - `card_added`: the shopper saved a card from the card link your app sent, and can check out with it.
|
|
21
|
+
* - `store_connected`: the shopper signed in to their own account at the store from the sign-in link your app sent, so the order can go through on it.
|
|
22
|
+
* - `store_connect_failed`: the shopper's sign-in to the store did not go through, and the link no longer works.
|
|
23
|
+
* - `store_connect_expired`: the sign-in link expired before the shopper finished signing in.
|
|
24
|
+
* - `cart_ready`: the store's checkout built the cart an answer named as still being built (`cart_building`), at the store's own total. Nothing is paid until the shopper says yes to it.
|
|
25
|
+
* - `cart_needs_input`: that checkout asks the shopper something first, such as a size, a delivery address, a sign-in to the store or an approval of the most the order can cost.
|
|
26
|
+
* - `cart_failed`: that checkout built no cart, and nothing was ordered or charged.
|
|
19
27
|
*/
|
|
20
|
-
export type PurchaseMessageKind = 'order_placed' | 'order_failed' | 'order_not_placed_yet' | 'order_unconfirmed' | 'approval_expiring' | 'approval_expired' | 'card_link_unfinished' | 'card_link_expired' | 'cart_idle' | 'card_added';
|
|
28
|
+
export type PurchaseMessageKind = 'order_placed' | 'order_failed' | 'order_not_placed_yet' | 'order_unconfirmed' | 'approval_expiring' | 'approval_expired' | 'approval_declined' | 'payment_declined' | 'card_link_unfinished' | 'card_link_expired' | 'cart_idle' | 'card_added' | 'store_connected' | 'store_connect_failed' | 'store_connect_expired' | 'cart_ready' | 'cart_needs_input' | 'cart_failed';
|
|
21
29
|
/** The message's values one by one, for a company that writes its own copy. A fact that does not apply is absent. */
|
|
22
30
|
export interface PurchaseFacts {
|
|
23
31
|
merchantName?: string;
|
|
@@ -28,7 +36,7 @@ export interface PurchaseFacts {
|
|
|
28
36
|
cardBrand?: string;
|
|
29
37
|
cardLast4?: string;
|
|
30
38
|
orderId?: string;
|
|
31
|
-
/** Why an order failed or was not placed, in words. */
|
|
39
|
+
/** Why an order failed or was not placed, or a payment was declined, in words. */
|
|
32
40
|
reason?: string;
|
|
33
41
|
approvalUrl?: string;
|
|
34
42
|
/** When the approval link stops working, as an ISO 8601 timestamp. */
|
package/dist/messages.d.ts
CHANGED
|
@@ -16,17 +16,17 @@
|
|
|
16
16
|
export declare function toMessages(text: string): string[];
|
|
17
17
|
/**
|
|
18
18
|
* How long someone texting takes to type `message`, in milliseconds: 300, plus 12 a character, at most
|
|
19
|
-
* 1,500. `messageStream` and `buy.turn` wait this long between two messages
|
|
20
|
-
* writes three paragraphs in 100 ms still delivers them one at a time.
|
|
19
|
+
* 1,500. With `pace: true`, `messageStream` and `buy.turn` wait this long between two messages, so a
|
|
20
|
+
* model that writes three paragraphs in 100 ms still delivers them one at a time.
|
|
21
21
|
*
|
|
22
22
|
* ```ts
|
|
23
23
|
* typingDelay('which one do you want?'); // 564
|
|
24
24
|
* ```
|
|
25
25
|
*/
|
|
26
26
|
export declare function typingDelay(message: string): number;
|
|
27
|
-
/** How messages are spaced: `
|
|
27
|
+
/** How messages are spaced: `false` (the default) not at all, `true` by `typingDelay`, or a function that returns the pause for a message. */
|
|
28
28
|
export type MessagePace = boolean | ((message: string) => number);
|
|
29
|
-
/** The pause for each message `pace` asks for, or null for none. Throws a TypeError for a pace that cannot work. */
|
|
29
|
+
/** The pause for each message `pace` asks for, or null for none, the default. Throws a TypeError for a pace that cannot work. */
|
|
30
30
|
export declare function paceOf(pace: unknown): ((message: string) => number) | null;
|
|
31
31
|
/** A typing hook that never throws: one that fails costs nothing but the indicator. */
|
|
32
32
|
export declare function typingHook(onTyping: unknown): (typing: boolean) => void;
|
|
@@ -49,11 +49,11 @@ export interface MessageStreamOptions {
|
|
|
49
49
|
*/
|
|
50
50
|
onMessage: (message: string) => void | Promise<void>;
|
|
51
51
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
52
|
+
* How the messages are spaced. Default `false`: each message goes out the moment it is written.
|
|
53
|
+
* `true` spaces them like someone texting: each message after the first waits until `typingDelay` of
|
|
54
|
+
* it has passed since the one before it went out, so a model that writes three paragraphs in 100 ms
|
|
55
|
+
* still delivers them one at a time, and time the model spent writing counts toward the pause. A
|
|
56
|
+
* function returns the pause in milliseconds for a message.
|
|
57
57
|
*/
|
|
58
58
|
pace?: MessagePace;
|
|
59
59
|
/**
|
package/dist/messages.js
CHANGED
|
@@ -122,8 +122,8 @@ const TYPING_MS_PER_CHARACTER = 12;
|
|
|
122
122
|
const TYPING_MAX_MS = 1_500;
|
|
123
123
|
/**
|
|
124
124
|
* How long someone texting takes to type `message`, in milliseconds: 300, plus 12 a character, at most
|
|
125
|
-
* 1,500. `messageStream` and `buy.turn` wait this long between two messages
|
|
126
|
-
* writes three paragraphs in 100 ms still delivers them one at a time.
|
|
125
|
+
* 1,500. With `pace: true`, `messageStream` and `buy.turn` wait this long between two messages, so a
|
|
126
|
+
* model that writes three paragraphs in 100 ms still delivers them one at a time.
|
|
127
127
|
*
|
|
128
128
|
* ```ts
|
|
129
129
|
* typingDelay('which one do you want?'); // 564
|
|
@@ -132,12 +132,12 @@ const TYPING_MAX_MS = 1_500;
|
|
|
132
132
|
export function typingDelay(message) {
|
|
133
133
|
return Math.min(TYPING_MAX_MS, TYPING_BASE_MS + TYPING_MS_PER_CHARACTER * [...String(message ?? '')].length);
|
|
134
134
|
}
|
|
135
|
-
/** The pause for each message `pace` asks for, or null for none. Throws a TypeError for a pace that cannot work. */
|
|
135
|
+
/** The pause for each message `pace` asks for, or null for none, the default. Throws a TypeError for a pace that cannot work. */
|
|
136
136
|
export function paceOf(pace) {
|
|
137
|
-
if (pace === undefined || pace ===
|
|
138
|
-
return typingDelay;
|
|
139
|
-
if (pace === false)
|
|
137
|
+
if (pace === undefined || pace === false)
|
|
140
138
|
return null;
|
|
139
|
+
if (pace === true)
|
|
140
|
+
return typingDelay;
|
|
141
141
|
if (typeof pace !== 'function')
|
|
142
142
|
throw new TypeError('Pass pace as true, false, or a function that returns the pause before a message in milliseconds.');
|
|
143
143
|
return (message) => {
|
package/dist/model-view.d.ts
CHANGED
|
@@ -11,10 +11,19 @@ import type { BuyNextAction } from './client.js';
|
|
|
11
11
|
import type { BuyToolProduct, BuyToolResult, BuyToolStatus, BuyToolStore } from './buy-tool.js';
|
|
12
12
|
export interface BuyToolModelCart {
|
|
13
13
|
store: string;
|
|
14
|
+
/**
|
|
15
|
+
* `price` is one unit's; `line_total` is what the line costs with its add-ons and quantity, and the
|
|
16
|
+
* lines' totals add up before `fees`. `options` are the choices made on the line and `variant` the
|
|
17
|
+
* variant it is. `product_id` is the id the line's product carries in `products`, or null.
|
|
18
|
+
*/
|
|
14
19
|
items: Array<{
|
|
20
|
+
product_id: string | null;
|
|
15
21
|
name: string;
|
|
16
22
|
qty: number;
|
|
17
23
|
price: string | null;
|
|
24
|
+
line_total: string | null;
|
|
25
|
+
options: string[];
|
|
26
|
+
variant: string | null;
|
|
18
27
|
}>;
|
|
19
28
|
fees: string | null;
|
|
20
29
|
/** The store's own fee and tax lines inside `fees`, by the store's labels. Empty when it itemized none. */
|
|
@@ -26,6 +35,8 @@ export interface BuyToolModelCart {
|
|
|
26
35
|
total: string;
|
|
27
36
|
/** True when the store has not priced its fees and tax yet: `total` is an estimate, never the final amount. */
|
|
28
37
|
total_is_estimate: boolean;
|
|
38
|
+
/** What an estimated `total` leaves out, in words the model can repeat; null when the store priced the cart. */
|
|
39
|
+
total_leaves_out: string | null;
|
|
29
40
|
/** The most the shopper's card can be held for this cart: above `total` at a store that prices its final at checkout, `total` itself for a flight, which holds exactly that; else null. */
|
|
30
41
|
max_charge: string | null;
|
|
31
42
|
}
|
|
@@ -53,6 +64,13 @@ export interface BuyToolModelView {
|
|
|
53
64
|
status: BuyToolStatus;
|
|
54
65
|
/** The one thing to ask the shopper for in this message, or null when nothing waits on them. Never ask for a later step. */
|
|
55
66
|
next_action: BuyNextAction | null;
|
|
67
|
+
/**
|
|
68
|
+
* The store whose checkout is still building the cart this answer named, absent when none is: its cart and total reach the
|
|
69
|
+
* shopper in a message from Agentcard, and only the shopper's yes to that total confirms it.
|
|
70
|
+
*/
|
|
71
|
+
cart_building?: {
|
|
72
|
+
store: string;
|
|
73
|
+
};
|
|
56
74
|
/** Agentcard's answer for the shopper, with every link taken out. */
|
|
57
75
|
reply: string;
|
|
58
76
|
/** The newest cart on the table. */
|
|
@@ -61,11 +79,24 @@ export interface BuyToolModelView {
|
|
|
61
79
|
carts: BuyToolModelCart[];
|
|
62
80
|
products: BuyToolModelProduct[];
|
|
63
81
|
approval_expires_at: string | null;
|
|
82
|
+
/**
|
|
83
|
+
* How long the newest link this answer sent (Vault, approval or store login) still works, in words the
|
|
84
|
+
* shopper reads, such as `about 24 hours` or `about 15 minutes`; `no longer` once it ran out; null when
|
|
85
|
+
* Agentcard named no expiry. Read when the answer came back, so the model never reckons it from a timestamp.
|
|
86
|
+
*/
|
|
87
|
+
link_expires_in: string | null;
|
|
88
|
+
/** True when the shopper need not reply to this answer, as when Agentcard places the order on its own. False beside a step `next_action` names, whose ask is one short line. */
|
|
64
89
|
shopper_need_not_reply: boolean;
|
|
65
90
|
/** True when the shopper already got a message this turn saying their request is being worked on, so the reply opens with no acknowledgement of its own. */
|
|
66
91
|
shopper_already_told_looking: boolean;
|
|
67
92
|
/** True when the shopper already has the link this answer needed. */
|
|
68
93
|
link_sent: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Which links went out with this answer: `store_login` is the store's own login page (the shopper
|
|
96
|
+
* logs in to their account there), `vault` is the page where they add a card, `approval` the page
|
|
97
|
+
* where they approve the payment. Name the link by what it is: a store login is never a card link.
|
|
98
|
+
*/
|
|
99
|
+
links_sent: Array<'vault' | 'approval' | 'store_login'>;
|
|
69
100
|
/** How many of the products above the shopper holds as cards, each with its name, price and picture; 0 when none went out. */
|
|
70
101
|
product_links_sent: number;
|
|
71
102
|
/** The product this call's `product_id` picked, and whether the cart now holds it; null when the call picked none. */
|
|
@@ -93,6 +124,15 @@ export declare function stripLinks(text: string): string;
|
|
|
93
124
|
* decimal point, so 1331 is $13.31 in USD, CA$13.31 in CAD and ¥1,331 in JPY. An amount whose currency
|
|
94
125
|
* Agentcard did not name shows with no symbol, because a dollar sign on it could be wrong.
|
|
95
126
|
*/
|
|
127
|
+
/**
|
|
128
|
+
* A product's price as the shopper reads it: `$12.99`, or `from $10.69` for a product priced by a
|
|
129
|
+
* required choice (a size, a patty count), whose base price is often $0. Null when Agentcard named none.
|
|
130
|
+
*/
|
|
131
|
+
export declare function productPrice(product: {
|
|
132
|
+
priceCents: number | null;
|
|
133
|
+
currency: string | null;
|
|
134
|
+
priceFrom?: true;
|
|
135
|
+
}): string | null;
|
|
96
136
|
export declare function money(minor: number | null | undefined, currency: string | null | undefined): string | null;
|
|
97
137
|
/**
|
|
98
138
|
* The products the model reads: the first twenty that keep a name and a store
|
|
@@ -123,6 +163,8 @@ export declare function cardKey(product: Pick<BuyToolProduct, 'id' | 'ref' | 'ur
|
|
|
123
163
|
* taps the flight they want (2026-10-20: three flights listed in text, and no card to tap).
|
|
124
164
|
*/
|
|
125
165
|
export declare function productCardKey(product: Pick<BuyToolProduct, 'id' | 'ref' | 'url' | 'flight'>): string | null;
|
|
166
|
+
/** How long until `iso`, from `now`, as the shopper reads it: `about 24 hours`, `about 15 minutes`, `no longer`, or null. */
|
|
167
|
+
export declare function expiresIn(iso: string | null | undefined, now?: number): string | null;
|
|
126
168
|
/**
|
|
127
169
|
* The model's view of one answer: the status, Agentcard's reply, the newest
|
|
128
170
|
* cart and every cart on the table, the products by id, name and price, and
|
|
@@ -130,6 +172,19 @@ export declare function productCardKey(product: Pick<BuyToolProduct, 'id' | 'ref
|
|
|
130
172
|
* reads as `needs_input`.
|
|
131
173
|
*/
|
|
132
174
|
export declare function forModel(result: BuyToolResult): BuyToolModelView;
|
|
175
|
+
/**
|
|
176
|
+
* Agentcard's reply, ready for the shopper as written, when the answer only asks them something: a
|
|
177
|
+
* `needs_input` answer with a reply, such as a product list, a size or a store to pick, or a cart's
|
|
178
|
+
* total and the ask for their yes. The reply comes with every link taken out, as the model reads it.
|
|
179
|
+
* Null for any other answer, which your model puts in its own words, and for a line the tool wrote
|
|
180
|
+
* itself for your model, such as the note that the cart changed or a `nextStep` turn's ask for the
|
|
181
|
+
* shopper's yes: only the reply Agentcard sent, as `envelope` holds it, is written for the shopper.
|
|
182
|
+
*
|
|
183
|
+
* ```ts
|
|
184
|
+
* replyAsWritten(result); // 'found two at Amazon. want the first one?'
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
export declare function replyAsWritten(result: BuyToolResult): string | null;
|
|
133
188
|
/**
|
|
134
189
|
* A thrown error as the model may read it: the same message with every link
|
|
135
190
|
* taken out, and the original error on `cause` for your logs. A TypeError
|