@curless/hotel-storefront 0.48.1 → 0.49.1
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/backend.d.ts.map +1 -1
- 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/demo-hotels.d.ts +7 -0
- package/dist/demo-hotels.d.ts.map +1 -1
- package/dist/demo-hotels.js +167 -79
- package/dist/demo-hotels.js.map +1 -1
- package/dist/fence.d.ts +10 -0
- package/dist/fence.d.ts.map +1 -1
- package/dist/fence.js +15 -0
- package/dist/fence.js.map +1 -1
- package/dist/huazhu/backend.d.ts.map +1 -1
- package/dist/huazhu/backend.js +21 -51
- package/dist/huazhu/backend.js.map +1 -1
- package/dist/huazhu/bookings.d.ts.map +1 -1
- package/dist/huazhu/bookings.js +1 -1
- package/dist/huazhu/bookings.js.map +1 -1
- package/dist/huazhu/catalog.d.ts +0 -1
- package/dist/huazhu/catalog.d.ts.map +1 -1
- package/dist/huazhu/catalog.js +7 -28
- package/dist/huazhu/catalog.js.map +1 -1
- package/dist/listing.d.ts +5 -1
- package/dist/listing.d.ts.map +1 -1
- package/dist/listing.js +6 -2
- package/dist/listing.js.map +1 -1
- package/dist/present.d.ts +32 -0
- package/dist/present.d.ts.map +1 -1
- package/dist/present.js +48 -2
- package/dist/present.js.map +1 -1
- package/dist/same-booking.d.ts +33 -0
- package/dist/same-booking.d.ts.map +1 -0
- package/dist/same-booking.js +74 -0
- package/dist/same-booking.js.map +1 -0
- package/dist/server.d.ts +2 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +10 -4
- package/dist/server.js.map +1 -1
- package/dist/sessions.d.ts +26 -1
- package/dist/sessions.d.ts.map +1 -1
- package/dist/sessions.js +35 -2
- package/dist/sessions.js.map +1 -1
- package/dist/tools.d.ts +21 -3
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +155 -42
- package/dist/tools.js.map +1 -1
- package/dist/trip.d.ts +0 -11
- package/dist/trip.d.ts.map +1 -1
- package/dist/trip.js +21 -6
- package/dist/trip.js.map +1 -1
- package/package.json +1 -1
package/dist/tools.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export declare const LOOK_UP_MAX = 6;
|
|
|
15
15
|
*/
|
|
16
16
|
export declare const buildTools: (n: ToolNames, v: Vocabulary, caps?: BackendCapabilities) => ({
|
|
17
17
|
readonly name: `list_${string}${string}`;
|
|
18
|
-
readonly description: `${string}, shown as a card for a guest to look through and pick one ${string} to book.
|
|
18
|
+
readonly description: `${string}, shown as a card for a guest to look through and pick one ${string} to book. It holds no plan of days: a day-by-day plan (\u884C\u7A0B, \u5B89\u6392, an itinerary) is a card of its own, laid out by the days, with a ${string} for each part of the trip read up as text. 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\`, \`breakfast\` and \`bed\` 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}`;
|
|
19
19
|
readonly annotations: {
|
|
20
20
|
readonly title: string;
|
|
21
21
|
readonly readOnlyHint: true;
|
|
@@ -178,7 +178,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary, caps?: BackendCap
|
|
|
178
178
|
};
|
|
179
179
|
} | {
|
|
180
180
|
readonly name: `look_up_${string}${string}`;
|
|
181
|
-
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.
|
|
181
|
+
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. Nothing it reads is shown, so the ${string} of a day-by-day plan - for each part of the trip, those near where its days are spent - can be read up before the plan is drawn; its ids are the ones a comparison and a plan take.`;
|
|
182
182
|
readonly annotations: {
|
|
183
183
|
readonly title: string;
|
|
184
184
|
readonly readOnlyHint: true;
|
|
@@ -663,7 +663,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary, caps?: BackendCap
|
|
|
663
663
|
};
|
|
664
664
|
} | {
|
|
665
665
|
readonly name: `show_${string}itinerary`;
|
|
666
|
-
readonly description: `A day-by-day plan (\u884C\u7A0B) as one card - the card for a trip of several days, or for a guest
|
|
666
|
+
readonly description: `A day-by-day plan (\u884C\u7A0B) as one card - the card for a trip of several days, or for days a guest asked to have planned (a city with a span of dates and \u884C\u7A0B is one): the days are laid out by where they are spent, and each part of the trip has a ${string} near those days, chosen from the ${string} read up as text. A week in a big city is usually two or three stays: its far-out sights fall on neighbouring days, and those nights have a ${string} near them, with the reason in that day's \`why\` (Beijing's Summer Palace, Yuanmingyuan, Fragrant Hills and the Peking and Tsinghua campuses - \u9890\u548C\u56ED, \u5706\u660E\u56ED, \u9999\u5C71, \u5317\u5927\u6E05\u534E - are days side by side in the north-west); a day out of town, such as the Great Wall, is a day of its own, there and back; one ${string} for the whole week suits days that are all within about 45 minutes of it. Nothing here knows a sight's opening days: each day is drawn as it is given, with its date (many museums close on Mondays, the Palace Museum - \u6545\u5BAB - among them). 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.`;
|
|
667
667
|
readonly annotations: {
|
|
668
668
|
readonly title: string;
|
|
669
669
|
readonly readOnlyHint: true;
|
|
@@ -1079,6 +1079,14 @@ export type ToolContext = {
|
|
|
1079
1079
|
tripKey?: string;
|
|
1080
1080
|
/** The client's network address, as the edge proxy saw it - for the booking limits only. */
|
|
1081
1081
|
ip?: string;
|
|
1082
|
+
/**
|
|
1083
|
+
* The client said at `initialize` that it shows no cards (`cardSupport` in
|
|
1084
|
+
* sessions.ts). An answer whose card carries what the model would say - the
|
|
1085
|
+
* plan, the comparison - then says that nothing was drawn and carries it in
|
|
1086
|
+
* words. Absent - a card host, or a session from before a restart - every
|
|
1087
|
+
* answer is as it always was.
|
|
1088
|
+
*/
|
|
1089
|
+
noCards?: boolean;
|
|
1082
1090
|
};
|
|
1083
1091
|
export type ToolAnswer = {
|
|
1084
1092
|
structured: Record<string, unknown>;
|
|
@@ -1155,4 +1163,14 @@ export declare const handleTool: (deps: ToolDeps, name: string, rawArgs: unknown
|
|
|
1155
1163
|
* of a long stay at one hotel. It is in every plan's answer now.
|
|
1156
1164
|
*/
|
|
1157
1165
|
export declare const PLAN_DRAWN_ONCE = "The plan on the card is this one. Something to change that you notice now - a sight closed on its day, a day too far from its hotel - goes in your reply, not in another plan: the plan is drawn again only when the guest asks for the change.";
|
|
1166
|
+
/**
|
|
1167
|
+
* The same place in the answer, for a client that shows no cards.
|
|
1168
|
+
*
|
|
1169
|
+
* ⚠️ NO CARD, SO THE REPLY IS THE PLAN (总线程, 2026-10-28, a client whose
|
|
1170
|
+
* `initialize` declared nothing): it was told "The plan on the card is this
|
|
1171
|
+
* one", and a model told that leaves the plan to a card the guest never sees.
|
|
1172
|
+
*/
|
|
1173
|
+
export declare const PLAN_NOT_DRAWN = "Nothing was drawn: this app shows no cards, so the guest has seen none of this plan. Tell it to them in your reply, day by day - where each day is spent, and each stay with its hotel, dates, price and terms as above. Something to change that you notice now - a sight closed on its day, a day too far from its hotel - is said in that reply, not planned again: the plan is made again only when the guest asks for the change.";
|
|
1174
|
+
/** A comparison's answer, for a client that shows no cards - see `PLAN_NOT_DRAWN`. */
|
|
1175
|
+
export declare const COMPARISON_NOT_DRAWN = "Nothing was drawn: this app shows no cards, so the guest has seen none of this comparison. Compare the hotels in your reply, with each one's price and terms as given here - each is its cheapest room that sleeps the party.";
|
|
1158
1176
|
//# sourceMappingURL=tools.d.ts.map
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,mBAAmB,EASzB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAa,YAAY,EAAgB,MAAM,cAAc,CAAC;AAC1E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAK7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AA+BhD,OAAO,EAGL,KAAK,SAAS,EAKf,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAEV,cAAc,EAkBd,eAAe,EAKhB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAa,KAAK,UAAU,EAAwC,MAAM,YAAY,CAAC;AAuF9F,+GAA+G;AAC/G,eAAO,MAAM,WAAW,IAAI,CAAC;AAE7B;;;;GAIG;AACH,eAAO,MAAM,UAAU,GACrB,GAAG,SAAS,EACZ,GAAG,UAAU,EACb,OAAM,mBAAuC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IACC,CAAC;AAEjD,mEAAmE;AACnE;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,EAC7E,GAAG,CAAC,EACJ,OAAO;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,KAC7C,CAWF,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,GAAG,SAAS,EAAE,MAAM,mBAAmB,KAAG,GAAG,CAAC,MAAM,CAO9E,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,7 +1,7 @@
|
|
|
1
1
|
import { DEFAULT_PAGE, FULL_CAPABILITIES, LASTING_REQUESTS, MAX_PAGE, REQUEST_KINDS, REQUEST_NOTE_MAX, guestTooLong, unitWords, } from './backend.js';
|
|
2
2
|
import { MAX_GUESTS, isDay, nights as nightsBetween } from './dates.js';
|
|
3
3
|
import { FACILITIES } from './facilities.js';
|
|
4
|
-
import { DESCRIPTION_MAX, POLICY_MAX, hotelText } from './fence.js';
|
|
4
|
+
import { DESCRIPTION_MAX, POLICY_MAX, hotelName, hotelText } from './fence.js';
|
|
5
5
|
import { clockOf, minutesOf, startProblem, stayWords, windowWords } from './hourly.js';
|
|
6
6
|
import { amountsIn, formatMoney, keepingAmounts, money, multiplyMoney, toMinor } from './money.js';
|
|
7
7
|
import { namedExactly, roomNamesOf, roomTypeNamed, withTraditional } from './names.js';
|
|
@@ -298,7 +298,7 @@ const allTools = (n, v) => {
|
|
|
298
298
|
// description says it too, as what the card is, never as an order: the
|
|
299
299
|
// directory rule (tool-descriptions.test.ts) holds, and "first" is one of
|
|
300
300
|
// its words, hence "the other way round".
|
|
301
|
-
description: `${upFirst(catalogue(v))}, shown as a card for a guest to look through and pick one ${v.noun} to book.
|
|
301
|
+
description: `${upFirst(catalogue(v))}, shown as a card for a guest to look through and pick one ${v.noun} to book. It holds no plan of days: a day-by-day plan (行程, 安排, an itinerary) is a card of its own, laid out by the days, with a ${v.noun} for each part of the trip read up as text. 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 ${v.nounPlural} it has; \`all\` lists the ${v.nounPlural} themselves. \`city\` (a city or a country) or \`query\` answers with the ${v.nounPlural}: a picture, the city and area, a rating where the ${v.noun} publishes one, and a from-price, and a city usually holds several. With \`checkIn\` and \`checkOut\` every ${v.noun} 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\`, \`breakfast\` and \`bed\` 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.${v.hints.search ? ` ${v.hints.search}` : ''}`,
|
|
302
302
|
annotations: reads(`Browse ${catalogue(v)}`, open),
|
|
303
303
|
inputSchema: {
|
|
304
304
|
type: 'object',
|
|
@@ -431,7 +431,7 @@ const allTools = (n, v) => {
|
|
|
431
431
|
// the guest for one question (robin, 2026-09-29, Opus in Claude.ai). The
|
|
432
432
|
// reference apps do the same split: search without a card, present with
|
|
433
433
|
// one.
|
|
434
|
-
description: `Reads up to ${LOOK_UP_MAX} ${v.nounPlural} - a \`city\`'s (or a country's), or the ones named in \`${v.nounPlural}\` - 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.
|
|
434
|
+
description: `Reads up to ${LOOK_UP_MAX} ${v.nounPlural} - a \`city\`'s (or a country's), or the ones named in \`${v.nounPlural}\` - 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. Nothing it reads is shown, so the ${v.nounPlural} of a day-by-day plan - for each part of the trip, those near where its days are spent - can be read up before the plan is drawn; its ids are the ones a comparison and a plan take.`,
|
|
435
435
|
annotations: reads(`Look up ${catalogue(v)}`, open),
|
|
436
436
|
inputSchema: {
|
|
437
437
|
type: 'object',
|
|
@@ -740,7 +740,7 @@ const allTools = (n, v) => {
|
|
|
740
740
|
},
|
|
741
741
|
{
|
|
742
742
|
name: n.itinerary,
|
|
743
|
-
description: `A day-by-day plan (行程) as one card - the card for a trip of several days, or for a guest
|
|
743
|
+
description: `A day-by-day plan (行程) as one card - the card for a trip of several days, or for days a guest asked to have planned (a city with a span of dates and 行程 is one): the days are laid out by where they are spent, and each part of the trip has a ${v.noun} near those days, chosen from the ${v.nounPlural} read up as text. A week in a big city is usually two or three stays: its far-out sights fall on neighbouring days, and those nights have a ${v.noun} near them, with the reason in that day's \`why\` (Beijing's Summer Palace, Yuanmingyuan, Fragrant Hills and the Peking and Tsinghua campuses - 颐和园, 圆明园, 香山, 北大清华 - are days side by side in the north-west); a day out of town, such as the Great Wall, is a day of its own, there and back; one ${v.noun} for the whole week suits days that are all within about 45 minutes of it. Nothing here knows a sight's opening days: each day is drawn as it is given, with its date (many museums close on Mondays, the Palace Museum - 故宫 - among them). Each day carries its own label, an optional note, the moments of the day in order, and the ${v.nounPlural} and add-ons attached to it, with their details filled in from their own records - a ${v.noun} 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.`,
|
|
744
744
|
annotations: reads(v.market ? `Lay out an itinerary on ${v.brand}` : `Lay out a ${v.brand} itinerary`, open),
|
|
745
745
|
inputSchema: {
|
|
746
746
|
type: 'object',
|
|
@@ -983,6 +983,8 @@ const TRIP_SCHEMA = {
|
|
|
983
983
|
subtotal: STR,
|
|
984
984
|
taxesAndFees: STR,
|
|
985
985
|
total: STR,
|
|
986
|
+
/** Which of a room type's rates a room named by its type was put in as - the text's first line. */
|
|
987
|
+
took: STR,
|
|
986
988
|
/** Who is signed in, on every payload. */
|
|
987
989
|
member: OBJ,
|
|
988
990
|
/** What the tier took off THIS trip, and what it earns. */
|
|
@@ -1340,6 +1342,8 @@ export const buildOutputSchemas = (n) => ({
|
|
|
1340
1342
|
/** Present only when the columns are priced in more than one currency. */
|
|
1341
1343
|
currencies: STRS,
|
|
1342
1344
|
rowLabels: STRS,
|
|
1345
|
+
/** Only for a client that shows no cards: that nothing was drawn - `COMPARISON_NOT_DRAWN`. */
|
|
1346
|
+
notDrawn: STR,
|
|
1343
1347
|
},
|
|
1344
1348
|
required: ['view'],
|
|
1345
1349
|
},
|
|
@@ -1373,6 +1377,12 @@ export const buildOutputSchemas = (n) => ({
|
|
|
1373
1377
|
inTrip: { type: 'number' },
|
|
1374
1378
|
planned: { type: 'number' },
|
|
1375
1379
|
dropped: ARR,
|
|
1380
|
+
/** The stays not booked yet, together - only when all are priced in one currency. */
|
|
1381
|
+
staysTotal: STR,
|
|
1382
|
+
/** What the text says of a long stay at one hotel, in the same words. */
|
|
1383
|
+
nudges: STRS,
|
|
1384
|
+
/** Only for a client that shows no cards: that nothing was drawn - `PLAN_NOT_DRAWN`. */
|
|
1385
|
+
notDrawn: STR,
|
|
1376
1386
|
},
|
|
1377
1387
|
required: ['view'],
|
|
1378
1388
|
},
|
|
@@ -2815,8 +2825,8 @@ const suggestionsFor = async (deps, d) => {
|
|
|
2815
2825
|
out.push({
|
|
2816
2826
|
// The label IS what is sent: a chip that sends more than it says is
|
|
2817
2827
|
// a chip that says something else in the guest's name.
|
|
2818
|
-
label: `Compare ${hotel.name} with the other ${hotel.city} hotels`,
|
|
2819
|
-
say: `Compare ${hotel.name} with the other ${hotel.city} hotels`,
|
|
2828
|
+
label: `Compare ${hotelName(hotel.name)} with the other ${hotelName(hotel.city)} hotels`,
|
|
2829
|
+
say: `Compare ${hotelName(hotel.name)} with the other ${hotelName(hotel.city)} hotels`,
|
|
2820
2830
|
});
|
|
2821
2831
|
}
|
|
2822
2832
|
// The price order is not a suggestion any more: it is the list's own sort
|
|
@@ -2825,12 +2835,12 @@ const suggestionsFor = async (deps, d) => {
|
|
|
2825
2835
|
const pick = (d.columns ?? []).find((c) => c.recommended);
|
|
2826
2836
|
if (pick) {
|
|
2827
2837
|
out.push({
|
|
2828
|
-
label: `Rooms at ${pick.hotel.name}`,
|
|
2838
|
+
label: `Rooms at ${hotelName(pick.hotel.name)}`,
|
|
2829
2839
|
ask: { tool: 'getHotel', args: { hotel: pick.hotel.id, ...on } },
|
|
2830
2840
|
});
|
|
2831
2841
|
out.push({
|
|
2832
|
-
label: `Plan the days at ${pick.hotel.name}`,
|
|
2833
|
-
say: `Plan the days at ${pick.hotel.name}`,
|
|
2842
|
+
label: `Plan the days at ${hotelName(pick.hotel.name)}`,
|
|
2843
|
+
say: `Plan the days at ${hotelName(pick.hotel.name)}`,
|
|
2834
2844
|
});
|
|
2835
2845
|
}
|
|
2836
2846
|
}
|
|
@@ -2994,6 +3004,48 @@ const OTHER_AREAS_MAX = 4;
|
|
|
2994
3004
|
* of a long stay at one hotel. It is in every plan's answer now.
|
|
2995
3005
|
*/
|
|
2996
3006
|
export const PLAN_DRAWN_ONCE = 'The plan on the card is this one. Something to change that you notice now - a sight closed on its day, a day too far from its hotel - goes in your reply, not in another plan: the plan is drawn again only when the guest asks for the change.';
|
|
3007
|
+
/**
|
|
3008
|
+
* The same place in the answer, for a client that shows no cards.
|
|
3009
|
+
*
|
|
3010
|
+
* ⚠️ NO CARD, SO THE REPLY IS THE PLAN (总线程, 2026-10-28, a client whose
|
|
3011
|
+
* `initialize` declared nothing): it was told "The plan on the card is this
|
|
3012
|
+
* one", and a model told that leaves the plan to a card the guest never sees.
|
|
3013
|
+
*/
|
|
3014
|
+
export const PLAN_NOT_DRAWN = 'Nothing was drawn: this app shows no cards, so the guest has seen none of this plan. Tell it to them in your reply, day by day - where each day is spent, and each stay with its hotel, dates, price and terms as above. Something to change that you notice now - a sight closed on its day, a day too far from its hotel - is said in that reply, not planned again: the plan is made again only when the guest asks for the change.';
|
|
3015
|
+
/** A comparison's answer, for a client that shows no cards - see `PLAN_NOT_DRAWN`. */
|
|
3016
|
+
export const COMPARISON_NOT_DRAWN = "Nothing was drawn: this app shows no cards, so the guest has seen none of this comparison. Compare the hotels in your reply, with each one's price and terms as given here - each is its cheapest room that sleeps the party.";
|
|
3017
|
+
/**
|
|
3018
|
+
* One column of a comparison as a line, for a client that shows no cards:
|
|
3019
|
+
* what the card's rows say - where, the rating, the room, the price - and the
|
|
3020
|
+
* terms the rows only answer yes or no to.
|
|
3021
|
+
*/
|
|
3022
|
+
const comparedInWords = (c) => {
|
|
3023
|
+
const h = c.hotel;
|
|
3024
|
+
const about = [
|
|
3025
|
+
[h.area, h.city].filter(Boolean).join(', '),
|
|
3026
|
+
h.rating ? `${h.rating.value}/${h.rating.scaleMax} from ${h.rating.count} reviews` : '',
|
|
3027
|
+
]
|
|
3028
|
+
.filter(Boolean)
|
|
3029
|
+
.join(' · ');
|
|
3030
|
+
const p = c.price;
|
|
3031
|
+
const priced = p
|
|
3032
|
+
? [
|
|
3033
|
+
`${hotelName(p.room)} at ${p.nightly} a night, ${p.stayTotal} for the stay incl. taxes and fees`,
|
|
3034
|
+
p.freeCancellationUntil
|
|
3035
|
+
? `free to cancel until ${freeUntilText(p)}`
|
|
3036
|
+
: p.refundable
|
|
3037
|
+
? 'no free cancellation'
|
|
3038
|
+
: 'non-refundable',
|
|
3039
|
+
p.payment === 'prepaid' ? 'paid in advance' : '',
|
|
3040
|
+
p.breakfast ? 'breakfast included' : 'no breakfast',
|
|
3041
|
+
]
|
|
3042
|
+
.filter(Boolean)
|
|
3043
|
+
.join('; ')
|
|
3044
|
+
: c.from
|
|
3045
|
+
? `from ${c.from} a night; rooms are priced once there are dates`
|
|
3046
|
+
: 'not priced without dates';
|
|
3047
|
+
return `- ${hotelName(h.name)} (\`${h.id}\`)${about ? ` · ${about}` : ''}: ${priced}`;
|
|
3048
|
+
};
|
|
2997
3049
|
/** An area's names as a day might say them: "Haidian", "海淀区" and "海淀". */
|
|
2998
3050
|
const areaWords = (h) => {
|
|
2999
3051
|
const out = new Set();
|
|
@@ -3072,7 +3124,7 @@ const longStayNudges = async (deps, stays, days) => {
|
|
|
3072
3124
|
const h = [...byArea.values()].find((o) => dayNames(day, areaWords(o)));
|
|
3073
3125
|
return h
|
|
3074
3126
|
? [
|
|
3075
|
-
`Day ${i + 1}${day.date ? ` (${day.date})` : ''} names ${h.area}, where ${h.name} (\`${h.id}\`) is.`,
|
|
3127
|
+
`Day ${i + 1}${day.date ? ` (${day.date})` : ''} names ${hotelName(h.area)}, where ${hotelName(h.name)} (\`${h.id}\`) is.`,
|
|
3076
3128
|
]
|
|
3077
3129
|
: [];
|
|
3078
3130
|
})
|
|
@@ -3081,14 +3133,18 @@ const longStayNudges = async (deps, stays, days) => {
|
|
|
3081
3133
|
continue;
|
|
3082
3134
|
const others = [...byArea.values()]
|
|
3083
3135
|
.slice(0, OTHER_AREAS_MAX)
|
|
3084
|
-
.map((h) => `${h.area} - ${h.name} (\`${h.id}\`)`);
|
|
3136
|
+
.map((h) => `${hotelName(h.area)} - ${hotelName(h.name)} (\`${h.id}\`)`);
|
|
3137
|
+
// The plan's own stay, in the same cleaned words.
|
|
3138
|
+
const at = hotelName(x.name);
|
|
3139
|
+
const area = hotelName(x.area);
|
|
3140
|
+
const city = hotelName(x.city);
|
|
3085
3141
|
out.push(whole
|
|
3086
3142
|
? [
|
|
3087
|
-
`All ${x.nights} nights are at ${
|
|
3143
|
+
`All ${x.nights} nights are at ${at} (${area}). ${city} also has: ${others.join('; ')}.`,
|
|
3088
3144
|
...elsewhere,
|
|
3089
|
-
`One hotel for the whole trip fits only when every day is within about 45 minutes of it. In your reply, name the days spent farther out than that and add one sentence on the other way - "it could also be two stays: ..." - those nights at the hotel here nearest them, and why; or say in one sentence why every day is within reach of ${
|
|
3145
|
+
`One hotel for the whole trip fits only when every day is within about 45 minutes of it. In your reply, name the days spent farther out than that and add one sentence on the other way - "it could also be two stays: ..." - those nights at the hotel here nearest them, and why; or say in one sentence why every day is within reach of ${area}.`,
|
|
3090
3146
|
].join(' ')
|
|
3091
|
-
: `${x.nights} nights are at ${
|
|
3147
|
+
: `${x.nights} nights are at ${at} (${area}). ${city} also has: ${others.join('; ')}. In your reply, name the planned days that are far from ${area} and offer moving those nights to the hotel here nearest them, with the reason in one sentence - or say why every day suits ${area}.`);
|
|
3092
3148
|
}
|
|
3093
3149
|
return out;
|
|
3094
3150
|
};
|
|
@@ -3191,6 +3247,31 @@ const putRoomLine = async (deps, t, ctx, key, store, args, detail) => {
|
|
|
3191
3247
|
// 2026-10-12).
|
|
3192
3248
|
const who = identity(deps, ctx, key);
|
|
3193
3249
|
const startTime = str(args.startTime);
|
|
3250
|
+
// ⚠️ TAKING A ROOM OUT NEEDS NO ROOM FOR SALE. The lookups below are of
|
|
3251
|
+
// what is for sale, so a room that had gone since it went into the trip -
|
|
3252
|
+
// the stay a booking could not make - could not be taken out of it
|
|
3253
|
+
// (2026-10-14). Nor could a room by the hour once its window had passed:
|
|
3254
|
+
// the start was checked against today's window first, and every line of a
|
|
3255
|
+
// trip must be removable (2026-10-27 audit, F6). The line in the trip is the
|
|
3256
|
+
// one asked about - by its nights, or a room by the hour by its start.
|
|
3257
|
+
if (args.rooms !== undefined && clampCount(args.rooms, MAX_ROOMS_PER_LINE) === 0) {
|
|
3258
|
+
const asked = asStay(args);
|
|
3259
|
+
const start = startTime ? minutesOf(startTime) : undefined;
|
|
3260
|
+
const held = deps.trips
|
|
3261
|
+
.read(store)
|
|
3262
|
+
.find((l) => l.kind === 'room' &&
|
|
3263
|
+
l.hotelId === hotelId &&
|
|
3264
|
+
l.roomId === roomRef &&
|
|
3265
|
+
(l.hourly
|
|
3266
|
+
? start !== undefined &&
|
|
3267
|
+
minutesOf(l.hourly.start) === start &&
|
|
3268
|
+
(!asked || l.stay.checkIn === asked.checkIn)
|
|
3269
|
+
: !!asked && l.stay.checkIn === asked.checkIn && l.stay.checkOut === asked.checkOut));
|
|
3270
|
+
if (held) {
|
|
3271
|
+
deps.trips.set(store, unset(held));
|
|
3272
|
+
return currentTrip(deps, key, who, store);
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3194
3275
|
const today = (startTime || !asStay(args)
|
|
3195
3276
|
? deps.backend.hourlyRooms?.(hotelId, guestsOf(args), who)
|
|
3196
3277
|
: undefined);
|
|
@@ -3269,23 +3350,6 @@ const putRoomLine = async (deps, t, ctx, key, store, args, detail) => {
|
|
|
3269
3350
|
: 'Which nights? A room needs a check-in and a check-out date before it can go into the trip.',
|
|
3270
3351
|
};
|
|
3271
3352
|
}
|
|
3272
|
-
// ⚠️ TAKING A ROOM OUT NEEDS NO ROOM FOR SALE. The lookup below is of
|
|
3273
|
-
// what is for sale, so a room that had gone since it went into the trip -
|
|
3274
|
-
// the stay a booking could not make - could not be taken out of it
|
|
3275
|
-
// (2026-10-14). The line in the trip is the one asked about.
|
|
3276
|
-
if (args.rooms !== undefined && clampCount(args.rooms, MAX_ROOMS_PER_LINE) === 0) {
|
|
3277
|
-
const held = deps.trips
|
|
3278
|
-
.read(store)
|
|
3279
|
-
.find((l) => l.kind === 'room' &&
|
|
3280
|
-
l.hotelId === hotelId &&
|
|
3281
|
-
l.roomId === roomRef &&
|
|
3282
|
-
l.stay.checkIn === stay.checkIn &&
|
|
3283
|
-
l.stay.checkOut === stay.checkOut);
|
|
3284
|
-
if (held) {
|
|
3285
|
-
deps.trips.set(store, unset(held));
|
|
3286
|
-
return currentTrip(deps, key, identity(deps, ctx, key), store);
|
|
3287
|
-
}
|
|
3288
|
-
}
|
|
3289
3353
|
// The backend owns the calendar - it knows each hotel's own timezone, and a
|
|
3290
3354
|
// second opinion computed from this process's clock would disagree with it
|
|
3291
3355
|
// for eight hours a day.
|
|
@@ -3370,7 +3434,11 @@ const putRoomLine = async (deps, t, ctx, key, store, args, detail) => {
|
|
|
3370
3434
|
rooms: clampCount(args.rooms ?? 1, MAX_ROOMS_PER_LINE),
|
|
3371
3435
|
});
|
|
3372
3436
|
const now = await currentTrip(deps, key, identity(deps, ctx, key), store);
|
|
3373
|
-
|
|
3437
|
+
// Which rate was taken, in the structure too and in the same words - see
|
|
3438
|
+
// the itinerary's `nudges` (2026-10-27 audit, F11).
|
|
3439
|
+
return took
|
|
3440
|
+
? { ...now, structured: { ...now.structured, took }, text: `${took}\n${now.text}` }
|
|
3441
|
+
: now;
|
|
3374
3442
|
};
|
|
3375
3443
|
/**
|
|
3376
3444
|
* Books the rooms in one slot of the trip store for a named guest - THE ONE
|
|
@@ -4370,7 +4438,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
|
|
|
4370
4438
|
const extras = await deps.backend.extrasFor(h.id, stay);
|
|
4371
4439
|
const rooms = (detail.rooms ?? []).filter((r) => r.roomsLeft >= roomsWanted);
|
|
4372
4440
|
blocks.push([
|
|
4373
|
-
`### ${h.name} · id \`${h.id}\``,
|
|
4441
|
+
`### ${hotelName(h.name)} · id \`${h.id}\``,
|
|
4374
4442
|
[
|
|
4375
4443
|
[h.area, h.city, h.country].filter(Boolean).join(', '),
|
|
4376
4444
|
h.brand,
|
|
@@ -4386,7 +4454,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
|
|
|
4386
4454
|
: rooms.length
|
|
4387
4455
|
? [
|
|
4388
4456
|
`Rooms for ${stay.checkIn} to ${stay.checkOut}, ${stay.guests} guests${roomsWanted > 1 ? `, ${roomsWanted} rooms` : ''}:`,
|
|
4389
|
-
...rooms.map((r) => `- ${r.name} · sleeps ${r.sleeps}${r.breakfast ? ' · breakfast' : ''} · ${formatMoney(r.stayTotal)} for the stay incl. taxes and fees${r.freeCancellationUntil ? `, free to cancel until ${freeUntilText(r)}` : r.refundable ? '' : ', non-refundable'}`),
|
|
4457
|
+
...rooms.map((r) => `- ${r.name} · sleeps ${r.sleeps}${r.breakfast ? ' · breakfast' : ''} · ${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 ${freeUntilText(r)}` : r.refundable ? '' : ', non-refundable'}`),
|
|
4390
4458
|
].join('\n')
|
|
4391
4459
|
: (detail.noRooms ?? `No room type here has ${roomsWanted} left for those nights.`),
|
|
4392
4460
|
extras.length
|
|
@@ -4721,9 +4789,13 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
|
|
|
4721
4789
|
url: step.url,
|
|
4722
4790
|
...(step.kind === 'confirm' ? { booking: bookingForCard(step.booking) } : {}),
|
|
4723
4791
|
},
|
|
4792
|
+
// ⚠️ THE LINK STAYS ON THE CARD (2026-10-27 audit, F9). Only the card
|
|
4793
|
+
// calls this, and the card hands this text to the model as context - so
|
|
4794
|
+
// UnionPay's own page, a link nobody here knows the life of, went into
|
|
4795
|
+
// the conversation. `url` is in the structure, for the card alone.
|
|
4724
4796
|
text: step.kind === 'enroll'
|
|
4725
|
-
? `No UnionPay card is on file for that email yet. UnionPay's page to add one
|
|
4726
|
-
: `UnionPay's page to confirm ${step.booking.confirmationCode} on
|
|
4797
|
+
? `No UnionPay card is on file for that email yet. UnionPay's page to add one is on the card - once it is added, paying with UnionPay again opens the confirmation.`
|
|
4798
|
+
: `UnionPay's page to confirm ${step.booking.confirmationCode} on is on the card - the hotel is asked once UnionPay has confirmed it.`,
|
|
4727
4799
|
};
|
|
4728
4800
|
}
|
|
4729
4801
|
if (name === t.previewChange || name === t.changeBooking) {
|
|
@@ -5080,6 +5152,9 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
|
|
|
5080
5152
|
].join('\n'),
|
|
5081
5153
|
};
|
|
5082
5154
|
}
|
|
5155
|
+
const stayWords = comparison.stay
|
|
5156
|
+
? ` for ${comparison.stay.checkIn} to ${comparison.stay.checkOut}`
|
|
5157
|
+
: '';
|
|
5083
5158
|
return {
|
|
5084
5159
|
structured: {
|
|
5085
5160
|
view: 'comparison',
|
|
@@ -5089,11 +5164,24 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
|
|
|
5089
5164
|
...c,
|
|
5090
5165
|
hotel: { ...c.hotel, fromNightly: undefined },
|
|
5091
5166
|
})),
|
|
5167
|
+
// In both, like the itinerary's: a host with no card may hand its
|
|
5168
|
+
// model this rather than the text.
|
|
5169
|
+
...(ctx.noCards ? { notDrawn: COMPARISON_NOT_DRAWN } : {}),
|
|
5092
5170
|
},
|
|
5093
5171
|
text: [
|
|
5094
5172
|
// The card carries the table; the text carries only what the model
|
|
5095
5173
|
// could not see before it asked - what was dropped, and why.
|
|
5096
|
-
|
|
5174
|
+
//
|
|
5175
|
+
// ⚠️ UNLESS THERE IS NO CARD (总线程, 2026-10-28): a client that shows
|
|
5176
|
+
// none was answered "2 side by side" and nothing else, and its model
|
|
5177
|
+
// could not say what either hotel cost. Then the table is said here,
|
|
5178
|
+
// one line a hotel, from the same records the card's rows are read off.
|
|
5179
|
+
...(ctx.noCards
|
|
5180
|
+
? [
|
|
5181
|
+
`${comparison.columns.length} compared${stayWords}. ${COMPARISON_NOT_DRAWN}`,
|
|
5182
|
+
...comparison.columns.map(comparedInWords),
|
|
5183
|
+
]
|
|
5184
|
+
: [`${comparison.columns.length} side by side${stayWords}.`]),
|
|
5097
5185
|
// ⚠️ SAY IT IN THE TEXT TOO. The card carries the warning for whoever is
|
|
5098
5186
|
// looking at it; this line is for the model, which is about to describe
|
|
5099
5187
|
// the table in prose and would otherwise read "¥67,500" against
|
|
@@ -5146,22 +5234,47 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
|
|
|
5146
5234
|
const stayItems = plan.days
|
|
5147
5235
|
.flatMap((day) => day.items)
|
|
5148
5236
|
.filter((x) => x.kind === 'hotel' && (x.night ?? 1) === 1 && x.checkIn && x.roomId);
|
|
5149
|
-
|
|
5237
|
+
// Each stay's rate says its terms where they bind - see `ItineraryItem.refundable`.
|
|
5238
|
+
const termsOf = (x) => [
|
|
5239
|
+
x.refundable === false ? 'non-refundable' : '',
|
|
5240
|
+
x.payment === 'prepaid' ? 'paid in advance' : '',
|
|
5241
|
+
]
|
|
5242
|
+
.filter(Boolean)
|
|
5243
|
+
.join(', ');
|
|
5244
|
+
// With no card, each stay's own price too: the card shows it beside the
|
|
5245
|
+
// stay, and the reply is now the only place the guest reads it.
|
|
5246
|
+
const stays = stayItems.map((x, i) => `Stay ${i + 1}: ${hotelName(x.name)} (\`${x.id}\`), room \`${x.roomId}\`${x.roomName ? ` (${hotelName(x.roomName)}${termsOf(x) ? `; ${termsOf(x)}` : ''})` : termsOf(x) ? ` (${termsOf(x)})` : ''}, ${x.checkIn} to ${x.checkOut}${x.nights ? ` (${x.nights} ${x.nights === 1 ? 'night' : 'nights'})` : ''}${ctx.noCards && x.stayTotal ? `, ${x.stayTotal} for the stay incl. taxes and fees` : ''}${x.booked ? ` - booked, ${x.booked}` : x.inTrip ? ' - in the trip' : ''}`);
|
|
5247
|
+
// ⚠️ IN BOTH, IN THE SAME WORDS (2026-10-27 audit, F11): a host that hands
|
|
5248
|
+
// the model `structuredContent` rather than the text never saw what the
|
|
5249
|
+
// text said of a long stay. The model's part of the answer is the same on
|
|
5250
|
+
// either side.
|
|
5251
|
+
const nudges = stays.length ? await longStayNudges(deps, stayItems, plan.days) : [];
|
|
5150
5252
|
return {
|
|
5151
|
-
structured: {
|
|
5253
|
+
structured: {
|
|
5254
|
+
view: 'itinerary',
|
|
5255
|
+
...common(deps, key),
|
|
5256
|
+
...plan,
|
|
5257
|
+
gap,
|
|
5258
|
+
...(nudges.length ? { nudges } : {}),
|
|
5259
|
+
// In both, like the nudges: a host with no card may hand its model
|
|
5260
|
+
// this rather than the text.
|
|
5261
|
+
...(ctx.noCards ? { notDrawn: PLAN_NOT_DRAWN } : {}),
|
|
5262
|
+
},
|
|
5152
5263
|
text: [
|
|
5153
5264
|
`**${plan.title}** · ${plan.days.length} ${plan.days.length === 1 ? 'day' : 'days'}. ${gap}`,
|
|
5154
5265
|
...(stays.length
|
|
5155
5266
|
? [
|
|
5156
5267
|
`Priced stays (${plan.stay?.guests ?? 2} guests; ${t.addToTrip} takes each one's hotel id, room id and dates):`,
|
|
5157
5268
|
...stays,
|
|
5158
|
-
|
|
5159
|
-
|
|
5269
|
+
...(plan.staysTotal
|
|
5270
|
+
? [`Together, the stays not booked yet: ${plan.staysTotal}.`]
|
|
5271
|
+
: []),
|
|
5272
|
+
...nudges,
|
|
5160
5273
|
]
|
|
5161
5274
|
: []),
|
|
5162
5275
|
...plan.dropped.map((d) => `Left out: ${d.name} — ${d.why}`),
|
|
5163
5276
|
...fitsManyOf(deps, plan.dropped),
|
|
5164
|
-
PLAN_DRAWN_ONCE,
|
|
5277
|
+
ctx.noCards ? PLAN_NOT_DRAWN : PLAN_DRAWN_ONCE,
|
|
5165
5278
|
deps.vocab.demo ? noteFor(deps.vocab) : '',
|
|
5166
5279
|
]
|
|
5167
5280
|
.filter(Boolean)
|