@agent-cards/commerce-sdk 0.16.0 → 0.17.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 CHANGED
@@ -409,7 +409,7 @@ A reaction is the default acknowledgement. It names nothing the shopper said, so
409
409
 
410
410
  - A message that starts a search gets it the moment `buy.turn` opens, before your model runs.
411
411
  - A pick of a product or a store the shopper saw, an address or other details, an answer to a question such as `yes`, `2` or a code, a step toward checkout such as `ok lets checkout`, and an acknowledgement such as `its ok` get none when the turn opens, because Agentcard answers them fast.
412
- - A call that Agentcard has not answered within 1.5 seconds puts the 👀 on a message that has none yet: a slow `yes`, a pick, or a confirm that is still placing the order. It sends no text.
412
+ - A call that Agentcard has not answered within 1 second puts the 👀 on a message that has none yet: a slow `yes`, a pick, or a confirm that is still placing the order. It sends no text.
413
413
  - A turn that runs long, even past a minute, still gets no text line. A search often takes 10 to 40 seconds, so a line at any wait short enough to help would land on most searches and bring back the chatter the reaction replaces. To show a long wait, pass `onProgress` and show its stage, such as `checking_out`, as a status line or a typing indicator.
414
414
 
415
415
  With `onItem` on `buy.turn`, the 👀 still goes to `onReaction` and never joins the turn's items, because it sits on the shopper's bubble rather than in the agent's sequence. Without an `onReaction`, the text instant reply is the turn's first `text` item, as before.
@@ -431,9 +431,9 @@ const buy = buyTool({
431
431
 
432
432
  `ack` takes `'reaction'`, `'text'` or `'none'`. Left out, the tool reacts when the tool or the turn has an `onReaction`, and sends text through `onWorking` otherwise, so an app built on 0.9 with only `onWorking` keeps its text replies. With both callbacks, the shopper gets the 👀 and never the text too. `'none'`, or `false`, sends nothing.
433
433
 
434
- In text mode, the tool sends the reply when Agentcard has not answered a new request within 1.5 seconds, so a quick answer arrives alone. It sends one for the calls the model makes in one turn, where a call that starts within 15 seconds of the last one ending counts as the same turn. A slow confirm or pick never hears `looking for`: a pick of a product the shopper saw hears `adding it now`, a pick of a store or a place in a list, such as `the first one`, hears `got it, one moment`, and a confirm hears `on it, one moment`. None of them names anything the shopper said, and a code hears nothing. The words rotate through six templates, and one tool never sends the same template twice in a row. A short paraphrase of the request goes in when the tool can read one: it stops before any link, email address, phone number, street address or long number, and a request that starts with one gets a reply that names nothing, such as `looking for that now`. The call waits for `onWorking`, so the shopper reads the reply before any link and before the model answers. An `onWorking` that throws costs only the instant reply.
434
+ In text mode, a streamed search's reply is Agentcard's own line, written from the conversation while it searches, such as `one sec, checking target for la croix`, and it reaches `onWorking` before the cards or not at all. A search `buy.turn` sends straight that has shown the shopper nothing by twice `ackDelayMs`, because Agentcard wrote no line or its line was dropped, sends `one moment` once, and a line of Agentcard's that comes after it is dropped. When your model's run fails before it wrote or called anything, call `turn.end()` before you show your error, so no wait line follows it. With `stream: false`, the tool sends its own reply when Agentcard has not answered a new request within `ackDelayMs`, 1 second by default, so a quick answer arrives alone. It sends one for the calls the model makes in one turn, where a call that starts within 15 seconds of the last one ending counts as the same turn. A slow confirm or pick never hears `looking for`: a pick, of a product, a store or a place in a list such as `the first one`, hears `got it, one moment`, a confirm hears `on it, one moment`, and any other answer hears `one moment`. A turn opened with `buy.turn` that has shown the shopper nothing, no line and no card, after the delay sends that line once. None of them names anything the shopper said, and a code hears nothing. The words rotate through six templates, and one tool never sends the same template twice in a row. A short paraphrase of the request goes in when the tool can read one: it stops before any link, email address, phone number, street address or long number, and a request that starts with one gets a reply that names nothing, such as `looking for that now`. The call waits for `onWorking`, so the shopper reads the reply before any link and before the model answers. An `onWorking` that throws costs only the instant reply.
435
435
 
436
- Pass `ack: (ask) => text` to write your own text from the shopper's words, where an empty answer sends none, and `ackDelayMs` to change the 1.5 seconds for either mode.
436
+ Pass `ack: (ask) => text` to write your own text from the shopper's words, where an empty answer sends none, and `ackDelayMs` to change the 1 second for either mode.
437
437
 
438
438
  The model takes several seconds to call the tool after the shopper's message arrives. Without `buy.turn`, react sooner by calling `buy.instantReaction(message)` the moment the message arrives, before your model runs, and put the emoji it returns on the message. In text mode, call `buy.instantAck(message)` and send the text it returns:
439
439
 
@@ -447,7 +447,7 @@ if (ack) await textShopper(shopper.id, ack);
447
447
 
448
448
  With `buy.turn`, pass `onReaction` or `onWorking` to the turn instead: it acknowledges the moment the turn opens, and calls `instantReaction` or `instantAck` for you. When either returns a 👀 or a reply, the tool sends none of its own for that turn.
449
449
 
450
- `instantReaction` and `instantAck` return null for a message that starts no search: a confirmation or a short answer such as `yes`, `1` or `ok`, a step toward paying for the cart such as `ok lets checkout`, `place the order` or `pay`, an acknowledgement such as `its ok`, `thats fine` or `sounds good`, a pick of a product the shopper already saw, such as `the first one`, `#2`, `I want the blue one` or a card's title with its price, a pick of a store, such as `order here` or a store card's `Order from Tacos El Grullense`, a code, small talk such as `hi` or `how are you`, a message that carries the shopper's own details, such as a name with a phone number, an email address, a street address, a bare house number and street such as `1801 wedemeyer`, or a zip code, any message that answers the last answer's request for an address, shipping or guest details or a code, and a short reply right after any other question the last answer asked, such as `im a 37` after `what size do you want?`, or `carne asada` after `which meat?`. A reply that opens a new request, such as `find me black running shoes`, or that runs past four words is still a search. The reply never reads those details back, never repeats the shopper's own first-person words, and names the request only when it reads as a short, whole phrase; otherwise it says `on it, give me a moment` or another reply that names nothing. The tool also reads a message that quotes the name of a product its last answer showed, or names one of the stores its last answer listed and nothing else to buy, as a pick of it. A request for a store the shopper did not see, such as `order from chipotle`, reads back as `looking for chipotle`, never `looking for from chipotle`. When it returns a reply, the tool sends none of its own through `onWorking` for that turn, and the model reads `shopper_already_told_looking: true`, so the shopper hears one acknowledgement. When it returns null for a message that picks nothing, such as `yes`, a call that runs past `ackDelayMs` still acknowledges: with a 👀 in reaction mode, and in text mode with an `onWorking` reply that names nothing and never says it is looking for anything, such as `working on it` or `one moment`, and when it returns null for a pick, such a call sends `adding it now` or `got it, one moment`.
450
+ `instantReaction` and `instantAck` return null for a message that starts no search: a confirmation or a short answer such as `yes`, `1` or `ok`, a step toward paying for the cart such as `ok lets checkout`, `place the order` or `pay`, an acknowledgement such as `its ok`, `thats fine` or `sounds good`, a pick of a product the shopper already saw, such as `the first one`, `#2`, `I want the blue one` or a card's title with its price, a pick of a store, such as `order here` or a store card's `Order from Tacos El Grullense`, a code, small talk such as `hi` or `how are you`, a message that carries the shopper's own details, such as a name with a phone number, an email address, a street address, a bare house number and street such as `1801 wedemeyer`, or a zip code, any message that answers the last answer's request for an address, shipping or guest details or a code, and a short reply right after any other question the last answer asked, such as `im a 37` after `what size do you want?`, or `carne asada` after `which meat?`. A reply that opens a new request, such as `find me black running shoes`, or that runs past four words is still a search. The reply never reads those details back, never repeats the shopper's own first-person words, and names the request only when it reads as a short, whole phrase; otherwise it says `on it, give me a moment` or another reply that names nothing. The tool also reads a message that quotes the name of a product its last answer showed, or names one of the stores its last answer listed and nothing else to buy, as a pick of it. A request for a store the shopper did not see, such as `order from chipotle`, reads back as `looking for chipotle`, never `looking for from chipotle`. When it returns a reply, the tool sends none of its own through `onWorking` for that turn, and the model reads `shopper_already_told_looking: true`, so the shopper hears one acknowledgement. When it returns null for a message that picks nothing, such as `yes`, a call that runs past `ackDelayMs` still acknowledges: with a 👀 in reaction mode, and in text mode with an `onWorking` reply that names nothing and never says it is looking for anything, such as `one moment`, and when it returns null for a pick, such a call sends `got it, one moment`.
451
451
 
452
452
  Without a buy tool, `instantReaction(message, { nextAction, asked, stores })` and `instantAck(message, { previous, nextAction, asked, stores })` from the package root decide the same way, and `ACK_REACTION` is the `👀` `instantReaction` returns: `previous` is the reply you sent last, so the next one comes from another template, `nextAction` is the last answer's `next_action`, `asked` is the last message the shopper read, so an answer to its question for an address, a name or a code, or a short answer to any other question it asked, gets none, and `stores` holds the names of the stores the last answer listed, so a message that picks one gets none. Pass `ack: 'none'` to the tool when you acknowledge yourself. `ackFor` is the earlier name of the same function.
453
453
 
package/dist/ack.d.ts CHANGED
@@ -43,13 +43,23 @@ export declare function paraphrase(text: string): string | null;
43
43
  export declare function ackAt(index: number, text: string): string;
44
44
  /** The reply at `index` for a message that is no search: it says Agentcard is working, and never that it is looking for anything. */
45
45
  export declare function waitAt(index: number): string;
46
- /** What a slow call that is not a new request is doing: adding a product the shopper picked, choosing a store, or going through with a cart. */
47
- export type SlowStep = 'product' | 'pick' | 'confirm';
48
46
  /**
49
- * The reply a slow pick or confirm sends once Agentcard takes longer than the delay: fixed words that
50
- * name nothing the shopper said and never say `looking for`, because the shopper already chose.
47
+ * What a slow call that is not a new request is doing: adding a product the shopper picked, choosing a store, going
48
+ * through with a cart, or answering anything else, such as `yes` or an address.
49
+ */
50
+ export type SlowStep = 'product' | 'pick' | 'confirm' | 'wait';
51
+ /**
52
+ * The reply a slow pick, confirm or answer sends once Agentcard takes longer than the delay: fixed words that
53
+ * name nothing the shopper said and never say `looking for`, because the shopper already chose. A pick never
54
+ * says it is adding anything: a sandwich's bread can still hold the cart line up.
51
55
  */
52
56
  export declare function slowStepLine(step: SlowStep): string;
57
+ /**
58
+ * Whether a model's message reads as an acknowledgement, such as `on it, looking for a dryer under $20`: one short line
59
+ * that opens with acknowledging words, says the turn is working, asks nothing and says nothing of what a search or a
60
+ * cart came to. A question, an answer such as `i can only help with shopping.`, or anything longer is not.
61
+ */
62
+ export declare function readsAsAckLine(message: string): boolean;
53
63
  /** A template other than `avoid`, picked at random. */
54
64
  export declare function nextTemplate(avoid: number, random?: () => number): number;
55
65
  export interface AckForOptions {
package/dist/ack.js CHANGED
@@ -298,11 +298,37 @@ export function waitAt(index) {
298
298
  return WAITS[((index % WAITS.length) + WAITS.length) % WAITS.length];
299
299
  }
300
300
  /**
301
- * The reply a slow pick or confirm sends once Agentcard takes longer than the delay: fixed words that
302
- * name nothing the shopper said and never say `looking for`, because the shopper already chose.
301
+ * The reply a slow pick, confirm or answer sends once Agentcard takes longer than the delay: fixed words that
302
+ * name nothing the shopper said and never say `looking for`, because the shopper already chose. A pick never
303
+ * says it is adding anything: a sandwich's bread can still hold the cart line up.
303
304
  */
304
305
  export function slowStepLine(step) {
305
- return step === 'product' ? 'adding it now' : step === 'confirm' ? 'on it, one moment' : 'got it, one moment';
306
+ return step === 'confirm' ? 'on it, one moment' : step === 'wait' ? 'one moment' : 'got it, one moment';
307
+ }
308
+ /** Words that say what a search or a cart came to, which no acknowledgement can know yet. */
309
+ const RESULT_CLAIM = /\b(found|finds?|added|adding|placed|total|totals|price[sd]?|pricing|in stock|out of stock|sold out|ordered|paid|charged|cart|costs?|here (?:are|is|'s))\b/i;
310
+ /** The most characters a model's acknowledgement line runs to, such as `on it, looking for a dryer under $20`. */
311
+ const ACK_LINE_MAX = 90;
312
+ /** Words an acknowledgement opens with, such as `on it,` in `on it, looking for a dryer under $20`. */
313
+ const ACK_OPENER = /^(?:(?:ok|okay|sure(?: thing)?|alright|all right|yes|yeah|yep|got it|on it|will do|of course|no problem|happy to help|absolutely)\b[\s,.!-]*)+/;
314
+ /** What is left of an acknowledgement after its opening words: it says the turn is working, and nothing else. */
315
+ const ACK_WORK = /^(?:(?:i'?ll|i'?m going to|i'?m gonna|i'?m|i am|let me|lemme|let'?s|just|now|gonna|going to)\s+)*(?:look|looking|check|checking|search|searching|finding|see what|seeing what|pull(?:ing)? up|work(?:ing)? on|dig(?:ging)?|hunt(?:ing)?|browse|browsing|start(?:ing)?)\b|^(?:one (?:moment|sec|second|minute)|a (?:moment|sec|second)|just a (?:moment|sec|second|minute)|give me a (?:moment|sec|second|minute)|hang tight|hold (?:on|tight)|two secs?|moment)\b/;
316
+ /**
317
+ * Whether a model's message reads as an acknowledgement, such as `on it, looking for a dryer under $20`: one short line
318
+ * that opens with acknowledging words, says the turn is working, asks nothing and says nothing of what a search or a
319
+ * cart came to. A question, an answer such as `i can only help with shopping.`, or anything longer is not.
320
+ */
321
+ export function readsAsAckLine(message) {
322
+ const line = String(message ?? '').trim();
323
+ if (line === '' || line.length > ACK_LINE_MAX || /[\r\n]/.test(line))
324
+ return false;
325
+ if (line.includes('?'))
326
+ return false;
327
+ if (RESULT_CLAIM.test(line))
328
+ return false;
329
+ // `on it` or `got it` is the whole acknowledgement; whatever follows those words has to say the turn is working.
330
+ const rest = line.toLowerCase().replace(ACK_OPENER, '').replace(/[\s,.!-]+$/, '');
331
+ return rest === '' ? ACK_OPENER.test(line.toLowerCase()) : ACK_WORK.test(rest);
306
332
  }
307
333
  /** A template other than `avoid`, picked at random. */
308
334
  export function nextTemplate(avoid, random = Math.random) {
@@ -315,12 +315,14 @@ export interface BuyToolOptions {
315
315
  */
316
316
  onReaction?: (emoji: string) => void | Promise<void>;
317
317
  /**
318
- * Sends the shopper an instant reply, such as `on it, looking for usb-c cables under $20`, while
319
- * Agentcard works on a new request, when the tool acknowledges in text: with `ack: 'text'`, or with
320
- * no `onReaction`. It goes out when Agentcard has not answered within
321
- * `ackDelayMs`, once for the calls the model makes in one turn. A slow pick gets `adding it now`
322
- * for a product or `got it, one moment` for a store or a place in a list. A slow confirm gets
323
- * `on it, one moment`. These replies name nothing the shopper said. A code gets no reply.
318
+ * Sends the shopper an instant reply, when the tool acknowledges in text: with `ack: 'text'`, or with
319
+ * no `onReaction`. A streamed search's reply is Agentcard's own line, such as `one sec, checking target
320
+ * for la croix`, written while it searches and sent before the cards or not at all; a search `buy.turn` sends
321
+ * straight that has shown nothing by twice `ackDelayMs` gets `one moment` instead; with `stream: false`
322
+ * it is the tool's template, once Agentcard has not answered within `ackDelayMs`. A pick, a confirm or
323
+ * any other answer that has shown nothing for `ackDelayMs` gets `got it, one moment`, `on it, one
324
+ * moment` or `one moment`, once for the calls the model makes in one turn. These replies name nothing
325
+ * the shopper said. A code gets no reply.
324
326
  * The call waits for it before it returns, so the shopper reads it before any link or the model's
325
327
  * answer. One that throws costs nothing but the instant reply.
326
328
  */
@@ -334,7 +336,7 @@ export interface BuyToolOptions {
334
336
  * names nothing.
335
337
  */
336
338
  ack?: BuyAckMode | false | ((ask: string) => string | null | undefined);
337
- /** How long Agentcard has to answer before the instant reply or the reaction goes out. Default 1500. */
339
+ /** How long a turn shows the shopper nothing before the instant reply or the reaction goes out. Default 1000. */
338
340
  ackDelayMs?: number;
339
341
  /**
340
342
  * Called with the whole answer once per call, after its links went out and before the model reads
@@ -620,6 +622,13 @@ export interface BuyToolTurn extends Omit<BuyTool, 'instantAck' | 'instantReacti
620
622
  * `buy.nextStep` for the same event opens a turn again, as on a webhook's redelivery. Does nothing on another turn.
621
623
  */
622
624
  release(): void;
625
+ /**
626
+ * Ends the turn: call it once your agent is done with the message, and above all when your model's run failed before
627
+ * it wrote or called anything. No wait line, such as `one moment`, and no 👀 goes out after it, not even for a call
628
+ * still running, so the shopper never reads that the turn is still working after your app showed its error. Calling it
629
+ * again does nothing.
630
+ */
631
+ end(): void;
623
632
  /** The instant reply this turn sent to `onWorking` when it opened, or null when it sent none, as with a reaction. */
624
633
  readonly ack: string | null;
625
634
  /** The reaction this turn put on the shopper's message, `👀`, when it opened or once a call ran slow, or null while it put none or when `onReaction` threw. */