@nuanu-ai/agentify-contracts 0.6.0 → 0.8.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/dist/card.js CHANGED
@@ -25,21 +25,29 @@
25
25
  * more shape to keep in step for no gain to anybody.
26
26
  */
27
27
  import { z } from "zod";
28
- import { ParamSpecSchema, paramSpecToValidator } from "./param-spec.js";
28
+ import { FieldSpecSchema, ParamNameSchema, ParamSpecSchema, paramSpecToValidator, } from "./param-spec.js";
29
29
  import { notPlainTextIn } from "./plain-text.js";
30
- import { IdentifierSchema, MoneySchema, TimestampSchema } from "./primitives.js";
30
+ import { IdentifierSchema, MoneySchema, OpenWordSchema, TimestampSchema } from "./primitives.js";
31
+ import { SellerSiteSchema } from "./seller-site.js";
31
32
  import { SellingStateSchema } from "./selling.js";
33
+ import { ShipToSchema } from "./ship-to.js";
34
+ import { ShipmentSchema } from "./shipment.js";
32
35
  /**
33
36
  * When the product reaches the agent, and therefore when the money moves.
34
37
  *
35
38
  * `sync` — in the answer to the purchase, and the payment executes last, after
36
39
  * the merchant has delivered. `async` — later, by a separate call, and the
37
40
  * payment executes at the moment of purchase. `confirm` — the merchant is
38
- * asked first, and the payment executes right after they say yes.
41
+ * asked first, and the payment executes right after they say yes. `ship` — a
42
+ * parcel the merchant hands to a carrier (ADR-0033): the payment executes at
43
+ * the moment of purchase, as in `async`, and the order carries the buyer's
44
+ * address until the merchant takes it on, or it ends without them.
39
45
  */
40
- export const FulfillmentSchema = z.enum(["sync", "async", "confirm"]).meta({
41
- description: 'When the product reaches the agent, and so when the money moves. "sync" — in the answer to the purchase, payment last. "async" — later, by a separate call, payment at the moment of purchase. "confirm" — the merchant is asked first and the payment follows their yes. A card cannot be published as "confirm" during the pilot: the confirmation request has no shape on the wire yet, so a handler could not tell one from a paid order.',
46
+ export const FulfillmentSchema = z.enum(["sync", "async", "confirm", "ship"]).meta({
47
+ description: 'When the product reaches the agent, and so when the money moves. "sync" — in the answer to the purchase, payment last. "async" — later, by a separate call, payment at the moment of purchase. "confirm" — the merchant is asked first and the payment follows their yes. "ship" — a parcel the merchant hands to a carrier, payment at the moment of purchase; the purchase carries the address it goes to. A card cannot be published as "confirm" during the pilot: the confirmation request has no shape on the wire yet, so a handler could not tell one from a paid order.',
42
48
  });
49
+ /** The longest a parcel's card may give its merchant to hand it to a carrier: thirty days. */
50
+ const SHIP_WITHIN_AT_MOST_SECONDS = 2_592_000;
43
51
  /**
44
52
  * How the price and availability of this card are asked for, if they are.
45
53
  *
@@ -118,8 +126,49 @@ const listedText = (what) => z
118
126
  * holds it is the merchants table, so this schema is the rule alone, applied
119
127
  * wherever a merchant's listing name is written down.
120
128
  */
121
- export const ServiceNameSchema = listedText("service name").meta({
122
- description: "The name a seller is listed under in a discovery catalog. At most 32 characters of printable ASCII, because that is what the catalog carries; a name outside that is refused here rather than truncated there, where nobody would be told.",
129
+ export const ServiceNameSchema = listedText("service name")
130
+ .superRefine((name, ctx) => {
131
+ // Every agent reads the name on every card and order of the merchant's
132
+ // (ADR-0034), so the door holds it to the card's own plain-text rule
133
+ // (ADR-0017): markup and character references are refused and named,
134
+ // not cleaned into words the merchant never wrote.
135
+ for (const phrase of notPlainTextIn(name, "one line")) {
136
+ ctx.addIssue({
137
+ code: "custom",
138
+ message: `this service name carries ${phrase}, and a service name is plain text, which an agent reads exactly as it is written`,
139
+ });
140
+ }
141
+ })
142
+ .meta({
143
+ description: "The name a seller is listed under in a discovery catalog and shown to every agent beside their products. At most 32 characters of printable ASCII, because that is what the catalog carries; a name outside that is refused here rather than truncated there, where nobody would be told. It is plain text: HTML markup and character references such as & are refused, and an ampersand written as text passes.",
144
+ });
145
+ /**
146
+ * The name as it is stored and read back: every rule the door holds it to
147
+ * except that it is plain text.
148
+ *
149
+ * That rule belongs to the door and not to the reader, for the reason the
150
+ * card's own words give (ADR-0017): a name stored before the rule may carry
151
+ * markup, every answer is held to its contract on the way out, and one old row
152
+ * would fail every card and order of its merchant's instead of being put right
153
+ * the next time the merchant sets their name.
154
+ */
155
+ const StoredServiceNameSchema = listedText("service name");
156
+ /**
157
+ * Who sells, as an agent reads it beside a product and on an order: the name
158
+ * the merchant sells under and the site of their shop (ADR-0034).
159
+ *
160
+ * Both are the merchant's own word and nothing here checked either, which the
161
+ * description says to the reader who acts on it — a name and a site can be
162
+ * anybody's. Null is "none given": a merchant who gave no site has none here,
163
+ * and a name is missing only from an order whose merchant has since lost it.
164
+ */
165
+ export const SellerSchema = z
166
+ .strictObject({
167
+ name: StoredServiceNameSchema.nullable(),
168
+ site: SellerSiteSchema.nullable(),
169
+ })
170
+ .meta({
171
+ description: "Who sells: the name the merchant sells under and the https address of their shop's own site, both as the merchant gave them. Agentify did not check either, so a name and a site can be anybody's. The site is where to take what an order cannot answer — a parcel that did not arrive, a return, the shop's terms. Either is null where the merchant has given none.",
123
172
  });
124
173
  /**
125
174
  * The words a merchant puts on one product so an agent searching a catalog can
@@ -220,19 +269,31 @@ const DescriptionSchema = z
220
269
  * JSON Schema, so the same constraint goes into the metadata as
221
270
  * `minProperties`, where a generator can still see it.
222
271
  *
223
- * One schema, used by the published card and by the projection below, because
272
+ * One rule, held by the published card and by the projection below, because
224
273
  * the promise is the same one: what the agent reads before paying is what the
225
274
  * merchant is held to afterwards. Two copies of a refinement would be two
226
- * promises, and the export would carry whichever was edited last.
275
+ * promises, and the export would carry whichever was edited last. The two
276
+ * declarations it is put on differ only in what else a field may carry: the
277
+ * merchant's is closed, and the agent's takes what is added later
278
+ * (ADR-0006 §5).
227
279
  */
228
- const DeclaredResultSchema = ParamSpecSchema.refine(
280
+ const declaringAResult = (declaration) => declaration
281
+ .refine(
229
282
  // Not merely non-empty: at least one field that actually arrives. A
230
283
  // declaration of one field marked `required: false` satisfies "at least
231
284
  // one" and promises the agent exactly as much as an empty one does.
232
- (spec) => Object.values(spec).some((field) => field.required !== false), "a card declares at least one field of what the agent receives, and at least one of them arrives every time").meta({
285
+ (spec) => Object.values(spec).some((field) => field.required !== false), "a card declares at least one field of what the agent receives, and at least one of them arrives every time")
286
+ .meta({
233
287
  description: "What the agent receives on delivery. At least one field, and at least one of the fields is not marked required: false — a result that might be entirely absent promises nothing.",
234
288
  minProperties: 1,
235
289
  });
290
+ const DeclaredResultSchema = declaringAResult(ParamSpecSchema);
291
+ /**
292
+ * A declaration as an agent reads it: the merchant's fields, each open to what
293
+ * is added to a field later (ADR-0006 §5), so a new attribute of one field
294
+ * leaves the card readable to an agent built before it.
295
+ */
296
+ const ReadDeclarationSchema = z.record(ParamNameSchema, FieldSpecSchema.loose());
236
297
  /**
237
298
  * The short forms a card may be written in, and the one form they all become.
238
299
  *
@@ -355,11 +416,15 @@ const CardFieldsSchema = z.strictObject({
355
416
  * says so, or a generated client would first meet the rule as a refusal.
356
417
  */
357
418
  price: CardPriceSchema.meta({
358
- description: 'Publishing refuses a price that is zero, not in USD or USDC, or written with fewer than two or more than six digits after the dot ("5.00", "0.001"), so that a price the catalog shows is one a payment can be taken at.',
419
+ description: 'Publishing refuses a price that is zero, not in USD or USDC, or written with fewer than two or more than six digits after the dot ("5.00", "0.001"), so that a price the catalog shows is one a payment can be taken at. On a "ship" card it is the goods without shipping: the price a purchase goes through at is the merchant\'s price handler\'s answer, which includes shipping to the buyer\'s place.',
359
420
  }),
360
421
  /** What the agent has to supply to buy. Absent when the purchase needs no input. */
361
422
  params: writtenShort(ParamSpecSchema).optional(),
362
- result: writtenShort(DeclaredResultSchema),
423
+ /**
424
+ * What the agent receives. Every card declares it except a parcel's, whose
425
+ * mode fixes it: the record of the parcel's shipment (ADR-0033).
426
+ */
427
+ result: writtenShort(DeclaredResultSchema).optional(),
363
428
  /**
364
429
  * Words for an agent searching a discovery catalog. Absent on a card whose
365
430
  * merchant named none, and never invented for them.
@@ -389,6 +454,16 @@ const CardFieldsSchema = z.strictObject({
389
454
  confirm_deadline_seconds: z.int().positive().optional(),
390
455
  /** How long the merchant may take to deliver an order it has accepted. */
391
456
  fulfill_deadline_seconds: z.int().positive().optional(),
457
+ /**
458
+ * On a parcel's card, how long the merchant may take to hand it to a carrier,
459
+ * counted from the charge (ADR-0033). A name of its own rather than the
460
+ * delivery deadline's, because on an asynchronous card that number is the
461
+ * time to deliver, and a number read under the wrong name tells a person
462
+ * their parcel arrives when it merely leaves. The ceiling is thirty days, the
463
+ * longest the marketplaces give a seller, and it bounds how long a buyer's
464
+ * money sits with a merchant before the order becomes a refund owed.
465
+ */
466
+ ship_within_seconds: z.int().positive().max(SHIP_WITHIN_AT_MOST_SECONDS).optional(),
392
467
  });
393
468
  /**
394
469
  * Both deadlines are shown to the agent before it pays, which is why a card
@@ -419,8 +494,8 @@ const cardRules = (card, ctx) => {
419
494
  // card that has one problem, and lifting the gate later would uncover it.
420
495
  return;
421
496
  }
422
- // Past the gate only "sync" and "async" remain, and neither is ever asked to
423
- // confirm — which is why this needs no test on the mode of its own.
497
+ // Past the gate no mode is ever asked to confirm — which is why this needs no
498
+ // test on the mode of its own.
424
499
  if (card.confirm_deadline_seconds !== undefined) {
425
500
  ctx.addIssue({
426
501
  code: "custom",
@@ -428,6 +503,24 @@ const cardRules = (card, ctx) => {
428
503
  message: `only a card with fulfillment "confirm" is asked to confirm; this one is "${card.fulfillment}"`,
429
504
  });
430
505
  }
506
+ if (card.fulfillment === "ship") {
507
+ parcelRules(card, ctx);
508
+ return;
509
+ }
510
+ if (card.ship_within_seconds !== undefined) {
511
+ ctx.addIssue({
512
+ code: "custom",
513
+ path: ["ship_within_seconds"],
514
+ message: `only a card with fulfillment "ship" hands its goods to a carrier and names a time to ship; this one is "${card.fulfillment}"`,
515
+ });
516
+ }
517
+ if (card.result === undefined) {
518
+ ctx.addIssue({
519
+ code: "custom",
520
+ path: ["result"],
521
+ message: 'result is required: a card declares what the agent receives, and only a parcel\'s, fulfillment "ship", leaves that to its mode',
522
+ });
523
+ }
431
524
  if (card.fulfillment === "sync" && card.fulfill_deadline_seconds !== undefined) {
432
525
  ctx.addIssue({
433
526
  code: "custom",
@@ -436,12 +529,62 @@ const cardRules = (card, ctx) => {
436
529
  });
437
530
  }
438
531
  };
532
+ /**
533
+ * What a parcel's card is held to (ADR-0033), beside what every card is.
534
+ *
535
+ * Each rule is about a word an agent or a merchant would otherwise read wrong.
536
+ * The time to ship is required because it is the one promise the agent is
537
+ * given about when the goods leave. A result is refused because the mode fixes
538
+ * it — the record of the shipment — and one a merchant declared would be a
539
+ * second promise beside it. A delivery deadline is refused because the number
540
+ * would read as the time the parcel arrives. The price is the merchant's
541
+ * handler's to answer, because shipping depends on where the parcel goes, and
542
+ * only the merchant knows what it costs to send it there. And the address is
543
+ * not one of the card's parameters, because it travels in its own block.
544
+ */
545
+ const parcelRules = (card, ctx) => {
546
+ if (card.ship_within_seconds === undefined) {
547
+ ctx.addIssue({
548
+ code: "custom",
549
+ path: ["ship_within_seconds"],
550
+ message: `a parcel's card names ship_within_seconds, the time to hand it to a carrier counted from the charge, at most ${SHIP_WITHIN_AT_MOST_SECONDS} (thirty days)`,
551
+ });
552
+ }
553
+ if (card.result !== undefined) {
554
+ ctx.addIssue({
555
+ code: "custom",
556
+ path: ["result"],
557
+ message: "a parcel's card declares no result: its mode fixes what the agent receives, the record of the parcel's shipment",
558
+ });
559
+ }
560
+ if (card.fulfill_deadline_seconds !== undefined) {
561
+ ctx.addIssue({
562
+ code: "custom",
563
+ path: ["fulfill_deadline_seconds"],
564
+ message: "a parcel's card names the time to ship, ship_within_seconds, and no delivery deadline: under that name the number would read as the time the parcel arrives",
565
+ });
566
+ }
567
+ if (card.price_check !== "handler") {
568
+ ctx.addIssue({
569
+ code: "custom",
570
+ path: ["price_check"],
571
+ message: "a parcel's price is asked of the merchant's own price handler, price_check: \"handler\", whose answer is the whole price with shipping to the buyer's place",
572
+ });
573
+ }
574
+ if (card.params !== undefined && "ship_to" in card.params) {
575
+ ctx.addIssue({
576
+ code: "custom",
577
+ path: ["params", "ship_to"],
578
+ message: "a parcel's card asks for no ship_to among its parameters: the address travels in a block of its own beside them",
579
+ });
580
+ }
581
+ };
439
582
  // JSON Schema has no way to say "this field only when that one has this
440
583
  // value", and zod drops the rules above when it renders a document. Left at
441
584
  // that, an engineer generating a client from the export would build one that
442
585
  // sends deadlines a card cannot carry and only find out on the first publish.
443
586
  // Saying it in words is weaker than checking it, and better than silence.
444
- const CARD_RULES_IN_WORDS = 'A card cannot be published as fulfillment "confirm" during the pilot: the confirmation request has no shape on the wire yet, so a handler could not tell one from a paid order. confirm_deadline_seconds is only allowed when fulfillment is "confirm", and fulfill_deadline_seconds only when fulfillment is "async" or "confirm" — a synchronous card delivers inside the system-wide response budget and names no deadline of its own.';
587
+ const CARD_RULES_IN_WORDS = 'A card cannot be published as fulfillment "confirm" during the pilot: the confirmation request has no shape on the wire yet, so a handler could not tell one from a paid order. confirm_deadline_seconds is only allowed when fulfillment is "confirm", and fulfill_deadline_seconds only when fulfillment is "async" or "confirm" — a synchronous card delivers inside the system-wide response budget and names no deadline of its own. result is required on every card except one whose fulfillment is "ship". A "ship" card, a parcel, names ship_within_seconds and is the only card that may; it declares no result and no fulfill_deadline_seconds, its price_check is "handler", and it asks for no ship_to among its params, because the purchase carries the address in a block of its own.';
445
588
  /**
446
589
  * The words on a card that an agent reads: the title, the description, and
447
590
  * the title of each field the card declares.
@@ -546,8 +689,12 @@ export const CardSchema = CardFieldsSchema.superRefine(cardRules)
546
689
  * place that picks.
547
690
  */
548
691
  export const purchaseCheckFor = (card) => paramSpecToValidator(card.params ?? {}, "purchase");
549
- /** The check this card's delivery is held to. */
550
- export const deliveryCheckFor = (card) => paramSpecToValidator(card.result, "delivery");
692
+ /**
693
+ * The check this card's delivery is held to: the goods its `result` declared,
694
+ * or for a parcel, which is not delivered with goods at all, its shipment
695
+ * (ADR-0033).
696
+ */
697
+ export const deliveryCheckFor = (card) => card.result === undefined ? ShipmentSchema : paramSpecToValidator(card.result, "delivery");
551
698
  /**
552
699
  * The fields of a card as an agent reads it in a catalog.
553
700
  *
@@ -571,11 +718,16 @@ export const deliveryCheckFor = (card) => paramSpecToValidator(card.result, "del
571
718
  * gateway's, and this document does not claim to know it.
572
719
  *
573
720
  * Both deadlines stay, because they are the merchant's promise to the agent
574
- * about how long it may wait, and the agent is told them before it pays. They
575
- * sit on the modes that have them, as branches of a union rather than as a
576
- * rule attached to one object: JSON Schema cannot say "this field only when
577
- * that one has this value", and zod drops such a rule without a word, so as
578
- * branches the constraint crosses whole into the export.
721
+ * about how long it may wait, and the agent is told them before it pays. The
722
+ * projection writes each only on a mode that has it, and the document says so
723
+ * in words rather than as structure: it takes fields added later (ADR-0006
724
+ * §5), so a field an agent does not expect is one it ignores, not one it can
725
+ * refuse.
726
+ *
727
+ * The mode is a word whose known values are listed beside it, for the same
728
+ * reason. The storefront has no version, so a mode added later reaches agents
729
+ * that still hold this contract, and to them it has to be a card to pass over
730
+ * rather than a catalog they cannot read.
579
731
  *
580
732
  * `as_of` is added: the moment the price shown here was published. A price
581
733
  * with no moment behind it cannot be judged stale, and this is the only
@@ -588,27 +740,28 @@ export const deliveryCheckFor = (card) => paramSpecToValidator(card.result, "del
588
740
  * page — so carrying them here would put a foreign catalog's constraint in
589
741
  * front of a reader who has no use for it.
590
742
  *
591
- * One thing an agent might reasonably want is not here, and saying so is
592
- * better than leaving it to be discovered: nothing in this document names who
593
- * is selling. There is a shape in this contract for one name a seller carries —
594
- * `ServiceNameSchema`, the name a discovery catalog lists them under — and it
595
- * is deliberately not this. That name exists to satisfy one channel's rules,
596
- * a merchant may have none, and standing it in for a public identity would
597
- * answer a question — who "we" are to the buyer, and who the merchant is —
598
- * that is still open.
743
+ * Who sells is here as the merchant gave it, a name and the site of their
744
+ * shop (ADR-0034), and as nothing more: no profile, no contact of ours, and no
745
+ * claim that either was checked. Who "we" are to the buyer is a question this
746
+ * document still does not answer.
599
747
  */
600
- const PublicCardFieldsSchema = z.strictObject({
748
+ export const PublicCardSchema = z
749
+ .looseObject({
601
750
  /** Our catalog identifier, the one a purchase, a receipt and a status use. */
602
751
  id: IdentifierSchema,
603
752
  title: TitleSchema,
604
753
  description: DescriptionSchema,
605
754
  /** The price in the catalog: what an agent compares when it is choosing. */
606
- price: MoneySchema,
755
+ price: MoneySchema.loose(),
607
756
  /** When the price above was published. */
608
757
  as_of: TimestampSchema,
609
758
  /** What the agent has to supply to buy. Absent when the purchase needs no input. */
610
- params: ParamSpecSchema.optional(),
611
- result: DeclaredResultSchema,
759
+ params: ReadDeclarationSchema.optional(),
760
+ /**
761
+ * What the agent receives. Absent on a parcel, whose mode fixes it: the
762
+ * record of the parcel's shipment.
763
+ */
764
+ result: declaringAResult(ReadDeclarationSchema).optional(),
612
765
  /**
613
766
  * Whether the merchant is asked for this product's price and availability at
614
767
  * the moment of purchase, so the sale may go through at a price other than
@@ -619,30 +772,36 @@ const PublicCardFieldsSchema = z.strictObject({
619
772
  * about to move, read as true it distrusts a price that never moves.
620
773
  */
621
774
  price_checked_at_purchase: z.boolean(),
622
- });
623
- export const PublicCardSchema = z
624
- .discriminatedUnion("fulfillment", [
625
- // Synchronous: the product arrives in the answer to the purchase, inside our
626
- // own response budget — one number for every product on the platform, which
627
- // is why no card names it and no card may name a delivery deadline instead.
628
- PublicCardFieldsSchema.extend({ fulfillment: z.literal("sync") }),
629
- // Asynchronous: the money moves at the purchase and the product comes later,
630
- // within the merchant's own delivery deadline where they set one.
631
- PublicCardFieldsSchema.extend({
632
- fulfillment: z.literal("async"),
633
- fulfill_deadline_seconds: z.int().positive().optional(),
634
- }),
635
- // With confirmation: the merchant is asked first and the payment follows
636
- // their yes, so both waits exist. No card can be published in this mode
637
- // during the pilot; the branch is here because the mode is in the
638
- // vocabulary, and a branch missing from a projection would be a second gate
639
- // in a second place for whoever lifts the first one.
640
- PublicCardFieldsSchema.extend({
641
- fulfillment: z.literal("confirm"),
642
- confirm_deadline_seconds: z.int().positive().optional(),
643
- fulfill_deadline_seconds: z.int().positive().optional(),
644
- }),
645
- ])
775
+ /**
776
+ * Who sells this product, in the merchant's own words and unchecked. It is
777
+ * read with the catalog rather than copied onto the card at publishing, so
778
+ * a merchant who changes their name or gives a site is found by it on every
779
+ * card they already sell.
780
+ */
781
+ seller: SellerSchema.loose(),
782
+ /**
783
+ * How the product is sold: "sync", "async" or "confirm" today, and a word
784
+ * added later reaches this same document. A reader keeps a default arm,
785
+ * and a card whose mode it does not know is one to pass over.
786
+ *
787
+ * Two branches rather than a bare string, so the known values cross into
788
+ * the exported document as a list a client can switch over, beside the
789
+ * branch that reads any other word (`OpenWordSchema`). A declared field's
790
+ * type, inside params and result, stays a closed list: a card with a type
791
+ * a reader does not know is one it cannot fill in, and it is passed over
792
+ * like a card of an unknown mode.
793
+ */
794
+ fulfillment: z.union([FulfillmentSchema, OpenWordSchema]),
795
+ /** On "async" and "confirm": how long the merchant has to deliver, in seconds. */
796
+ fulfill_deadline_seconds: z.int().positive().optional(),
797
+ /** On "confirm": how long the merchant has to say yes, in seconds. */
798
+ confirm_deadline_seconds: z.int().positive().optional(),
799
+ /**
800
+ * On "ship": how long the merchant has to hand the parcel to a carrier,
801
+ * counted from the charge, in seconds. When it leaves, not when it arrives.
802
+ */
803
+ ship_within_seconds: z.int().positive().optional(),
804
+ })
646
805
  .meta({
647
806
  // Everything below is written in prose above as well, and it has to be
648
807
  // written twice: the reader this matters most to is the one holding the
@@ -650,7 +809,7 @@ export const PublicCardSchema = z
650
809
  // money on what this card claims. `as_of` in particular means something
651
810
  // narrower here than the same name means elsewhere in this contract, and a
652
811
  // reader who assumed otherwise would trust a stale number.
653
- description: "A product an agent can buy, projected from the card its merchant published. as_of is when the price shown here was published, and nothing more: on a card whose price is checked at purchase it says nothing about how fresh that check will be — elsewhere in this contract the same name means the moment a live answer was true. price_checked_at_purchase says the merchant is asked for a price at the moment of purchase, not that they answer; what happens when they are silent depends on the mode and belongs to the gateway. The number above is what an agent compares when choosing and may not be what the sale goes through at. Two rules hold beyond the shape: a synchronous product names no delivery deadline, because it is delivered inside a response budget that is the same for every product on the platform, and only a product whose merchant is asked to confirm names a confirmation deadline. No field here names who is selling: this contract has no shape for a merchant's public identity.",
812
+ description: "A product an agent can buy, projected from the card its merchant published. as_of is when the price shown here was published, and nothing more: on a card whose price is checked at purchase it says nothing about how fresh that check will be — elsewhere in this contract the same name means the moment a live answer was true. price_checked_at_purchase says the merchant is asked for a price at the moment of purchase, not that they answer; what happens when they are silent depends on the mode and belongs to the gateway. The number above is what an agent compares when choosing and may not be what the sale goes through at. Three rules hold beyond the shape: a synchronous product names no delivery deadline, because it is delivered inside a response budget that is the same for every product on the platform; only a product whose merchant is asked to confirm names a confirmation deadline; and a parcel, fulfillment \"ship\", names ship_within_seconds — the time to hand it to a carrier counted from the charge, not the time it arrives — and no result, because what the agent receives is the record of its shipment, while every other product names a result. On a parcel the price shown is the goods without shipping, and the purchase goes through at the merchant's answer for the whole, shipping to the buyer's place included. seller is who sells, as the merchant gave it: Agentify did not check the name or the site. fulfillment is a word whose known values are listed beside it, and more may be added: a reader keeps a default arm, and a card whose mode it does not know is one to pass over, not a reason to stop reading the catalog. This document and every part inside it may also gain fields; a reader ignores the ones it does not know. The type of a declared field in params and result is a closed list, and a card with a type a reader does not know is one to pass over too.",
654
813
  });
655
814
  /**
656
815
  * The card as an agent reads it, built from the card the merchant published.
@@ -662,18 +821,20 @@ export const PublicCardSchema = z
662
821
  * every agent by default, and the first anybody heard of it would be a
663
822
  * merchant's pricing address in a public catalog.
664
823
  *
665
- * The two things the card cannot know are passed in: the catalog identifier we
666
- * issued when it was published, and the moment its price was published.
824
+ * The three things the card cannot know are passed in: the catalog identifier
825
+ * we issued when it was published, the moment its price was published, and
826
+ * who sells it as their merchant stands now.
667
827
  */
668
828
  export const publicCardOf = (card, issued) => {
669
829
  const common = {
670
830
  id: issued.id,
831
+ seller: issued.seller,
671
832
  title: card.title,
672
833
  description: card.description,
673
834
  price: card.price,
674
835
  as_of: issued.as_of,
675
836
  ...(card.params === undefined ? {} : { params: card.params }),
676
- result: card.result,
837
+ ...(card.result === undefined ? {} : { result: card.result }),
677
838
  price_checked_at_purchase: card.price_check !== undefined,
678
839
  };
679
840
  switch (card.fulfillment) {
@@ -698,6 +859,14 @@ export const publicCardOf = (card, issued) => {
698
859
  ? {}
699
860
  : { fulfill_deadline_seconds: card.fulfill_deadline_seconds }),
700
861
  };
862
+ case "ship":
863
+ return {
864
+ ...common,
865
+ fulfillment: "ship",
866
+ ...(card.ship_within_seconds === undefined
867
+ ? {}
868
+ : { ship_within_seconds: card.ship_within_seconds }),
869
+ };
701
870
  }
702
871
  };
703
872
  /**
@@ -732,9 +901,40 @@ const exampleOf = (spec) => Object.fromEntries(Object.entries(spec).map(([name,
732
901
  * inside another schema, so that one line is dropped here.
733
902
  */
734
903
  const purchaseBodySchemaOf = (card) => {
735
- const { $schema, ...body } = z.toJSONSchema(z.strictObject({ params: purchaseCheckFor(card) }));
904
+ const { $schema, ...body } = z.toJSONSchema(z.strictObject({
905
+ params: purchaseCheckFor(card),
906
+ // A parcel's purchase carries the address it goes to beside its
907
+ // parameters, and is refused without one (ADR-0032).
908
+ ...(card.fulfillment === "ship" ? { ship_to: ShipToSchema } : {}),
909
+ }));
736
910
  return body;
737
911
  };
912
+ /**
913
+ * Where a parcel goes, in a listing's example purchase: Agentify's own office
914
+ * (ADR-0033), with the number taken out of it, so that the example is a real
915
+ * place and nobody's home.
916
+ */
917
+ const LISTED_SHIP_TO = {
918
+ name: "Agentify",
919
+ line_one: "Jl. Raya Kediri, Beraban",
920
+ line_two: "Nuanu Creative City",
921
+ city: "Tabanan",
922
+ state: "BA",
923
+ postal_code: "82121",
924
+ country: "ID",
925
+ phone_number: "+62 000 0000 0000",
926
+ };
927
+ /**
928
+ * A recorded shipment, in a listing's example output, written as every other
929
+ * example here is: each field standing for what goes there, and the instant
930
+ * at the start of the clock rather than a date that looks like one somebody
931
+ * shipped on.
932
+ */
933
+ const LISTED_SHIPMENT = {
934
+ carrier: "string",
935
+ tracking_number: "string",
936
+ shipped_at: "1970-01-01T00:00:00Z",
937
+ };
738
938
  /**
739
939
  * The card as a discovery catalog reads it.
740
940
  *
@@ -754,9 +954,14 @@ export const bazaarDeclarationOf = (card, listed) => ({
754
954
  ...(listed.serviceName === null ? {} : { serviceName: listed.serviceName }),
755
955
  ...(card.tags === undefined ? {} : { tags: card.tags }),
756
956
  },
757
- input: { params: exampleOf(card.params ?? {}) },
957
+ input: {
958
+ params: exampleOf(card.params ?? {}),
959
+ ...(card.fulfillment === "ship" ? { ship_to: LISTED_SHIP_TO } : {}),
960
+ },
758
961
  inputSchema: purchaseBodySchemaOf(card),
759
- output: { example: exampleOf(card.result) },
962
+ // What the agent is finally handed: the goods the card declared, or for a
963
+ // parcel the record of its shipment.
964
+ output: { example: card.result === undefined ? LISTED_SHIPMENT : exampleOf(card.result) },
760
965
  });
761
966
  /**
762
967
  * One card as the merchant who published it reads it back.
@@ -73,12 +73,35 @@ export declare const WORKER_ENVELOPE_PAYLOADS: Readonly<{
73
73
  as_of: z.ZodISODateTime;
74
74
  }, z.core.$strict>;
75
75
  price_id: z.ZodOptional<z.ZodString>;
76
+ ship_to: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
77
+ name: z.ZodString;
78
+ line_one: z.ZodString;
79
+ line_two: z.ZodOptional<z.ZodString>;
80
+ city: z.ZodString;
81
+ state: z.ZodOptional<z.ZodString>;
82
+ postal_code: z.ZodOptional<z.ZodString>;
83
+ country: z.ZodString;
84
+ phone_number: z.ZodString;
85
+ }, z.core.$strict>, z.ZodObject<{
86
+ country: z.ZodString;
87
+ state: z.ZodOptional<z.ZodString>;
88
+ city: z.ZodString;
89
+ postal_code: z.ZodOptional<z.ZodString>;
90
+ }, z.core.$strict>, z.ZodObject<{
91
+ erased_at: z.ZodISODateTime;
92
+ }, z.core.$strict>]>>;
76
93
  test: z.ZodBoolean;
77
94
  }, z.core.$strict>;
78
95
  /** "How much is this and is it there", asked before a sale goes through. */
79
96
  readonly quote_request: z.ZodObject<{
80
97
  merchant_item_id: z.ZodString;
81
98
  params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
99
+ ship_to: z.ZodOptional<z.ZodObject<{
100
+ country: z.ZodString;
101
+ state: z.ZodOptional<z.ZodString>;
102
+ city: z.ZodString;
103
+ postal_code: z.ZodOptional<z.ZodString>;
104
+ }, z.core.$strict>>;
82
105
  price_id: z.ZodString;
83
106
  purpose: z.ZodEnum<{
84
107
  purchase: "purchase";
@@ -128,6 +151,23 @@ export declare const WorkerEnvelopeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
128
151
  as_of: z.ZodISODateTime;
129
152
  }, z.core.$strict>;
130
153
  price_id: z.ZodOptional<z.ZodString>;
154
+ ship_to: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
155
+ name: z.ZodString;
156
+ line_one: z.ZodString;
157
+ line_two: z.ZodOptional<z.ZodString>;
158
+ city: z.ZodString;
159
+ state: z.ZodOptional<z.ZodString>;
160
+ postal_code: z.ZodOptional<z.ZodString>;
161
+ country: z.ZodString;
162
+ phone_number: z.ZodString;
163
+ }, z.core.$strict>, z.ZodObject<{
164
+ country: z.ZodString;
165
+ state: z.ZodOptional<z.ZodString>;
166
+ city: z.ZodString;
167
+ postal_code: z.ZodOptional<z.ZodString>;
168
+ }, z.core.$strict>, z.ZodObject<{
169
+ erased_at: z.ZodISODateTime;
170
+ }, z.core.$strict>]>>;
131
171
  test: z.ZodBoolean;
132
172
  }, z.core.$strict>;
133
173
  }, z.core.$strict>, z.ZodObject<{
@@ -137,6 +177,12 @@ export declare const WorkerEnvelopeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
137
177
  payload: z.ZodObject<{
138
178
  merchant_item_id: z.ZodString;
139
179
  params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
180
+ ship_to: z.ZodOptional<z.ZodObject<{
181
+ country: z.ZodString;
182
+ state: z.ZodOptional<z.ZodString>;
183
+ city: z.ZodString;
184
+ postal_code: z.ZodOptional<z.ZodString>;
185
+ }, z.core.$strict>>;
140
186
  price_id: z.ZodString;
141
187
  purpose: z.ZodEnum<{
142
188
  purchase: "purchase";