@agent-cards/commerce-sdk 0.14.0 → 0.16.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 +53 -5
- package/dist/ack.d.ts +26 -16
- package/dist/ack.js +64 -2
- package/dist/browser.d.ts +1 -0
- package/dist/browser.js +2 -0
- package/dist/buy-tool.d.ts +73 -8
- package/dist/buy-tool.js +304 -32
- package/dist/client.d.ts +58 -0
- package/dist/client.js +43 -2
- package/dist/definition.js +2 -0
- package/dist/direct.d.ts +5 -2
- package/dist/index.d.ts +5 -3
- package/dist/index.js +3 -1
- package/dist/messages.js +4 -4
- package/dist/model-view.d.ts +59 -7
- package/dist/model-view.js +231 -31
- package/dist/order.d.ts +119 -0
- package/dist/order.js +155 -0
- package/dist/sentences.d.ts +3 -0
- package/dist/sentences.js +29 -0
- package/dist/shopper-message.d.ts +10 -0
- package/dist/shopper-message.js +51 -11
- package/dist/texting.d.ts +81 -0
- package/dist/texting.js +347 -0
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -51,6 +51,30 @@ export interface BuyCatalogUpdate {
|
|
|
51
51
|
/** The products found past the cards, in the order they were found, to show on "show more" with no new search. */
|
|
52
52
|
more: BuyCatalog['items'];
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Whether a place a card shows has room at the time the shopper asked for: `checking` while Agentcard's live search runs,
|
|
56
|
+
* `available` once it found the place open, with the open times it named in `slots` (empty when it named none),
|
|
57
|
+
* `unavailable` when it found the place full then, and `unknown` when the search ended without saying either way. Only
|
|
58
|
+
* `available` is a place to offer as a choice. A product with no such question, such as a retail product, carries none.
|
|
59
|
+
*/
|
|
60
|
+
export type BuyAvailability = {
|
|
61
|
+
status: 'checking';
|
|
62
|
+
} | {
|
|
63
|
+
status: 'available';
|
|
64
|
+
slots: string[];
|
|
65
|
+
} | {
|
|
66
|
+
status: 'unavailable';
|
|
67
|
+
} | {
|
|
68
|
+
status: 'unknown';
|
|
69
|
+
};
|
|
70
|
+
/** What Agentcard's live search said of the places a running turn's cards show, matched to each card by its catalog `id`. */
|
|
71
|
+
export interface BuyAvailabilityUpdate {
|
|
72
|
+
conversationId: string;
|
|
73
|
+
items: Array<{
|
|
74
|
+
id: string;
|
|
75
|
+
availability: BuyAvailability;
|
|
76
|
+
}>;
|
|
77
|
+
}
|
|
54
78
|
/** How many products a running turn found so far, and how many of them it shows: "8 of 23 shown". */
|
|
55
79
|
export interface BuyFoundUpdate {
|
|
56
80
|
conversationId: string;
|
|
@@ -107,6 +131,8 @@ export interface BuyStreamHandlers {
|
|
|
107
131
|
onCart?: (update: BuyCartUpdate) => void | Promise<void>;
|
|
108
132
|
/** Each piece of a message as Agentcard writes it. Passing it asks Agentcard for the pieces (`?stream=deltas`). */
|
|
109
133
|
onDelta?: (update: BuyDeltaUpdate) => void | Promise<void>;
|
|
134
|
+
/** Whether the places the turn's cards show have room, once Agentcard's live search says, after the cards it changes. */
|
|
135
|
+
onAvailability?: (update: BuyAvailabilityUpdate) => void | Promise<void>;
|
|
110
136
|
}
|
|
111
137
|
export declare const DEFAULT_BASE_URL = "https://api.agentcard.sh";
|
|
112
138
|
/** The wait for one purchase turn: past Agentcard's own 110-second budget, so a slow turn answers `turn_timeout` rather than aborting here. */
|
|
@@ -301,6 +327,8 @@ export interface BuyCatalogItem {
|
|
|
301
327
|
store: BuyStore | null;
|
|
302
328
|
/** The product's page at its store, as an http or https address, or null when Agentcard sent none. */
|
|
303
329
|
url: string | null;
|
|
330
|
+
/** Whether a place has room at the asked time (BuyAvailability). Absent on a product with no such question. */
|
|
331
|
+
availability?: BuyAvailability;
|
|
304
332
|
}
|
|
305
333
|
/**
|
|
306
334
|
* The products the last search found. `presented` is true when this answer's reply presents them: the
|
|
@@ -345,6 +373,32 @@ export interface BuyStorePick {
|
|
|
345
373
|
name: string | null;
|
|
346
374
|
selected: boolean;
|
|
347
375
|
}
|
|
376
|
+
/** One value the shopper picks from. `id` is the option id the cart takes, null when the shopper's words answer it. */
|
|
377
|
+
export interface BuyChoiceOption {
|
|
378
|
+
id: string | null;
|
|
379
|
+
label: string;
|
|
380
|
+
/** What choosing it adds to the price, in cents: 0 when free, null when the store does not say. */
|
|
381
|
+
price_delta_cents: number | null;
|
|
382
|
+
}
|
|
383
|
+
/** One group of values, such as a sandwich's bread or a shirt's size. `min` and `max` count the values picked; `max` null for no limit. */
|
|
384
|
+
export interface BuyChoiceGroup {
|
|
385
|
+
name: string;
|
|
386
|
+
required: boolean;
|
|
387
|
+
min: number;
|
|
388
|
+
max: number | null;
|
|
389
|
+
options: BuyChoiceOption[];
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* The choice an answer's reply asks the shopper to make, as data: the question, the item it is about, where the values come
|
|
393
|
+
* from (`menu_item`: a restaurant item's options, `store_checkout`: a store's own size or colour question, `catalog_variant`:
|
|
394
|
+
* a product's sizes or colours in stock), and every group with its values.
|
|
395
|
+
*/
|
|
396
|
+
export interface BuyChoice {
|
|
397
|
+
question: string | null;
|
|
398
|
+
item: string | null;
|
|
399
|
+
source: string;
|
|
400
|
+
groups: BuyChoiceGroup[];
|
|
401
|
+
}
|
|
348
402
|
export interface BuyPaymentSource {
|
|
349
403
|
source: string;
|
|
350
404
|
brand: string | null;
|
|
@@ -407,6 +461,8 @@ export interface BuyTurn {
|
|
|
407
461
|
store_pick: BuyStorePick | null;
|
|
408
462
|
/** The cart the store's checkout is still building after this answer; null when none is, and on an older API. */
|
|
409
463
|
cart_building: BuyCartBuilding | null;
|
|
464
|
+
/** The choice the reply asks the shopper to make, each group with every value; null when it asks none, and on an older API. */
|
|
465
|
+
choice: BuyChoice | null;
|
|
410
466
|
/** The answer as Agentcard sent it. */
|
|
411
467
|
raw: Record<string, unknown>;
|
|
412
468
|
}
|
|
@@ -478,6 +534,8 @@ export declare class AgentcardApiError extends Error {
|
|
|
478
534
|
}
|
|
479
535
|
/** A turn as Agentcard answers it. */
|
|
480
536
|
export declare function turnOf(body: Record<string, unknown>): BuyTurn;
|
|
537
|
+
/** An answer's `choice`, its groups with at least one value each, or null when it carries none. */
|
|
538
|
+
export declare function choiceOf(value: unknown): BuyChoice | null;
|
|
481
539
|
/** A cart being built, as an answer, a refusal or the conversation read names it. */
|
|
482
540
|
export declare function cartBuildingOf(value: unknown): BuyCartBuilding | null;
|
|
483
541
|
/** The client every shopper's buy tool shares. Build one per set of credentials and keep it. */
|
package/dist/client.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { isRecord } from './verify.js';
|
|
12
12
|
import { listenToAgentcard } from './listen.js';
|
|
13
13
|
/** The handlers that ask Agentcard to stream a turn. */
|
|
14
|
-
const STREAM_HANDLERS = ['onProgress', 'onStatus', 'onFound', 'onCatalog', 'onSegment', 'onCart', 'onDelta'];
|
|
14
|
+
const STREAM_HANDLERS = ['onProgress', 'onStatus', 'onFound', 'onCatalog', 'onSegment', 'onCart', 'onDelta', 'onAvailability'];
|
|
15
15
|
const PROGRESS_STAGES = new Set(['searching', 'building_cart', 'checking_out', 'working']);
|
|
16
16
|
export const DEFAULT_BASE_URL = 'https://api.agentcard.sh';
|
|
17
17
|
/** The wait for one purchase turn: past Agentcard's own 110-second budget, so a slow turn answers `turn_timeout` rather than aborting here. */
|
|
@@ -223,9 +223,30 @@ function catalogItemsOf(v) {
|
|
|
223
223
|
...(flight ? { flight } : {}),
|
|
224
224
|
store: storeOf(i.store),
|
|
225
225
|
url: pageUrl(i.url),
|
|
226
|
+
...availabilityField(i),
|
|
226
227
|
};
|
|
227
228
|
}) : [];
|
|
228
229
|
}
|
|
230
|
+
/**
|
|
231
|
+
* A place's availability as Agentcard marks it: `checking`, `open` or `available` with the times in `slots`, `none` or
|
|
232
|
+
* `unavailable`, and `unknown`. A mark this version does not know reads as `unknown`, so no place is offered on a guess.
|
|
233
|
+
*/
|
|
234
|
+
function availabilityOf(v) {
|
|
235
|
+
const mark = v.availability;
|
|
236
|
+
if (mark === 'checking')
|
|
237
|
+
return { status: 'checking' };
|
|
238
|
+
if (mark === 'open' || mark === 'available') {
|
|
239
|
+
const slots = Array.isArray(v.slots) ? v.slots.filter((s) => typeof s === 'string' && s.trim() !== '') : [];
|
|
240
|
+
return { status: 'available', slots };
|
|
241
|
+
}
|
|
242
|
+
if (mark === 'none' || mark === 'unavailable')
|
|
243
|
+
return { status: 'unavailable' };
|
|
244
|
+
return typeof mark === 'string' ? { status: 'unknown' } : null;
|
|
245
|
+
}
|
|
246
|
+
function availabilityField(v) {
|
|
247
|
+
const availability = availabilityOf(v);
|
|
248
|
+
return availability ? { availability } : {};
|
|
249
|
+
}
|
|
229
250
|
/** A count Agentcard sent: a whole number, never negative. Null otherwise. */
|
|
230
251
|
function countOf(v) {
|
|
231
252
|
return typeof v === 'number' && Number.isInteger(v) && v >= 0 ? v : null;
|
|
@@ -278,9 +299,20 @@ export function turnOf(body) {
|
|
|
278
299
|
stores: storesOf(body.stores),
|
|
279
300
|
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,
|
|
280
301
|
cart_building: cartBuildingOf(body.cart_building),
|
|
302
|
+
choice: choiceOf(body.choice),
|
|
281
303
|
raw: body,
|
|
282
304
|
};
|
|
283
305
|
}
|
|
306
|
+
/** An answer's `choice`, its groups with at least one value each, or null when it carries none. */
|
|
307
|
+
export function choiceOf(value) {
|
|
308
|
+
if (!isRecord(value) || !Array.isArray(value.groups))
|
|
309
|
+
return null;
|
|
310
|
+
const groups = value.groups.filter(isRecord).flatMap((g) => {
|
|
311
|
+
const options = (Array.isArray(g.options) ? g.options : []).filter(isRecord).flatMap((o) => str(o.label) ? [{ id: str(o.id), label: o.label.trim(), price_delta_cents: num(o.price_delta_cents) }] : []);
|
|
312
|
+
return options.length ? [{ name: str(g.name) ?? '', required: g.required === true, min: num(g.min) ?? 0, max: num(g.max), options }] : [];
|
|
313
|
+
});
|
|
314
|
+
return groups.length ? { question: str(value.question), item: str(value.item), source: str(value.source) ?? '', groups } : null;
|
|
315
|
+
}
|
|
284
316
|
/** A cart being built, as an answer, a refusal or the conversation read names it. */
|
|
285
317
|
export function cartBuildingOf(value) {
|
|
286
318
|
return isRecord(value) && str(value.store) ? { store: value.store, started_at: str(value.started_at) ?? '' } : null;
|
|
@@ -424,7 +456,7 @@ async function eachEvent(res, onEvent) {
|
|
|
424
456
|
}
|
|
425
457
|
/**
|
|
426
458
|
* A streamed purchase turn, read to its `envelope`: the same answer a JSON turn returns, with its
|
|
427
|
-
* status in `http_status`. Each `progress`, `status`, `delta`, `catalog`, `find`, `segment` and `
|
|
459
|
+
* status in `http_status`. Each `progress`, `status`, `delta`, `catalog`, `find`, `segment`, `cart` and `venue_availability` goes to its handler
|
|
428
460
|
* on the way, and any other event, such as a `ping` or a `tool`, is read past. A stream that breaks or
|
|
429
461
|
* ends before its envelope, once it named the conversation, reads as a turn past its budget
|
|
430
462
|
* (`turn_timeout`), so the tool reads the conversation for what the turn left instead of losing it.
|
|
@@ -503,6 +535,15 @@ async function readStreamedTurn(res, stream, timeoutMs) {
|
|
|
503
535
|
else if (event === 'segment' && typeof data.text === 'string' && data.text.trim() !== '' && stream.onSegment) {
|
|
504
536
|
deliver(() => stream.onSegment({ conversationId: named, text: data.text }));
|
|
505
537
|
}
|
|
538
|
+
else if (event === 'venue_availability' && stream.onAvailability && str(data.conversation_id) && Array.isArray(data.items)) {
|
|
539
|
+
const items = data.items.filter(isRecord).flatMap((i) => {
|
|
540
|
+
const id = str(i.id);
|
|
541
|
+
const availability = availabilityOf(i);
|
|
542
|
+
return id && availability ? [{ id, availability }] : [];
|
|
543
|
+
});
|
|
544
|
+
if (items.length)
|
|
545
|
+
deliver(() => stream.onAvailability({ conversationId: data.conversation_id, items }));
|
|
546
|
+
}
|
|
506
547
|
else if (event === 'cart' && stream.onCart && str(data.conversation_id)) {
|
|
507
548
|
const update = { conversationId: data.conversation_id, cart: cartOf(data.cart), carts: cartsOf(data.carts) };
|
|
508
549
|
deliver(() => stream.onCart(update));
|
package/dist/definition.js
CHANGED
|
@@ -42,6 +42,7 @@ export const BUY_TOOL_DESCRIPTION = [
|
|
|
42
42
|
"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.",
|
|
43
43
|
'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?".',
|
|
44
44
|
'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.',
|
|
45
|
+
'When `yes_carried` is set, the shopper already said yes to this cart, or picked it (`yes_carried.picked`), and Agentcard went ahead on it once they finished the step outside the chat, so never ask for their yes again: ask only for the step `next_action` names. When `yes_carried.total_changed` is true, that one short line opens with the new total, the one price it names beside `max_charge`, such as "it\'s $17.89 with fees and tax now. approve the payment to place your order (up to $19.02)".',
|
|
45
46
|
'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.',
|
|
46
47
|
'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".',
|
|
47
48
|
'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.',
|
|
@@ -52,6 +53,7 @@ export const BUY_TOOL_DESCRIPTION = [
|
|
|
52
53
|
"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.",
|
|
53
54
|
'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.',
|
|
54
55
|
'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".',
|
|
56
|
+
'When you list options or choices for the shopper, such as sizes, breads, times, or stores no card shows, write one per line, each starting with "• ", in the same paragraph as the line that asks which one, so the question and its choices reach the shopper as one message. When the result has `choice`, list every value it holds that way, with its extra price, such as "• Gluten-free bread (+$2.46)", even when `reply` names them inside a sentence. The products and stores the cards show are still never listed again.',
|
|
55
57
|
'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.',
|
|
56
58
|
'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".',
|
|
57
59
|
'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.',
|
package/dist/direct.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { BuyNextAction } from './client.js';
|
|
2
2
|
import type { BuyToolInput } from './definition.js';
|
|
3
|
-
/**
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Why a message goes straight to Agentcard. `next_step` is a turn `buy.nextStep` opened: its call reads where the
|
|
5
|
+
* purchase stands and goes on from there whatever the model would write, so it never needs the model to decide it.
|
|
6
|
+
*/
|
|
7
|
+
export type DirectReason = 'product_ask' | 'pick' | 'answer' | 'confirm' | 'next_step';
|
|
5
8
|
/** The buy call for a message that goes straight to Agentcard, and why it does. */
|
|
6
9
|
export interface DirectCall {
|
|
7
10
|
input: BuyToolInput;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
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
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
|
+
export { ORDER_STEPS, updateOrder, arrivalText, type ShopperOrder, type OrderStep, type OrderPathStep, type OrderStepState, type ArrivalTextOptions, } from './order.js';
|
|
4
5
|
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 AgentcardTraceOptions, type OtelApi, type TraceContext, type FetchLike, type FetchLikeResponse, type BuyProgress, type BuyProgressStage, type BuyCatalogUpdate, type BuyFoundUpdate, type BuyTurnStatus, type BuySegmentUpdate, type BuyCartUpdate, type BuyDeltaUpdate, 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
|
+
export { AgentcardClient, AgentcardApiError, BUY_TURN_TIMEOUT_MS, DEFAULT_BASE_URL, type AgentcardClientOptions, type AgentcardTraceOptions, type OtelApi, type TraceContext, type FetchLike, type FetchLikeResponse, type BuyProgress, type BuyProgressStage, type BuyCatalogUpdate, type BuyAvailability, type BuyAvailabilityUpdate, type BuyFoundUpdate, type BuyTurnStatus, type BuySegmentUpdate, type BuyCartUpdate, type BuyDeltaUpdate, 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, type BuyChoice, type BuyChoiceGroup, type BuyChoiceOption, } from './client.js';
|
|
6
7
|
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
8
|
export { toMessages, messageStream, typingDelay, type MessagePace, type MessageStream, type MessageStreamOptions, } from './messages.js';
|
|
8
|
-
export { buyTool, nextStepPrompt, type BuyTool, type BuyToolOptions, type BuyToolResult, type BuyToolStatus, type BuyToolLink, type BuyToolFound, type BuyToolProduct, type BuyToolProductAction, productAction, type BuyToolStore, type BuyToolTurn, type BuyToolTurnItem, type BuyToolTurnOptions, type BuyToolTurnMessage, type BuyAckMode, } from './buy-tool.js';
|
|
9
|
+
export { buyTool, nextStepPrompt, type BuyTool, type BuyToolOptions, type BuyToolResult, type BuyToolStatus, type BuyToolLink, type BuyToolFound, type BuyToolAvailability, type BuyToolProduct, type BuyToolProductAction, productAction, type BuyToolStore, type BuyToolTurn, type BuyToolTurnItem, type BuyToolTurnOptions, type BuyToolTurnMessage, type BuyAckMode, } from './buy-tool.js';
|
|
9
10
|
export { instantAck, instantReaction, ackFor, keepsYes, ACK_REACTION, type AckForOptions } from './ack.js';
|
|
10
11
|
export { directCall, isProductAsk, type DirectCall, type DirectContext, type DirectReason } from './direct.js';
|
|
11
|
-
export { forModel, forModelError,
|
|
12
|
+
export { forModel, forModelError, stepAsWritten, withoutCardList, stripLinks, type BuyToolModelView, type BuyToolModelCart, type BuyToolModelProduct, type BuyToolModelStore, type ShownCard, } from './model-view.js';
|
|
13
|
+
export { asBullets, choiceLines, replyAsWritten, textingStyle, withChoice, type ReplyAsWrittenOptions } from './texting.js';
|
|
12
14
|
export { toAnthropicTool, toOpenRouterTool, type AnthropicBuyTool, type OpenRouterToolkit, type ZodLike, } from './adapters.js';
|
|
13
15
|
export { listenToAgentcard, type AgentcardListener, type ListenOptions, type ListenCursorStore, type StreamFetch, } from './listen.js';
|
|
14
16
|
export { ACTION_LINK_ENDINGS, ACTION_LINK_KINDS, actionLinkId, actionLinkOutcome, isActionLink, type ActionLinkKind, type ActionLinkMessage, type ActionLinkOutcome, } from './action-links.js';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { verifyAgentcardWebhook, AgentcardSignatureError, } from './verify.js';
|
|
2
2
|
export { readPurchaseMessage, } from './message.js';
|
|
3
3
|
export { readShopperMessage, isStale, SHOPPER_EVENT_TYPES, NEXT_STEP_KINDS, } from './shopper-message.js';
|
|
4
|
+
export { ORDER_STEPS, updateOrder, arrivalText, } from './order.js';
|
|
4
5
|
export { agentcardWebhook, purchaseAgentWebhook, } from './handler.js';
|
|
5
6
|
export { AgentcardClient, AgentcardApiError, BUY_TURN_TIMEOUT_MS, DEFAULT_BASE_URL, } from './client.js';
|
|
6
7
|
export { BUY_TOOL_NAME, BUY_TOOL_DESCRIPTION, buyToolDefinition, parseBuyToolInput, } from './definition.js';
|
|
@@ -8,7 +9,8 @@ export { toMessages, messageStream, typingDelay, } from './messages.js';
|
|
|
8
9
|
export { buyTool, nextStepPrompt, productAction, } from './buy-tool.js';
|
|
9
10
|
export { instantAck, instantReaction, ackFor, keepsYes, ACK_REACTION } from './ack.js';
|
|
10
11
|
export { directCall, isProductAsk } from './direct.js';
|
|
11
|
-
export { forModel, forModelError,
|
|
12
|
+
export { forModel, forModelError, stepAsWritten, withoutCardList, stripLinks, } from './model-view.js';
|
|
13
|
+
export { asBullets, choiceLines, replyAsWritten, textingStyle, withChoice } from './texting.js';
|
|
12
14
|
export { toAnthropicTool, toOpenRouterTool, } from './adapters.js';
|
|
13
15
|
export { listenToAgentcard, } from './listen.js';
|
|
14
16
|
export { ACTION_LINK_ENDINGS, ACTION_LINK_KINDS, actionLinkId, actionLinkOutcome, isActionLink, } from './action-links.js';
|
package/dist/messages.js
CHANGED
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
*/
|
|
7
7
|
/** A line holding nothing but spaces or tabs. */
|
|
8
8
|
const BLANK = /^[ \t]*$/;
|
|
9
|
-
/** A list item: `-`,
|
|
10
|
-
const LIST_ITEM = /^ {0,3}(?:[
|
|
9
|
+
/** A list item: `-`, `*`, `+` or the bullet `•`, or a number followed by `.` or `)`, then a space or the line's end. */
|
|
10
|
+
const LIST_ITEM = /^ {0,3}(?:[-*+•]|\d{1,9}[.)])(?:[ \t]|$)/;
|
|
11
11
|
/** A list item whose marker a space already follows, the only kind a line still being written is sure to stay. */
|
|
12
|
-
const LIST_ITEM_SPACED = /^ {0,3}(?:[
|
|
12
|
+
const LIST_ITEM_SPACED = /^ {0,3}(?:[-*+•]|\d{1,9}[.)])[ \t]/;
|
|
13
13
|
/** A line indented under a list item: two spaces or a tab, then text. */
|
|
14
14
|
const INDENTED = /^(?: {2,}|\t)\S/;
|
|
15
15
|
/** The start of a line that can still grow into a list item or an indented line once more text arrives. */
|
|
16
|
-
const MAYBE_ITEM = /^ {0,3}(?:[
|
|
16
|
+
const MAYBE_ITEM = /^ {0,3}(?:[-*+•]|\d{1,9}[.)]?)?$|^[ \t]*$/;
|
|
17
17
|
/** A code fence: three or more backticks or tildes, indented by at most three spaces. */
|
|
18
18
|
const FENCE = /^ {0,3}(`{3,}|~{3,})/;
|
|
19
19
|
/** How the line after a blank line inside a list reads: it continues the list, it ends it, or it is too short to tell. */
|
package/dist/model-view.d.ts
CHANGED
|
@@ -107,6 +107,18 @@ export interface BuyToolModelView {
|
|
|
107
107
|
product_id: string | null;
|
|
108
108
|
in_cart: boolean;
|
|
109
109
|
} | null;
|
|
110
|
+
/**
|
|
111
|
+
* The choice the reply asks the shopper to make, absent when it asks none: each group's name and its values with their
|
|
112
|
+
* extra price, such as `Gluten-free bread (+$2.46)`. List them one per line, each starting with `• `, under the question.
|
|
113
|
+
*/
|
|
114
|
+
choice?: {
|
|
115
|
+
question: string | null;
|
|
116
|
+
groups: Array<{
|
|
117
|
+
name: string;
|
|
118
|
+
required: boolean;
|
|
119
|
+
options: string[];
|
|
120
|
+
}>;
|
|
121
|
+
};
|
|
110
122
|
/** The stores the reply presents to choose from, in the order it names them. */
|
|
111
123
|
stores: BuyToolModelStore[];
|
|
112
124
|
/** How many store pages of the stores above the shopper holds, 0 when none went out. */
|
|
@@ -116,12 +128,30 @@ export interface BuyToolModelView {
|
|
|
116
128
|
store_id: string;
|
|
117
129
|
selected: boolean;
|
|
118
130
|
} | null;
|
|
131
|
+
/**
|
|
132
|
+
* Present when this answer went ahead on the yes the shopper already gave in the chat, after a step they finished
|
|
133
|
+
* outside it, or on their pick of what is in the cart (`picked`): the total they said yes to, or the chat last showed
|
|
134
|
+
* them, the cart's total now, and whether it changed. Never ask for their yes again.
|
|
135
|
+
*/
|
|
136
|
+
yes_carried?: {
|
|
137
|
+
said_yes_to: string;
|
|
138
|
+
total: string;
|
|
139
|
+
total_changed: boolean;
|
|
140
|
+
picked?: true;
|
|
141
|
+
};
|
|
119
142
|
}
|
|
120
143
|
/**
|
|
121
144
|
* The text with every link taken out: a Markdown link keeps its label, an address with a path or a scheme goes
|
|
122
145
|
* entirely, and a bare address reads as the site's name (siteName), so the store a reply names stays in it.
|
|
123
146
|
*/
|
|
124
147
|
export declare function stripLinks(text: string): string;
|
|
148
|
+
/**
|
|
149
|
+
* Agentcard's reply as the shopper and the model read it, with every link taken out (stripLinks) and no line left pointing at
|
|
150
|
+
* one. A line that pointed at a link ends as a sentence when the answer sent the shopper what it pointed at, a product or a
|
|
151
|
+
* store card or a step's link, and goes when it sent none, so the reply never promises what the shopper does not get. 2026-10-27, chat.opencommerce.sh:
|
|
152
|
+
* "here's the stool on Wayfair:" reached the shopper with nothing after it.
|
|
153
|
+
*/
|
|
154
|
+
export declare function replyWithoutLinks(reply: string, sent: boolean): string;
|
|
125
155
|
/**
|
|
126
156
|
* An amount in its currency's minor unit, as the model reads it: the currency's own minor units place the
|
|
127
157
|
* decimal point, so 1331 is $13.31 in USD, CA$13.31 in CAD and ¥1,331 in JPY. An amount whose currency
|
|
@@ -162,10 +192,11 @@ export declare function pickId(product: Pick<BuyToolProduct, 'id' | 'ref'>): str
|
|
|
162
192
|
export declare function cardKey(product: Pick<BuyToolProduct, 'id' | 'ref' | 'url'>): string | null;
|
|
163
193
|
/**
|
|
164
194
|
* The key of the card a product gets, or null when it gets none: a product with a page at its store,
|
|
165
|
-
* or a flight option, which
|
|
166
|
-
* taps the flight they want (2026-10-20: three flights listed in text, and no card to tap)
|
|
195
|
+
* or a flight option or a place to book, which can have no page, with an id a pick takes. A flight's card
|
|
196
|
+
* is how the shopper taps the flight they want (2026-10-20: three flights listed in text, and no card to tap),
|
|
197
|
+
* and a place's card is where the shopper reads whether it has room.
|
|
167
198
|
*/
|
|
168
|
-
export declare function productCardKey(product: Pick<BuyToolProduct, 'id' | 'ref' | 'url' | 'flight'>): string | null;
|
|
199
|
+
export declare function productCardKey(product: Pick<BuyToolProduct, 'id' | 'ref' | 'url' | 'flight' | 'availability'>): string | null;
|
|
169
200
|
/** How long until `iso`, from `now`, as the shopper reads it: `about 24 hours`, `about 15 minutes`, `no longer`, or null. */
|
|
170
201
|
export declare function expiresIn(iso: string | null | undefined, now?: number): string | null;
|
|
171
202
|
/**
|
|
@@ -175,6 +206,16 @@ export declare function expiresIn(iso: string | null | undefined, now?: number):
|
|
|
175
206
|
* reads as `needs_input`.
|
|
176
207
|
*/
|
|
177
208
|
export declare function forModel(result: BuyToolResult): BuyToolModelView;
|
|
209
|
+
/**
|
|
210
|
+
* The fixed short line for an answer whose next step is a link the shopper already holds: `add your card to place the
|
|
211
|
+
* order`, `sign in to DoorDash to place your order`, or `approve the payment to place your order (up to $19.02)`, with
|
|
212
|
+
* the most the shopper can be charged in the parentheses when the cart names it. After the shopper's own words put the
|
|
213
|
+
* cart together, a line about the cart comes first, as its own message. After a yes the turn carried past a step outside
|
|
214
|
+
* the chat, a total that changed opens the ask: `it's $17.89 with fees and tax now. approve the payment ...`. Null for any
|
|
215
|
+
* other answer, when the link did not go out, when the shopper already read Agentcard's own ask through `onText`, when a
|
|
216
|
+
* card or approval step has no cart to place, and when the answer also showed products.
|
|
217
|
+
*/
|
|
218
|
+
export declare function stepAsWritten(result: BuyToolResult): string | null;
|
|
178
219
|
/**
|
|
179
220
|
* Agentcard's reply, ready for the shopper as written, when the answer only asks them something: a
|
|
180
221
|
* `needs_input` answer with a reply, such as a product list, a size or a store to pick, or a cart's
|
|
@@ -183,25 +224,36 @@ export declare function forModel(result: BuyToolResult): BuyToolModelView;
|
|
|
183
224
|
* itself for your model, such as the note that the cart changed or a `nextStep` turn's ask for the
|
|
184
225
|
* shopper's yes: only the reply Agentcard sent, as `envelope` holds it, is written for the shopper.
|
|
185
226
|
* Null as well once the shopper read the reply through `onText` (`shopper_already_read_reply`).
|
|
227
|
+
* An answer whose next step is a link the call handed over reads as that step's fixed short line
|
|
228
|
+
* instead (`stepAsWritten`), whoever wrote the reply.
|
|
186
229
|
*
|
|
187
230
|
* ```ts
|
|
188
231
|
* replyAsWritten(result); // 'found two at Amazon. want the first one?'
|
|
189
232
|
* ```
|
|
190
233
|
*/
|
|
191
234
|
export declare function replyAsWritten(result: BuyToolResult): string | null;
|
|
235
|
+
/** A card the shopper holds: a product's or a store's name and, for a product, its price as the model reads it (`$22.89`, `from $10.69`). */
|
|
236
|
+
export interface ShownCard {
|
|
237
|
+
name: string;
|
|
238
|
+
price?: string | null;
|
|
239
|
+
}
|
|
192
240
|
/**
|
|
193
241
|
* Agentcard's reply without the list its cards already show the shopper: each list line that names a product or
|
|
194
242
|
* store whose card went out, the line that led into that list (`here are a few:`, `other choices:`), a sentence that
|
|
195
|
-
* points at a number the list gave (`the dishwand in #8`),
|
|
196
|
-
*
|
|
197
|
-
*
|
|
243
|
+
* points at a number the list gave (`the dishwand in #8`), a sentence that goes on to more of the list (`other 6-packs
|
|
244
|
+
* are ...`, `there's also ...`) and names a card, and, past the one or two products a reply recommends in words, each
|
|
245
|
+
* further sentence that names another card. What is left is the reply around the list, such as `found these at
|
|
246
|
+
* amazon.` and `which one do you want?`. A reply whose cards went out names none of them twice over.
|
|
247
|
+
*
|
|
248
|
+
* Each card is its name, or its name and its price as the model reads it (`{ name, price: '$22.89' }`): with the price,
|
|
249
|
+
* a reply's short name for the product, such as `Planet Oat Extra Creamy ($22.89)`, reads as its card (cardsNamed).
|
|
198
250
|
*
|
|
199
251
|
* ```ts
|
|
200
252
|
* withoutCardList('Two I'd suggest:\n- Duracell AA, 24 pack, $20.78\n\nWhich one?', ['Duracell AA Batteries, 24 Count']);
|
|
201
253
|
* // 'Which one?'
|
|
202
254
|
* ```
|
|
203
255
|
*/
|
|
204
|
-
export declare function withoutCardList(reply: string, cardNames: readonly string[]): string;
|
|
256
|
+
export declare function withoutCardList(reply: string, cardNames: readonly (string | ShownCard)[]): string;
|
|
205
257
|
/**
|
|
206
258
|
* A thrown error as the model may read it: the same message with every link
|
|
207
259
|
* taken out, and the original error on `cause` for your logs. A TypeError
|