@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.
- package/README.md +28 -11
- package/dist/__tests__/__fixtures__/huazhu-fake.d.ts +54 -0
- package/dist/__tests__/__fixtures__/huazhu-fake.d.ts.map +1 -0
- package/dist/__tests__/__fixtures__/huazhu-fake.js +340 -0
- package/dist/__tests__/__fixtures__/huazhu-fake.js.map +1 -0
- package/dist/__tests__/huazhu-backend.test.d.ts +2 -0
- package/dist/__tests__/huazhu-backend.test.d.ts.map +1 -0
- package/dist/__tests__/huazhu-backend.test.js +434 -0
- package/dist/__tests__/huazhu-backend.test.js.map +1 -0
- package/dist/__tests__/huazhu-cancel-policy.test.d.ts +2 -0
- package/dist/__tests__/huazhu-cancel-policy.test.d.ts.map +1 -0
- package/dist/__tests__/huazhu-cancel-policy.test.js +82 -0
- package/dist/__tests__/huazhu-cancel-policy.test.js.map +1 -0
- package/dist/__tests__/huazhu-sellable.test.d.ts +2 -0
- package/dist/__tests__/huazhu-sellable.test.d.ts.map +1 -0
- package/dist/__tests__/huazhu-sellable.test.js +192 -0
- package/dist/__tests__/huazhu-sellable.test.js.map +1 -0
- package/dist/__tests__/server.test.js +129 -4
- package/dist/__tests__/server.test.js.map +1 -1
- package/dist/__tests__/tool-descriptions.test.js +57 -4
- package/dist/__tests__/tool-descriptions.test.js.map +1 -1
- package/dist/__tests__/tools.test.js +2 -0
- package/dist/__tests__/tools.test.js.map +1 -1
- package/dist/backend.d.ts +27 -0
- package/dist/backend.d.ts.map +1 -1
- package/dist/backend.js +7 -0
- package/dist/backend.js.map +1 -1
- package/dist/card.generated.d.ts +1 -1
- package/dist/card.generated.js +2 -2
- package/dist/card.generated.js.map +1 -1
- package/dist/config.d.ts +52 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/facilities.d.ts.map +1 -1
- package/dist/facilities.js +7 -5
- package/dist/facilities.js.map +1 -1
- package/dist/huazhu/backend.d.ts +40 -0
- package/dist/huazhu/backend.d.ts.map +1 -0
- package/dist/huazhu/backend.js +1318 -0
- package/dist/huazhu/backend.js.map +1 -0
- package/dist/huazhu/bookings.d.ts +81 -0
- package/dist/huazhu/bookings.d.ts.map +1 -0
- package/dist/huazhu/bookings.js +189 -0
- package/dist/huazhu/bookings.js.map +1 -0
- package/dist/huazhu/cancel-policy.d.ts +69 -0
- package/dist/huazhu/cancel-policy.d.ts.map +1 -0
- package/dist/huazhu/cancel-policy.js +201 -0
- package/dist/huazhu/cancel-policy.js.map +1 -0
- package/dist/huazhu/catalog.d.ts +75 -0
- package/dist/huazhu/catalog.d.ts.map +1 -0
- package/dist/huazhu/catalog.js +251 -0
- package/dist/huazhu/catalog.js.map +1 -0
- package/dist/huazhu/check.d.ts +5 -0
- package/dist/huazhu/check.d.ts.map +1 -0
- package/dist/huazhu/check.js +170 -0
- package/dist/huazhu/check.js.map +1 -0
- package/dist/huazhu/client.d.ts +100 -0
- package/dist/huazhu/client.d.ts.map +1 -0
- package/dist/huazhu/client.js +232 -0
- package/dist/huazhu/client.js.map +1 -0
- package/dist/huazhu/sellable.d.ts +81 -0
- package/dist/huazhu/sellable.d.ts.map +1 -0
- package/dist/huazhu/sellable.js +219 -0
- package/dist/huazhu/sellable.js.map +1 -0
- package/dist/huazhu/wire.d.ts +389 -0
- package/dist/huazhu/wire.d.ts.map +1 -0
- package/dist/huazhu/wire.js +72 -0
- package/dist/huazhu/wire.js.map +1 -0
- package/dist/index.js +18 -4
- package/dist/index.js.map +1 -1
- package/dist/legal.d.ts +4 -3
- package/dist/legal.d.ts.map +1 -1
- package/dist/legal.js +33 -18
- package/dist/legal.js.map +1 -1
- package/dist/lib.d.ts +8 -3
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +8 -1
- package/dist/lib.js.map +1 -1
- package/dist/server.d.ts +20 -8
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +132 -28
- package/dist/server.js.map +1 -1
- package/dist/surface.d.ts +2 -1
- package/dist/surface.d.ts.map +1 -1
- package/dist/surface.js +37 -10
- package/dist/surface.js.map +1 -1
- package/dist/tools.d.ts +33 -20
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +130 -11
- package/dist/tools.js.map +1 -1
- package/dist/types.d.ts +21 -2
- package/dist/types.d.ts.map +1 -1
- 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
|
-
|
|
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;
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
|
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
|
-
|
|
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(
|
|
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. ${
|
|
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
|
]
|