@curless/hotel-storefront 0.23.1 → 0.24.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.
Files changed (93) hide show
  1. package/README.md +28 -11
  2. package/dist/__tests__/__fixtures__/huazhu-fake.d.ts +54 -0
  3. package/dist/__tests__/__fixtures__/huazhu-fake.d.ts.map +1 -0
  4. package/dist/__tests__/__fixtures__/huazhu-fake.js +340 -0
  5. package/dist/__tests__/__fixtures__/huazhu-fake.js.map +1 -0
  6. package/dist/__tests__/huazhu-backend.test.d.ts +2 -0
  7. package/dist/__tests__/huazhu-backend.test.d.ts.map +1 -0
  8. package/dist/__tests__/huazhu-backend.test.js +434 -0
  9. package/dist/__tests__/huazhu-backend.test.js.map +1 -0
  10. package/dist/__tests__/huazhu-cancel-policy.test.d.ts +2 -0
  11. package/dist/__tests__/huazhu-cancel-policy.test.d.ts.map +1 -0
  12. package/dist/__tests__/huazhu-cancel-policy.test.js +82 -0
  13. package/dist/__tests__/huazhu-cancel-policy.test.js.map +1 -0
  14. package/dist/__tests__/huazhu-sellable.test.d.ts +2 -0
  15. package/dist/__tests__/huazhu-sellable.test.d.ts.map +1 -0
  16. package/dist/__tests__/huazhu-sellable.test.js +192 -0
  17. package/dist/__tests__/huazhu-sellable.test.js.map +1 -0
  18. package/dist/__tests__/server.test.js +129 -4
  19. package/dist/__tests__/server.test.js.map +1 -1
  20. package/dist/__tests__/tool-descriptions.test.js +57 -4
  21. package/dist/__tests__/tool-descriptions.test.js.map +1 -1
  22. package/dist/__tests__/tools.test.js +2 -0
  23. package/dist/__tests__/tools.test.js.map +1 -1
  24. package/dist/backend.d.ts +27 -0
  25. package/dist/backend.d.ts.map +1 -1
  26. package/dist/backend.js +7 -0
  27. package/dist/backend.js.map +1 -1
  28. package/dist/card.generated.d.ts +1 -1
  29. package/dist/card.generated.js +2 -2
  30. package/dist/card.generated.js.map +1 -1
  31. package/dist/config.d.ts +52 -4
  32. package/dist/config.d.ts.map +1 -1
  33. package/dist/config.js.map +1 -1
  34. package/dist/facilities.d.ts.map +1 -1
  35. package/dist/facilities.js +7 -5
  36. package/dist/facilities.js.map +1 -1
  37. package/dist/huazhu/backend.d.ts +40 -0
  38. package/dist/huazhu/backend.d.ts.map +1 -0
  39. package/dist/huazhu/backend.js +1318 -0
  40. package/dist/huazhu/backend.js.map +1 -0
  41. package/dist/huazhu/bookings.d.ts +81 -0
  42. package/dist/huazhu/bookings.d.ts.map +1 -0
  43. package/dist/huazhu/bookings.js +189 -0
  44. package/dist/huazhu/bookings.js.map +1 -0
  45. package/dist/huazhu/cancel-policy.d.ts +69 -0
  46. package/dist/huazhu/cancel-policy.d.ts.map +1 -0
  47. package/dist/huazhu/cancel-policy.js +201 -0
  48. package/dist/huazhu/cancel-policy.js.map +1 -0
  49. package/dist/huazhu/catalog.d.ts +75 -0
  50. package/dist/huazhu/catalog.d.ts.map +1 -0
  51. package/dist/huazhu/catalog.js +251 -0
  52. package/dist/huazhu/catalog.js.map +1 -0
  53. package/dist/huazhu/check.d.ts +5 -0
  54. package/dist/huazhu/check.d.ts.map +1 -0
  55. package/dist/huazhu/check.js +170 -0
  56. package/dist/huazhu/check.js.map +1 -0
  57. package/dist/huazhu/client.d.ts +100 -0
  58. package/dist/huazhu/client.d.ts.map +1 -0
  59. package/dist/huazhu/client.js +232 -0
  60. package/dist/huazhu/client.js.map +1 -0
  61. package/dist/huazhu/sellable.d.ts +81 -0
  62. package/dist/huazhu/sellable.d.ts.map +1 -0
  63. package/dist/huazhu/sellable.js +219 -0
  64. package/dist/huazhu/sellable.js.map +1 -0
  65. package/dist/huazhu/wire.d.ts +389 -0
  66. package/dist/huazhu/wire.d.ts.map +1 -0
  67. package/dist/huazhu/wire.js +72 -0
  68. package/dist/huazhu/wire.js.map +1 -0
  69. package/dist/index.js +18 -4
  70. package/dist/index.js.map +1 -1
  71. package/dist/legal.d.ts +4 -3
  72. package/dist/legal.d.ts.map +1 -1
  73. package/dist/legal.js +33 -18
  74. package/dist/legal.js.map +1 -1
  75. package/dist/lib.d.ts +8 -3
  76. package/dist/lib.d.ts.map +1 -1
  77. package/dist/lib.js +8 -1
  78. package/dist/lib.js.map +1 -1
  79. package/dist/server.d.ts +20 -8
  80. package/dist/server.d.ts.map +1 -1
  81. package/dist/server.js +132 -28
  82. package/dist/server.js.map +1 -1
  83. package/dist/surface.d.ts +2 -1
  84. package/dist/surface.d.ts.map +1 -1
  85. package/dist/surface.js +37 -10
  86. package/dist/surface.js.map +1 -1
  87. package/dist/tools.d.ts +33 -20
  88. package/dist/tools.d.ts.map +1 -1
  89. package/dist/tools.js +130 -11
  90. package/dist/tools.js.map +1 -1
  91. package/dist/types.d.ts +21 -2
  92. package/dist/types.d.ts.map +1 -1
  93. package/package.json +1 -1
package/dist/tools.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type BackendCapabilities } from './backend.js';
1
2
  import type { HotelBackend } from './backend.js';
2
3
  import type { ToolNames } from './config.js';
3
4
  import type { Misses } from './limits.js';
@@ -7,7 +8,12 @@ import type { RoomRequestKind } from './types.js';
7
8
  import { type Vocabulary } from './vocab.js';
8
9
  /** How many ${nounPlural} one look-up reads - a city's worth, and what a comparison can hold and then some. */
9
10
  export declare const LOOK_UP_MAX = 6;
10
- export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
11
+ /**
12
+ * The tools this deployment offers: every tool, less the ones its backend
13
+ * cannot do, and with the descriptions of the rest saying only what is true
14
+ * of it - see `BackendCapabilities`.
15
+ */
16
+ export declare const buildTools: (n: ToolNames, v: Vocabulary, caps?: BackendCapabilities) => ({
11
17
  readonly name: `list_${string}${string}`;
12
18
  readonly description: `${string} ${string}, shown as a card. Asked with no place - or a country of many cities - it answers with the places to choose from: regions, countries and cities, each with how many ${string} it has; \`all\` lists the ${string} themselves. \`city\` (a city or a country) or \`query\` answers with the ${string}: a picture, the city and area, a rating where the ${string} publishes one, and a from-price, and a city usually holds several. With \`checkIn\` and \`checkOut\` every ${string} is priced for those nights - its cheapest room that takes the party, \`rooms\` of it, for the whole stay with taxes - or marked as having no room for them. \`sort\` and \`maxNightly\` order and cap by that price, or by the from-price without dates, where the matches share one currency. \`brand\`, \`facilities\`, \`freeCancellation\` and \`breakfast\` narrow the list, and the answer counts what each would leave. A long list comes a page at a time: the answer says how many more there are and the \`offset\` of the next page. Words or a place that match nothing are widened once, and the answer names what it widened to.${string}`;
13
19
  readonly annotations: {
@@ -87,7 +93,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
87
93
  };
88
94
  };
89
95
  };
90
- }, {
96
+ } | {
91
97
  readonly name: `get_${string}${string}`;
92
98
  readonly description: `One ${string} in full: description, amenities, address, check-in and check-out times. Given \`checkIn\` and \`checkOut\` it also returns the room types that can be booked for those nights, each with its nightly rate, the stay total including taxes and fees, whether it can be cancelled free and until when, and how many rooms are left. \`rooms\` leaves out the types that have fewer than that many. Without dates it returns the ${string} only, because a room has no price until the nights are known. The ${string} is drawn as a card for the guest to look at.${string}`;
93
99
  readonly annotations: {
@@ -123,7 +129,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
123
129
  };
124
130
  readonly required: readonly ["hotel"];
125
131
  };
126
- }, {
132
+ } | {
127
133
  readonly name: `look_up_${string}${string}`;
128
134
  readonly description: `Reads up to 6 ${string} - a \`city\`'s (or a country's), or the ones named in \`${string}\` - and returns them as text, with nothing drawn and nothing shown to the guest: each one's id, area, rating, description and amenities, its add-ons with their ids and prices, and, given \`checkIn\` and \`checkOut\`, its rooms priced for those nights with taxes in. The ids are the ones a comparison and a plan take.`;
129
135
  readonly annotations: {
@@ -158,7 +164,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
158
164
  };
159
165
  };
160
166
  };
161
- }, {
167
+ } | {
162
168
  readonly name: `list_${string}extras`;
163
169
  readonly description: `What one ${string} offers beside the room: airport transfers, dinners, guided experiences, spa and family add-ons, each with its price and whether it is charged per stay, per person or per night, drawn as a card for the guest to look at.`;
164
170
  readonly annotations: {
@@ -197,7 +203,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
197
203
  };
198
204
  };
199
205
  };
200
- }, {
206
+ } | {
201
207
  readonly name: `add_to_${string}trip`;
202
208
  readonly description: `Puts room nights or an add-on into the trip and returns the whole trip, priced. A room line needs the ${string}, the room type and the dates; \`rooms\` books more than one of that room type. An add-on line needs the ${string} and the add-on. A line is replaced, not stacked: sending the same room and dates again sets that line's room count, and 0 removes it.`;
203
209
  readonly annotations: {
@@ -245,7 +251,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
245
251
  };
246
252
  readonly required: readonly ["hotel"];
247
253
  };
248
- }, {
254
+ } | {
249
255
  readonly name: `view_${string}trip`;
250
256
  readonly description: "The trip as it stands: every room line and add-on with its own total, the taxes and fees, and the all-in total, laid out day by day on the card.";
251
257
  readonly annotations: {
@@ -259,7 +265,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
259
265
  readonly type: "object";
260
266
  readonly properties: {};
261
267
  };
262
- }, {
268
+ } | {
263
269
  readonly name: `clear_${string}trip`;
264
270
  readonly description: "Empties the trip.";
265
271
  readonly annotations: {
@@ -273,7 +279,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
273
279
  readonly type: "object";
274
280
  readonly properties: {};
275
281
  };
276
- }, {
282
+ } | {
277
283
  readonly name: `request_${string}booking`;
278
284
  readonly description: `Asks ${string} to hold the trip's rooms for a named guest and returns a confirmation code and the moment the hold lapses. No payment is taken and no card is collected; the rooms are held, not paid for. Preferences - a bed type, a floor, a late arrival - ride along on the booking as requests the desk tries to meet.`;
279
285
  readonly annotations: {
@@ -321,7 +327,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
321
327
  };
322
328
  readonly required: readonly ["name", "email"];
323
329
  };
324
- }, {
330
+ } | {
325
331
  readonly name: `list_my_${string}bookings`;
326
332
  readonly description: "The bookings made in this conversation, newest to oldest, with their status, dates and confirmation codes. A booking made elsewhere is added when both its confirmation code and the email it was made with are given.";
327
333
  readonly annotations: {
@@ -348,7 +354,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
348
354
  };
349
355
  };
350
356
  };
351
- }, {
357
+ } | {
352
358
  readonly name: `preview_${string}cancellation`;
353
359
  readonly description: "What cancelling a booking would cost and what would be refunded, with the rule it was worked out from and the free-cancellation deadline where the rate has one. Nothing is cancelled.";
354
360
  readonly annotations: {
@@ -372,7 +378,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
372
378
  };
373
379
  readonly required: readonly ["booking"];
374
380
  };
375
- }, {
381
+ } | {
376
382
  readonly name: `cancel_${string}booking`;
377
383
  readonly description: "Cancels a booking. Called without `confirmed` it returns the cancellation preview - the fee, the refund and the rule behind them - and cancels nothing; it cancels only when `confirmed` is true.";
378
384
  readonly annotations: {
@@ -404,7 +410,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
404
410
  };
405
411
  readonly required: readonly ["booking"];
406
412
  };
407
- }, {
413
+ } | {
408
414
  readonly name: `preview_${string}booking_change`;
409
415
  readonly description: `What moving a booking to other nights, another room type or a different number of rooms - or putting an add-on on it, taking one off or changing how many - would cost: what it holds now, what it would hold, and the difference between them. The new nights are priced at what those nights cost today, not at the rate the booking was made on. A rate that cannot be moved, a ${string} that has no room left for the new nights and a booking that is already gone are each said in those words. Nothing is changed.`;
410
416
  readonly annotations: {
@@ -452,7 +458,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
452
458
  };
453
459
  readonly required: readonly ["booking"];
454
460
  };
455
- }, {
461
+ } | {
456
462
  readonly name: `change_${string}booking`;
457
463
  readonly description: "Moves a booking to other nights, another room type or a different number of rooms, or sets one of its add-ons (`extra` and `quantity`; 0 takes it off). Called without `confirmed` it returns the same preview - the two stays and the difference - and changes nothing; it changes only when `confirmed` is true. The confirmation code stays the same.";
458
464
  readonly annotations: {
@@ -504,7 +510,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
504
510
  };
505
511
  readonly required: readonly ["booking"];
506
512
  };
507
- }, {
513
+ } | {
508
514
  readonly name: `compare_${string}${string}`;
509
515
  readonly description: `Puts two to four ${string} side by side as one card. Each column's price, rate, cancellation terms, breakfast and rooms left come from the ${string}'s own record; the arguments carry only the ids, one line on who each suits, and its pros and cons. With \`checkIn\`/\`checkOut\` the columns carry each ${string}'s cheapest room for those nights; without them, a from-price.`;
510
516
  readonly annotations: {
@@ -574,7 +580,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
574
580
  };
575
581
  readonly required: readonly ["entries"];
576
582
  };
577
- }, {
583
+ } | {
578
584
  readonly name: `show_${string}itinerary`;
579
585
  readonly description: `A day-by-day plan as one card: each day carries its own label, an optional note, the moments of the day in order, and the ${string} and add-ons attached to it, with their details filled in from their own records - a ${string} with its photograph and, given dates, the price of its nights. Each stay is marked with whether it is booked, in the trip or only in the plan, and the card says how many of each. Nothing is added to the trip.`;
580
586
  readonly annotations: {
@@ -656,7 +662,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
656
662
  };
657
663
  readonly required: readonly ["title", "days"];
658
664
  };
659
- }, {
665
+ } | {
660
666
  readonly name: `sign_in_${string}member`;
661
667
  readonly description: `Signs a ${string} loyalty member in for this conversation with their membership number and the surname on it, and answers with their tier, their point balance and what the tier takes off the public rate. Room rates and trip totals are then quoted at the member rate until they are signed out.`;
662
668
  readonly annotations: {
@@ -680,7 +686,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
680
686
  };
681
687
  readonly required: readonly ["number", "surname"];
682
688
  };
683
- }, {
689
+ } | {
684
690
  readonly name: `sign_out_${string}member`;
685
691
  readonly description: "Ends the membership sign-in for this conversation. Rates go back to the public ones; the trip and any bookings are left as they are.";
686
692
  readonly annotations: {
@@ -694,7 +700,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
694
700
  readonly type: "object";
695
701
  readonly properties: {};
696
702
  };
697
- }, {
703
+ } | {
698
704
  readonly name: `forget_${string}preferences`;
699
705
  readonly description: `Forgets the room preferences saved on the signed-in ${string} membership - a bed type, a floor, a quiet or connecting room. Bookings already made keep what they asked for.`;
700
706
  readonly annotations: {
@@ -708,7 +714,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
708
714
  readonly type: "object";
709
715
  readonly properties: {};
710
716
  };
711
- }, {
717
+ } | {
712
718
  readonly name: `get_${string}${string}_policies`;
713
719
  readonly description: `What one ${string} publishes about cancellation, check-in and check-out, children and extra beds, pets, deposits and accessibility. With \`topic\` it returns that topic; without, all of them.`;
714
720
  readonly annotations: {
@@ -732,10 +738,17 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
732
738
  };
733
739
  readonly required: readonly ["hotel"];
734
740
  };
735
- }];
741
+ })[];
742
+ /** The tool names a backend with these capabilities leaves out. */
743
+ export declare const withheldTools: (n: ToolNames, caps: BackendCapabilities) => Set<string>;
736
744
  export declare const buildOutputSchemas: (n: ToolNames) => Record<string, unknown>;
737
745
  export type ToolDeps = {
738
746
  backend: HotelBackend;
747
+ /**
748
+ * The tool names this deployment offers, when it offers fewer than all -
749
+ * the card is sent only these, so it draws no button for a missing tool.
750
+ */
751
+ offered?: ReadonlySet<string>;
739
752
  trips: TripStore;
740
753
  members: MemberStore;
741
754
  tools: ToolNames;
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAgBhD,OAAO,EAGL,KAAK,SAAS,EAGf,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAYV,eAAe,EAIhB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAkExD,+GAA+G;AAC/G,eAAO,MAAM,WAAW,IAAI,CAAC;AAE7B,eAAO,MAAM,UAAU,GAAI,GAAG,SAAS,EAAE,GAAG,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6frD,CAAC;AAgFF,eAAO,MAAM,kBAAkB,GAAI,GAAG,SAAS,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAyMtE,CAAC;AAEH,MAAM,MAAM,QAAQ,GAAG;IACrB,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,0FAA0F;IAC1F,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AA8pB/E;;;;GAIG;AACH,eAAO,MAAM,eAAe,IAAI,CAAC;AA+IjC,eAAO,MAAM,UAAU,GACrB,MAAM,QAAQ,EACd,MAAM,MAAM,EACZ,SAAS,OAAO,EAChB,KAAK,WAAW,KACf,OAAO,CAAC,UAAU,CAMpB,CAAC"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
package/dist/tools.js CHANGED
@@ -1,4 +1,4 @@
1
- import { DEFAULT_PAGE, LASTING_REQUESTS, MAX_PAGE, REQUEST_KINDS, REQUEST_NOTE_MAX, } from './backend.js';
1
+ import { DEFAULT_PAGE, FULL_CAPABILITIES, LASTING_REQUESTS, MAX_PAGE, REQUEST_KINDS, REQUEST_NOTE_MAX, } from './backend.js';
2
2
  import { isDay, nights as nightsBetween } from './dates.js';
3
3
  import { FACILITIES } from './facilities.js';
4
4
  import { DESCRIPTION_MAX, POLICY_MAX, hotelText } from './fence.js';
@@ -63,7 +63,69 @@ const cancels = (title, openWorld) => ({
63
63
  const DATE = (what) => ({ type: 'string', description: `${what}, as YYYY-MM-DD` });
64
64
  /** How many ${nounPlural} one look-up reads - a city's worth, and what a comparison can hold and then some. */
65
65
  export const LOOK_UP_MAX = 6;
66
- export const buildTools = (n, v) => {
66
+ /**
67
+ * The tools this deployment offers: every tool, less the ones its backend
68
+ * cannot do, and with the descriptions of the rest saying only what is true
69
+ * of it - see `BackendCapabilities`.
70
+ */
71
+ export const buildTools = (n, v, caps = FULL_CAPABILITIES) => forCapabilities(allTools(n, v), n, v, caps);
72
+ /** The tool names a backend with these capabilities leaves out. */
73
+ export const withheldTools = (n, caps) => new Set([
74
+ ...(caps.members ? [] : [n.signIn, n.signOut, n.forgetPreferences]),
75
+ ...(caps.extras ? [] : [n.listExtras]),
76
+ ...(caps.changes || caps.extras ? [] : [n.previewChange, n.changeBooking]),
77
+ ]);
78
+ const forCapabilities = (tools, n, v, caps) => {
79
+ const withheld = withheldTools(n, caps);
80
+ const without = (schema, ...keys) => {
81
+ const s = schema;
82
+ const properties = { ...(s.properties ?? {}) };
83
+ for (const k of keys)
84
+ delete properties[k];
85
+ return { ...s, properties };
86
+ };
87
+ return tools
88
+ .filter((t) => !withheld.has(t.name))
89
+ .map((t) => {
90
+ let out = t;
91
+ if (!caps.extras) {
92
+ if (t.name === n.addToTrip) {
93
+ out = {
94
+ ...out,
95
+ description: `Puts room nights into the trip and returns the whole trip, priced. A room line needs the ${v.noun}, the room type and the dates; \`rooms\` books more than one of that room type. A line is replaced, not stacked: sending the same room and dates again sets that line's room count, and 0 removes it.`,
96
+ inputSchema: without(out.inputSchema, 'extra', 'quantity'),
97
+ };
98
+ }
99
+ if (t.name === n.viewTrip) {
100
+ out = {
101
+ ...out,
102
+ description: 'The trip as it stands: every room line with its own total, the taxes and fees, and the all-in total, laid out day by day on the card.',
103
+ };
104
+ }
105
+ if (t.name === n.lookUp) {
106
+ out = {
107
+ ...out,
108
+ description: out.description.replace(', its add-ons with their ids and prices', ''),
109
+ };
110
+ }
111
+ if (t.name === n.itinerary) {
112
+ out = JSON.parse(JSON.stringify(out).replaceAll(' and add-ons', ''));
113
+ }
114
+ }
115
+ if (t.name === n.requestBooking) {
116
+ if (!caps.members)
117
+ out = { ...out, inputSchema: without(out.inputSchema, 'rememberRequests') };
118
+ if (caps.booking === 'pay-at-hotel') {
119
+ out = {
120
+ ...out,
121
+ description: `Books the trip's rooms with ${v.brand} for a named guest: each stay becomes the hotel's own reservation, with its own confirmation code, paid at the hotel. No payment is taken here and no card is collected. All of the trip is booked or none of it. Preferences - a bed type, a floor, a late arrival - ride along on the booking as requests the desk tries to meet.`,
122
+ };
123
+ }
124
+ }
125
+ return out;
126
+ });
127
+ };
128
+ const allTools = (n, v) => {
67
129
  const open = !v.demo;
68
130
  const stayProps = {
69
131
  checkIn: DATE('The arrival date'),
@@ -578,6 +640,7 @@ const TRIP_SCHEMA = {
578
640
  view: { enum: ['trip', 'needs-dates', 'error'] },
579
641
  brand: STR,
580
642
  toolNames: OBJ,
643
+ bookingMode: STR,
581
644
  vocab: OBJ,
582
645
  demo: BOOL,
583
646
  demoNote: STR,
@@ -601,6 +664,7 @@ const CANCEL_SCHEMA = {
601
664
  view: { enum: ['cancellation-preview', 'cancelled', 'booking-not-found'] },
602
665
  brand: STR,
603
666
  toolNames: OBJ,
667
+ bookingMode: STR,
604
668
  demo: BOOL,
605
669
  demoNote: STR,
606
670
  booking: OBJ,
@@ -619,6 +683,7 @@ const CHANGE_SCHEMA = {
619
683
  view: { enum: ['change-preview', 'changed', 'booking-not-found'] },
620
684
  brand: STR,
621
685
  toolNames: OBJ,
686
+ bookingMode: STR,
622
687
  demo: BOOL,
623
688
  demoNote: STR,
624
689
  booking: OBJ,
@@ -641,6 +706,7 @@ export const buildOutputSchemas = (n) => ({
641
706
  view: { enum: ['hotels', 'no-hotels', 'destinations'] },
642
707
  brand: STR,
643
708
  toolNames: OBJ,
709
+ bookingMode: STR,
644
710
  vocab: OBJ,
645
711
  demo: BOOL,
646
712
  demoNote: STR,
@@ -669,6 +735,7 @@ export const buildOutputSchemas = (n) => ({
669
735
  view: { enum: ['hotel', 'no-rooms', 'hotel-not-found'] },
670
736
  brand: STR,
671
737
  toolNames: OBJ,
738
+ bookingMode: STR,
672
739
  vocab: OBJ,
673
740
  demo: BOOL,
674
741
  demoNote: STR,
@@ -693,6 +760,7 @@ export const buildOutputSchemas = (n) => ({
693
760
  view: { enum: ['extras', 'hotel-not-found'] },
694
761
  brand: STR,
695
762
  toolNames: OBJ,
763
+ bookingMode: STR,
696
764
  demo: BOOL,
697
765
  demoNote: STR,
698
766
  hotel: OBJ,
@@ -709,6 +777,7 @@ export const buildOutputSchemas = (n) => ({
709
777
  view: { enum: ['booking', 'needs-guest', 'empty-trip', 'error'] },
710
778
  brand: STR,
711
779
  toolNames: OBJ,
780
+ bookingMode: STR,
712
781
  demo: BOOL,
713
782
  demoNote: STR,
714
783
  booking: OBJ,
@@ -729,6 +798,7 @@ export const buildOutputSchemas = (n) => ({
729
798
  view: { const: 'bookings' },
730
799
  brand: STR,
731
800
  toolNames: OBJ,
801
+ bookingMode: STR,
732
802
  demo: BOOL,
733
803
  demoNote: STR,
734
804
  bookings: ARR,
@@ -746,6 +816,7 @@ export const buildOutputSchemas = (n) => ({
746
816
  view: { enum: ['comparison', 'nothing-to-compare'] },
747
817
  brand: STR,
748
818
  toolNames: OBJ,
819
+ bookingMode: STR,
749
820
  demo: BOOL,
750
821
  demoNote: STR,
751
822
  title: STR,
@@ -764,6 +835,7 @@ export const buildOutputSchemas = (n) => ({
764
835
  view: { enum: ['itinerary', 'empty-itinerary'] },
765
836
  brand: STR,
766
837
  toolNames: OBJ,
838
+ bookingMode: STR,
767
839
  demo: BOOL,
768
840
  demoNote: STR,
769
841
  title: STR,
@@ -782,6 +854,7 @@ export const buildOutputSchemas = (n) => ({
782
854
  view: { enum: ['member', 'member-not-found'] },
783
855
  brand: STR,
784
856
  toolNames: OBJ,
857
+ bookingMode: STR,
785
858
  demo: BOOL,
786
859
  demoNote: STR,
787
860
  member: OBJ,
@@ -799,6 +872,7 @@ export const buildOutputSchemas = (n) => ({
799
872
  view: { enum: ['member', 'no-member'] },
800
873
  brand: STR,
801
874
  toolNames: OBJ,
875
+ bookingMode: STR,
802
876
  member: OBJ,
803
877
  },
804
878
  required: ['view'],
@@ -811,6 +885,7 @@ export const buildOutputSchemas = (n) => ({
811
885
  view: { enum: ['look-up', 'nothing-found'] },
812
886
  brand: STR,
813
887
  toolNames: OBJ,
888
+ bookingMode: STR,
814
889
  demo: BOOL,
815
890
  hotels: ARR,
816
891
  stay: OBJ,
@@ -825,6 +900,7 @@ export const buildOutputSchemas = (n) => ({
825
900
  view: { enum: ['policy', 'policies', 'policy-missing', 'hotel-not-found'] },
826
901
  brand: STR,
827
902
  toolNames: OBJ,
903
+ bookingMode: STR,
828
904
  demo: BOOL,
829
905
  hotel: OBJ,
830
906
  topic: STR,
@@ -872,10 +948,32 @@ const memberBadge = (deps, key) => {
872
948
  }
873
949
  : { signedIn: false };
874
950
  };
951
+ /**
952
+ * What was made, in one sentence - which depends on what asking MAKES here.
953
+ *
954
+ * ⚠️ HELD IS NOT BOOKED, AND BOOKED IS NOT PAID. The demonstration holds rooms
955
+ * until a moment and then lets them go; a group's own system makes its own
956
+ * reservation, kept until cancelled and paid at the hotel. Saying "held until"
957
+ * of a reservation invents a deadline, and saying "booked" of a hold invents a
958
+ * room - and a booking whose confirmation has not come back yet is neither,
959
+ * and is said as exactly that.
960
+ */
961
+ const bookedLine = (deps, b) => {
962
+ const total = formatMoney(b.quote.total);
963
+ if ((deps.backend.capabilities ?? FULL_CAPABILITIES).booking === 'pay-at-hotel') {
964
+ return b.status === 'requested'
965
+ ? `${total} all in, to be paid at the hotel. ${deps.vocab.brand} has not confirmed this booking yet - that takes up to eight minutes, and ${deps.tools.listBookings} shows when it has. Nothing has been charged.`
966
+ : `${total} all in, paid at the hotel. **Nothing has been charged here** - this is ${deps.vocab.brand}'s own reservation, kept until it is cancelled, and ${b.guest.name} pays at check-in.`;
967
+ }
968
+ return `${total} all in. **Nothing has been charged** - the rooms are held until ${b.holdUntil?.slice(0, 16).replace('T', ' ')} UTC, and ${b.guest.name} presents a card at check-in.`;
969
+ };
875
970
  const common = (deps, key) => ({
876
971
  brand: deps.vocab.brand,
877
972
  ...(deps.logoUrl ? { logoUrl: deps.logoUrl } : {}),
878
- toolNames: deps.tools,
973
+ toolNames: deps.offered
974
+ ? Object.fromEntries(Object.entries(deps.tools).filter(([, name]) => deps.offered?.has(name)))
975
+ : deps.tools,
976
+ bookingMode: (deps.backend.capabilities ?? FULL_CAPABILITIES).booking,
879
977
  vocab: { noun: deps.vocab.noun, nounPlural: deps.vocab.nounPlural },
880
978
  ...(deps.vocab.demo ? { demo: true, demoNote: DEMO_NOTE } : { demo: false }),
881
979
  ...(key ? memberBadge(deps, key) : {}),
@@ -1882,8 +1980,14 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
1882
1980
  // which narrowing would make the question answerable.
1883
1981
  const oneCurrency = answer.currencies.length === 1;
1884
1982
  const wantedPrice = sort === 'price-low' || sort === 'price-high';
1885
- const applied = wantedPrice && !oneCurrency ? 'group' : sort;
1983
+ const applied = (wantedPrice && !oneCurrency) || answer.ignored?.sort ? 'group' : sort;
1984
+ // Room terms the backend could not apply are not "filtered to".
1985
+ const filtersSaid = asked.filters && answer.ignored?.roomTerms
1986
+ ? { ...asked.filters, freeCancellation: undefined, breakfast: undefined }
1987
+ : asked.filters;
1886
1988
  const priceNotes = [
1989
+ // What the backend was asked and did not do, in its own words.
1990
+ ...(answer.unapplied ?? []),
1887
1991
  wantedPrice && !oneCurrency
1888
1992
  ? `These are quoted in ${answer.currencies.length} currencies (${answer.currencies.join(', ')}), and no rate is applied here, so they are not in price order — one city or one country gives one currency.`
1889
1993
  : '',
@@ -1893,7 +1997,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
1893
1997
  answer.overBudget && hotels.length
1894
1998
  ? `${answer.overBudget} more matched and came in over the ceiling.`
1895
1999
  : '',
1896
- asked.filters ? `Filtered to ${filterWords(asked.filters)}.` : '',
2000
+ filtersSaid && filterWords(filtersSaid) ? `Filtered to ${filterWords(filtersSaid)}.` : '',
1897
2001
  // What the prices on a dated list are, in one line - and how many cannot
1898
2002
  // be had at all, so a sold-out hotel is not described as a choice.
1899
2003
  ask.stay
@@ -2054,7 +2158,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
2054
2158
  },
2055
2159
  text: [
2056
2160
  `**${h.name}** · ${enough.length} room ${enough.length === 1 ? 'type' : 'types'} for ${stay.checkIn} to ${stay.checkOut}, ${stay.guests} guests${roomsWanted > 1 ? `, ${roomsWanted} rooms` : ''}.`,
2057
- ...enough.map((r) => `- ${r.name} — ${formatMoney(r.nightly)} a night, ${formatMoney(r.stayTotal)} for the stay incl. taxes and fees${roomsWanted > 1 ? ` (${formatMoney(multiplyMoney(r.stayTotal, roomsWanted))} for ${roomsWanted} rooms)` : ''}${r.freeCancellationUntil ? `, free to cancel until ${r.freeCancellationUntil.slice(0, 10)}` : r.refundable ? ', the free cancellation window has closed - cancelling costs a night' : ', non-refundable'}`),
2161
+ ...enough.map((r) => `- ${r.name} — ${formatMoney(r.nightly)} a night, ${formatMoney(r.stayTotal)} for the stay incl. taxes and fees${roomsWanted > 1 ? ` (${formatMoney(multiplyMoney(r.stayTotal, roomsWanted))} for ${roomsWanted} rooms)` : ''}${r.freeCancellationUntil ? `, free to cancel until ${r.freeCancellationUntil.slice(0, 10)}` : r.refundable ? ', the free cancellation window has closed - cancelling now has a fee' : ', non-refundable'}`),
2058
2162
  flex.text,
2059
2163
  deps.vocab.demo ? DEMO_NOTE : '',
2060
2164
  ]
@@ -2270,7 +2374,9 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
2270
2374
  error: 'no-such-extra',
2271
2375
  query: extraRef,
2272
2376
  },
2273
- text: `${detail.hotel.name} does not offer that. ${t.listExtras} lists what it has.`,
2377
+ text: (deps.backend.capabilities ?? FULL_CAPABILITIES).extras
2378
+ ? `${detail.hotel.name} does not offer that. ${t.listExtras} lists what it has.`
2379
+ : `${deps.vocab.brand} ${deps.vocab.nounPlural} sell rooms only here - there are no add-ons.`,
2274
2380
  };
2275
2381
  }
2276
2382
  const line = {
@@ -2469,11 +2575,11 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
2469
2575
  },
2470
2576
  text: [
2471
2577
  several
2472
- ? `${booked.length} bookings, one for each stay - each hotel holds its own nights under its own code, and each is changed or cancelled on its own.`
2578
+ ? `${booked.length} bookings, one for each stay - each hotel keeps its own nights under its own code, and each is ${(deps.backend.capabilities ?? FULL_CAPABILITIES).changes ? 'changed or cancelled' : 'cancelled'} on its own.`
2473
2579
  : '',
2474
2580
  ...booked.flatMap(({ booking, asked }) => [
2475
2581
  `**${booking.confirmationCode}** · ${booking.hotelName} · ${booking.stay.checkIn} to ${booking.stay.checkOut}`,
2476
- `${formatMoney(booking.quote.total)} all in. **Nothing has been charged** - the rooms are held until ${booking.holdUntil?.slice(0, 16).replace('T', ' ')} UTC, and ${booking.guest.name} presents a card at check-in.`,
2582
+ bookedLine(deps, booking),
2477
2583
  ...asked,
2478
2584
  ]),
2479
2585
  remembered?.line ?? '',
@@ -2737,18 +2843,23 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
2737
2843
  const cancelled = await deps.backend.cancelBooking(ref, who, str(args.reason) || undefined);
2738
2844
  if (!cancelled)
2739
2845
  return asPreview;
2846
+ // ⚠️ WHAT THE HOTEL SAYS IT CHARGED, once it has said. A group's own system
2847
+ // answers a cancellation with its penalty; the preview was our reading of
2848
+ // the rate's terms, and where the two differ the hotel's is the one that
2849
+ // will be charged.
2850
+ const charged = cancelled.cancellationFee ?? preview.fee;
2740
2851
  return {
2741
2852
  structured: {
2742
2853
  view: 'cancelled',
2743
2854
  ...common(deps, key),
2744
2855
  booking: { ...cancelled, quote: quoteForCard(cancelled.quote) },
2745
- fee: formatMoney(preview.fee),
2856
+ fee: formatMoney(charged),
2746
2857
  refund: formatMoney(preview.refund),
2747
2858
  rule: preview.rule,
2748
2859
  cancellable: false,
2749
2860
  },
2750
2861
  text: [
2751
- `${cancelled.confirmationCode} is cancelled. ${preview.fee.minor === 0 ? 'No fee.' : `The hotel charges ${formatMoney(preview.fee)}.`}`,
2862
+ `${cancelled.confirmationCode} is cancelled. ${charged.minor === 0 ? 'No fee.' : `The hotel charges ${formatMoney(charged)}.`}`,
2752
2863
  deps.vocab.demo ? DEMO_NOTE : '',
2753
2864
  ]
2754
2865
  .filter(Boolean)
@@ -2832,10 +2943,18 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
2832
2943
  // with one room actually added looks exactly like a booking unless
2833
2944
  // somebody says otherwise, and this counts it rather than describing it.
2834
2945
  const gap = planGap(plan);
2946
+ // Each stay as the trip takes it, for a guest who asks in the chat to put
2947
+ // the plan in the trip: without the room, the model opened the hotel to
2948
+ // find one - another card - before it could add anything.
2949
+ const stays = plan.days
2950
+ .flatMap((day) => day.items)
2951
+ .filter((x) => x.kind === 'hotel' && (x.night ?? 1) === 1 && x.checkIn && x.roomId)
2952
+ .map((x) => `${x.name} (\`${x.id}\`) ${x.checkIn} to ${x.checkOut}, room \`${x.roomId}\`${x.booked ? ` - booked, ${x.booked}` : x.inTrip ? ' - in the trip' : ''}`);
2835
2953
  return {
2836
2954
  structured: { view: 'itinerary', ...common(deps, key), ...plan, gap },
2837
2955
  text: [
2838
2956
  `**${plan.title}** · ${plan.days.length} ${plan.days.length === 1 ? 'day' : 'days'}. ${gap}`,
2957
+ stays.length ? `Stays, priced on: ${stays.join('; ')}.` : '',
2839
2958
  ...plan.dropped.map((d) => `Left out: ${d.name} — ${d.why}`),
2840
2959
  deps.vocab.demo ? DEMO_NOTE : '',
2841
2960
  ]