@agent-cards/commerce-sdk 0.18.15 → 0.18.16
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 +1 -0
- package/dist/basket.d.ts +36 -0
- package/dist/basket.js +22 -0
- package/dist/buy-tool.d.ts +3 -0
- package/dist/buy-tool.js +21 -1
- package/dist/client.d.ts +3 -0
- package/dist/client.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/message.d.ts +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -886,6 +886,7 @@ The facts are `merchantName`, `totalCents`, `currency`, `cardBrand`, `cardLast4`
|
|
|
886
886
|
| `cart_ready` | The store's checkout built the cart an answer named as still being built (`cart_building`). `text` gives the store's own total, and nothing is paid until the shopper says yes to it. |
|
|
887
887
|
| `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. Their reply continues the cart. |
|
|
888
888
|
| `cart_failed` | That checkout built no cart, and nothing was ordered or charged. |
|
|
889
|
+
| `basket_approval` | Every store in a basket reached its payment step after the answer to the shopper's yes. `text` carries the one approval link for every seller, and each store charges separately. |
|
|
889
890
|
|
|
890
891
|
Agentcard can add a kind later. A kind your code does not know still carries `text`, ready to send as is.
|
|
891
892
|
|
package/dist/basket.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A basket on a purchase answer: the carts at several sellers that one approval pays, each seller charging the card
|
|
3
|
+
* separately for no more than its line. `approval_url` is the one link the shopper approves on while the basket waits for
|
|
4
|
+
* that approval or pays under it. Null on an answer with no basket, and on an older API.
|
|
5
|
+
*
|
|
6
|
+
* A basket whose stores reach their payment step after this answer has no link yet: the link then arrives as a
|
|
7
|
+
* `basket_approval` purchase message.
|
|
8
|
+
*/
|
|
9
|
+
export type BuyBasketStatus = 'collecting' | 'awaiting_approval' | 'approved' | 'settling' | 'done' | 'expired' | 'declined';
|
|
10
|
+
/**
|
|
11
|
+
* Where one seller's line stands: `parking` on its way to the store's payment step, `parked` there and in the approval,
|
|
12
|
+
* `left_out` asking its own approval, `approved`, `sent` (the card went to the store), `charged`, `needs_input` (the store
|
|
13
|
+
* asks more than the approval covers, and asks its own approval at the new total), `failed`, `released`.
|
|
14
|
+
*/
|
|
15
|
+
export type BuyBasketLineStatus = 'parking' | 'parked' | 'left_out' | 'approved' | 'sent' | 'charged' | 'needs_input' | 'failed' | 'released';
|
|
16
|
+
export interface BuyBasketLine {
|
|
17
|
+
/** The seller as the shopper knows it: the store or the airline. */
|
|
18
|
+
seller: string;
|
|
19
|
+
status: BuyBasketLineStatus;
|
|
20
|
+
total_cents: number;
|
|
21
|
+
/** The most this seller may charge: its total_cents, since a seller is paid exactly the amount the shopper approved. */
|
|
22
|
+
cap_cents: number;
|
|
23
|
+
}
|
|
24
|
+
export interface BuyBasket {
|
|
25
|
+
id: string;
|
|
26
|
+
status: BuyBasketStatus;
|
|
27
|
+
total_cents: number;
|
|
28
|
+
/** The most every seller together may charge: total_cents, the sum of what each seller is paid. */
|
|
29
|
+
cap_cents: number;
|
|
30
|
+
currency: string;
|
|
31
|
+
approval_url: string | null;
|
|
32
|
+
expires_at: string;
|
|
33
|
+
lines: BuyBasketLine[];
|
|
34
|
+
}
|
|
35
|
+
/** The basket an answer carries, or null when it carries none, or one with a status or a line this version cannot read. */
|
|
36
|
+
export declare function basketOf(v: unknown): BuyBasket | null;
|
package/dist/basket.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const BASKET_STATUSES = new Set(['collecting', 'awaiting_approval', 'approved', 'settling', 'done', 'expired', 'declined']);
|
|
2
|
+
const LINE_STATUSES = new Set(['parking', 'parked', 'left_out', 'approved', 'sent', 'charged', 'needs_input', 'failed', 'released']);
|
|
3
|
+
const isRecord = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
4
|
+
const whole = (v) => typeof v === 'number' && Number.isSafeInteger(v);
|
|
5
|
+
/** The basket an answer carries, or null when it carries none, or one with a status or a line this version cannot read. */
|
|
6
|
+
export function basketOf(v) {
|
|
7
|
+
if (!isRecord(v) || typeof v.id !== 'string' || typeof v.status !== 'string' || !Array.isArray(v.lines))
|
|
8
|
+
return null;
|
|
9
|
+
if (!whole(v.total_cents) || !whole(v.cap_cents) || typeof v.currency !== 'string' || typeof v.expires_at !== 'string')
|
|
10
|
+
return null;
|
|
11
|
+
if (!BASKET_STATUSES.has(v.status))
|
|
12
|
+
return null;
|
|
13
|
+
const lines = [];
|
|
14
|
+
// Every line or none: an app never shows fewer sellers than the approval covers.
|
|
15
|
+
for (const l of v.lines) {
|
|
16
|
+
if (!isRecord(l) || typeof l.seller !== 'string' || typeof l.status !== 'string' || !LINE_STATUSES.has(l.status) || !whole(l.total_cents) || !whole(l.cap_cents))
|
|
17
|
+
return null;
|
|
18
|
+
lines.push({ seller: l.seller, status: l.status, total_cents: l.total_cents, cap_cents: l.cap_cents });
|
|
19
|
+
}
|
|
20
|
+
return { id: v.id, status: v.status, total_cents: v.total_cents, cap_cents: v.cap_cents, currency: v.currency,
|
|
21
|
+
approval_url: typeof v.approval_url === 'string' ? v.approval_url : null, expires_at: v.expires_at, lines };
|
|
22
|
+
}
|
package/dist/buy-tool.d.ts
CHANGED
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
*/
|
|
34
34
|
import type { BuyCardSize } from './card-size.js';
|
|
35
35
|
import type { BuyPlan } from './plan.js';
|
|
36
|
+
import type { BuyBasket } from './basket.js';
|
|
36
37
|
import type { BuyMerchantStatusUpdate, BuySearchStatus } from './search-status.js';
|
|
37
38
|
import { type BuyGroupsUpdate } from './search-groups.js';
|
|
38
39
|
import type { BuyFreshness, BuyRecheck, BuyStoreCheckUpdate } from './client.js';
|
|
@@ -259,6 +260,8 @@ export interface BuyToolResult {
|
|
|
259
260
|
cart_building?: BuyCartBuilding | null;
|
|
260
261
|
/** The checkout plan the answer started or moved, such as a trip's flight, hotel and car with each one's state; null when none. */
|
|
261
262
|
plan?: BuyPlan | null;
|
|
263
|
+
/** The basket the yes parked its carts into: one approval link for every seller, and each seller's line; null when none. */
|
|
264
|
+
basket?: BuyBasket | null;
|
|
262
265
|
/** Every store the answer's searches asked and where each stands (`searchAt` narrows it to one store); null when it searched none. */
|
|
263
266
|
search?: BuySearchStatus | null;
|
|
264
267
|
/**
|
package/dist/buy-tool.js
CHANGED
|
@@ -337,6 +337,7 @@ function answerOf(turn) {
|
|
|
337
337
|
choice: turn.choice ?? null,
|
|
338
338
|
code_request: turn.code_request ?? null,
|
|
339
339
|
plan: turn.plan ?? null,
|
|
340
|
+
basket: turn.basket ?? null,
|
|
340
341
|
search: turn.search ?? null,
|
|
341
342
|
envelope: turn.raw,
|
|
342
343
|
};
|
|
@@ -372,6 +373,18 @@ function onlyNextStepLinks(result, next) {
|
|
|
372
373
|
const links = result.links.filter((link) => (link.kind === 'vault' ? next === 'add_card' : link.kind === 'approval' ? next === 'approve' : true));
|
|
373
374
|
return links.length === result.links.length ? result : { ...result, links };
|
|
374
375
|
}
|
|
376
|
+
/**
|
|
377
|
+
* The answer whose step is the approval it carries: an approval Agentcard already sent is the step the
|
|
378
|
+
* shopper takes now, so an answer that names another step names `approve` instead. The link is the one way
|
|
379
|
+
* to that approval, and the other step held it back and left the shopper with an address question and no
|
|
380
|
+
* link until the approval was about to expire (2026-11-04, an Amazon cart). An answer that names no step
|
|
381
|
+
* keeps naming none, as it always did.
|
|
382
|
+
*/
|
|
383
|
+
function approvalIsTheStep(result) {
|
|
384
|
+
if (result.status !== 'approval_sent' || !result.next_action || result.next_action === 'approve')
|
|
385
|
+
return result;
|
|
386
|
+
return result.links.some((link) => link.kind === 'approval') ? { ...result, next_action: 'approve' } : result;
|
|
387
|
+
}
|
|
375
388
|
/**
|
|
376
389
|
* The answer's `messages`, read once its `reply` is final: the messages Agentcard sent while the reply
|
|
377
390
|
* is still Agentcard's own, else the reply the tool wrote, each cut at its blank lines.
|
|
@@ -448,7 +461,8 @@ function askingForNamedStep(result, next) {
|
|
|
448
461
|
}
|
|
449
462
|
/** A purchase answer, read for its status. A status Agentcard adds later reads as `needs_input`. */
|
|
450
463
|
function resultOfTurn(turn) {
|
|
451
|
-
|
|
464
|
+
const sent = approvalIsTheStep(resultOfStatus(turn));
|
|
465
|
+
return askingForNamedStep(onlyNextStepLinks(sent, sent.next_action), sent.next_action);
|
|
452
466
|
}
|
|
453
467
|
/** A purchase answer, read for its status, with every link the answer carries. */
|
|
454
468
|
function resultOfStatus(turn) {
|
|
@@ -456,6 +470,12 @@ function resultOfStatus(turn) {
|
|
|
456
470
|
const card = needsCardOf(turn, answer);
|
|
457
471
|
if (card)
|
|
458
472
|
return card;
|
|
473
|
+
// A basket awaiting its one approval: that link is the step, for every seller at once, whatever the answer's status.
|
|
474
|
+
const basket = turn.basket;
|
|
475
|
+
if (basket && basket.status === 'awaiting_approval' && basket.approval_url) {
|
|
476
|
+
return { ...answer, status: 'approval_sent', next_action: 'approve', approval_expires_at: basket.expires_at,
|
|
477
|
+
links: [...answer.links, approvalLink(basket.approval_url, basket.expires_at, null)] };
|
|
478
|
+
}
|
|
459
479
|
switch (turn.status) {
|
|
460
480
|
case 'order_placed':
|
|
461
481
|
case 'partially_placed':
|
package/dist/client.d.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { type BuyTrip } from './trip.js';
|
|
12
12
|
import { type BuyPlan } from './plan.js';
|
|
13
|
+
import { type BuyBasket } from './basket.js';
|
|
13
14
|
import { type BuyMerchantStatusUpdate, type BuySearchStatus } from './search-status.js';
|
|
14
15
|
import { type BuyGroup, type BuyGroupsUpdate } from './search-groups.js';
|
|
15
16
|
import { type BuyFulfilledBy } from './fulfilled-by.js';
|
|
@@ -731,6 +732,8 @@ export interface BuyTurn {
|
|
|
731
732
|
choice: BuyChoice | null;
|
|
732
733
|
/** The checkout plan this answer's yes started or moved, with each item's state; null when none, and on an older API. */
|
|
733
734
|
plan: BuyPlan | null;
|
|
735
|
+
/** The basket this answer's yes parked its carts into, with its one approval link and each seller's line; null when none, and on an older API. */
|
|
736
|
+
basket: BuyBasket | null;
|
|
734
737
|
/** Every store the turn searched and where each stands; null for a turn that searched none, and on an older API. */
|
|
735
738
|
search: BuySearchStatus | null;
|
|
736
739
|
/** The answer as Agentcard sent it. */
|
package/dist/client.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { tripOf } from './trip.js';
|
|
12
12
|
import { planOf } from './plan.js';
|
|
13
|
+
import { basketOf } from './basket.js';
|
|
13
14
|
import { merchantStatusOf, searchOf } from './search-status.js';
|
|
14
15
|
import { groupListOf, groupOf, groupsOf } from './search-groups.js';
|
|
15
16
|
import { isRecord } from './verify.js';
|
|
@@ -405,6 +406,7 @@ export function turnOf(body) {
|
|
|
405
406
|
cart_building: cartBuildingOf(body.cart_building),
|
|
406
407
|
choice: choiceOf(body.choice),
|
|
407
408
|
plan: planOf(body.plan),
|
|
409
|
+
basket: basketOf(body.basket),
|
|
408
410
|
search: searchOf(body.search),
|
|
409
411
|
raw: body,
|
|
410
412
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export { turnStream, readTurn, TURN_RECORD_TTL_SECONDS, type TurnStream, type Tu
|
|
|
23
23
|
export { TURN_RECOVERY_ON, isTurnId, type TurnEvent, type TurnRecord, type TurnRecordStore } from './turn-record.js';
|
|
24
24
|
export type { BuyLateResults, BuyStoreWait } from './store-wait.js';
|
|
25
25
|
export { planOf, type BuyPlan, type BuyPlanItem, type BuyPlanItemState, type BuyPlanStatus } from './plan.js';
|
|
26
|
+
export { basketOf, type BuyBasket, type BuyBasketLine, type BuyBasketLineStatus, type BuyBasketStatus } from './basket.js';
|
|
26
27
|
export { tripOf, type BuyTrip, type TripLane, type TripLaneName, type TripLaneResult, type TripLaneStatus } from './trip.js';
|
|
27
28
|
export { fulfilledByLine, fulfilledByOf, type BuyFulfilledBy, type BuyFulfilledByKind } from './fulfilled-by.js';
|
|
28
29
|
export { cardSizeOf, type BuyCardSize } from './card-size.js';
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,7 @@ export { AGENTCARD_EVENT_TYPES, } from './events.js';
|
|
|
22
22
|
export { turnStream, readTurn, TURN_RECORD_TTL_SECONDS } from './turn-stream.js';
|
|
23
23
|
export { TURN_RECOVERY_ON, isTurnId } from './turn-record.js';
|
|
24
24
|
export { planOf } from './plan.js';
|
|
25
|
+
export { basketOf } from './basket.js';
|
|
25
26
|
export { tripOf } from './trip.js';
|
|
26
27
|
export { fulfilledByLine, fulfilledByOf } from './fulfilled-by.js';
|
|
27
28
|
export { cardSizeOf } from './card-size.js';
|
package/dist/message.d.ts
CHANGED
|
@@ -25,8 +25,9 @@ import { type AgentcardEvent } from './verify.js';
|
|
|
25
25
|
* - `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.
|
|
26
26
|
* - `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.
|
|
27
27
|
* - `cart_failed`: that checkout built no cart, and nothing was ordered or charged.
|
|
28
|
+
* - `basket_approval`: every store in a basket reached its payment step after the answer to the shopper's yes; `text` carries the one approval link for every seller, and each store charges separately.
|
|
28
29
|
*/
|
|
29
|
-
export type PurchaseMessageKind = 'order_placed' | 'order_failed' | 'order_not_placed_yet' | 'order_unconfirmed' | 'order_placing' | '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';
|
|
30
|
+
export type PurchaseMessageKind = 'order_placed' | 'order_failed' | 'order_not_placed_yet' | 'order_unconfirmed' | 'order_placing' | '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' | 'basket_approval';
|
|
30
31
|
/** The message's values one by one, for a company that writes its own copy. A fact that does not apply is absent. */
|
|
31
32
|
export interface PurchaseFacts {
|
|
32
33
|
merchantName?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-cards/commerce-sdk",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.16",
|
|
4
4
|
"description": "Give your agent one tool that buys for your shoppers at real stores, and post each purchase update Agentcard sends into the shopper's chat, over a connection your server opens or a webhook endpoint.",
|
|
5
5
|
"homepage": "https://opencommerce.sh",
|
|
6
6
|
"type": "module",
|