@curless/hotel-storefront 0.47.4 → 0.47.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/card.generated.d.ts +1 -1
- package/dist/card.generated.js +2 -2
- package/dist/card.generated.js.map +1 -1
- package/dist/names.d.ts +27 -2
- package/dist/names.d.ts.map +1 -1
- package/dist/names.js +32 -2
- package/dist/names.js.map +1 -1
- package/dist/present.d.ts +10 -0
- package/dist/present.d.ts.map +1 -1
- package/dist/present.js +3 -0
- package/dist/present.js.map +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.js +2 -2
- package/dist/surface.d.ts.map +1 -1
- package/dist/surface.js +14 -4
- package/dist/surface.js.map +1 -1
- package/dist/tools.d.ts +5 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +76 -12
- package/dist/tools.js.map +1 -1
- package/package.json +1 -1
package/dist/tools.js
CHANGED
|
@@ -4,7 +4,7 @@ import { FACILITIES } from './facilities.js';
|
|
|
4
4
|
import { DESCRIPTION_MAX, POLICY_MAX, guestText, 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
|
-
import { namedExactly, roomNamesOf, withTraditional } from './names.js';
|
|
7
|
+
import { namedExactly, roomNamesOf, roomTypeNamed, withTraditional } from './names.js';
|
|
8
8
|
import { MAX_COMPARED, MAX_CONS, MAX_DAYS, MAX_ITEMS_PER_DAY, MAX_MOMENTS, MAX_PROS, buildItinerary, candidatesFor, compareHotels, namesMoreThanOne, } from './present.js';
|
|
9
9
|
import { regionOf } from './regions.js';
|
|
10
10
|
import { activeKeysOf, byRoomType, facetsOf, keyWords, matchesTerms, optionsOf, roomTermsOf, termWords, } from './room-filters.js';
|
|
@@ -747,12 +747,16 @@ const allTools = (n, v) => {
|
|
|
747
747
|
properties: {
|
|
748
748
|
label: {
|
|
749
749
|
type: 'string',
|
|
750
|
-
description: `e.g. "
|
|
750
|
+
description: `e.g. "Arrive${places[0] ? ` in ${places[0]}` : ''}" - the card prints each day's date beside it, so leave the date out`,
|
|
751
751
|
},
|
|
752
752
|
note: {
|
|
753
753
|
type: 'string',
|
|
754
754
|
description: 'One or two lines on the day, in your own words',
|
|
755
755
|
},
|
|
756
|
+
why: {
|
|
757
|
+
type: 'string',
|
|
758
|
+
description: `Why the ${v.noun} named on this day suits the days of that stay - near what, good for what - one sentence, in your own words, with no lead-in such as "My pick:" - the card has its own`,
|
|
759
|
+
},
|
|
756
760
|
items: {
|
|
757
761
|
type: 'array',
|
|
758
762
|
items: { type: 'string' },
|
|
@@ -1321,7 +1325,21 @@ export const buildOutputSchemas = (n) => ({
|
|
|
1321
1325
|
demoNote: STR,
|
|
1322
1326
|
title: STR,
|
|
1323
1327
|
stay: OBJ,
|
|
1324
|
-
days:
|
|
1328
|
+
days: {
|
|
1329
|
+
type: 'array',
|
|
1330
|
+
items: {
|
|
1331
|
+
type: 'object',
|
|
1332
|
+
properties: {
|
|
1333
|
+
label: STR,
|
|
1334
|
+
note: STR,
|
|
1335
|
+
/** Why the hotel this day names suits the days of that stay - the model's sentence. */
|
|
1336
|
+
why: STR,
|
|
1337
|
+
date: STR,
|
|
1338
|
+
plan: ARR,
|
|
1339
|
+
items: ARR,
|
|
1340
|
+
},
|
|
1341
|
+
},
|
|
1342
|
+
},
|
|
1325
1343
|
/** How many of the planned items are actually in the trip. Computed, never written. */
|
|
1326
1344
|
inTrip: { type: 'number' },
|
|
1327
1345
|
planned: { type: 'number' },
|
|
@@ -2996,7 +3014,7 @@ const putRoomLine = async (deps, t, ctx, key, store, args, detail) => {
|
|
|
2996
3014
|
})),
|
|
2997
3015
|
},
|
|
2998
3016
|
text: sold.length
|
|
2999
|
-
? `${detail.hotel.name} has no room by the hour by that name today. It has: ${sold.map((r) => `${r.name} (\`${r.id}\`, ${windowWords(r.hourly)})`).join('; ')}.`
|
|
3017
|
+
? `${detail.hotel.name} has no room by the hour by that name today. It has: ${sold.map((r) => `${r.name} (\`${r.id}\`, ${windowWords(r.hourly)})`).join('; ')}. Call again with the id.`
|
|
3000
3018
|
: ((await today)?.noRooms ?? `${detail.hotel.name} has no rooms by the hour today.`),
|
|
3001
3019
|
};
|
|
3002
3020
|
}
|
|
@@ -3085,12 +3103,37 @@ const putRoomLine = async (deps, t, ctx, key, store, args, detail) => {
|
|
|
3085
3103
|
text: dated?.noRooms ?? 'Those nights cannot be booked here.',
|
|
3086
3104
|
};
|
|
3087
3105
|
}
|
|
3088
|
-
// ⚠️ BY THE OFFER'S WHOLE NAME,
|
|
3106
|
+
// ⚠️ BY THE ID, THE OFFER'S WHOLE NAME, OR A ROOM TYPE'S BARE NAME - in
|
|
3107
|
+
// that order, and in any language the name has: a guest who read
|
|
3089
3108
|
// 「豪华大床房 · 双早 · 不可取消」 on the card says that, and it is the same offer
|
|
3090
3109
|
// as "Deluxe King Room · Breakfast for 2 · Non-refundable". Never a part of
|
|
3091
|
-
//
|
|
3092
|
-
|
|
3110
|
+
// a name. A type's bare name ("Deluxe King Room") used to be no room at all
|
|
3111
|
+
// when the type has several rates, and a model that planned a trip by the
|
|
3112
|
+
// type's name was answered "no such room" three times over (the owner,
|
|
3113
|
+
// 2026-10-26, Atour). It now takes the type's CHEAPEST rate that sleeps the
|
|
3114
|
+
// stay's guests and has rooms left - and the answer says which rate that
|
|
3115
|
+
// was and that there are others, so the guest is told what they got. Two
|
|
3116
|
+
// types answering to one name, or none, is still no room.
|
|
3117
|
+
const wanted = Math.max(1, clampCount(args.rooms ?? 1, MAX_ROOMS_PER_LINE));
|
|
3118
|
+
let room = dated.rooms.find((r) => r.id === roomRef) ?? namedExactly(dated.rooms, roomRef);
|
|
3119
|
+
let took = '';
|
|
3120
|
+
const type = room ? undefined : roomTypeNamed(dated.rooms, roomRef);
|
|
3121
|
+
if (type) {
|
|
3122
|
+
const fits = type.rooms.filter((r) => r.sleeps >= stay.guests && r.roomsLeft >= wanted);
|
|
3123
|
+
const [first, ...rest] = fits;
|
|
3124
|
+
const cheapest = first
|
|
3125
|
+
? rest.reduce((a, b) => (b.stayTotal.minor < a.stayTotal.minor ? b : a), first)
|
|
3126
|
+
: undefined;
|
|
3127
|
+
if (cheapest) {
|
|
3128
|
+
room = cheapest;
|
|
3129
|
+
const others = fits.filter((r) => r !== cheapest);
|
|
3130
|
+
took = `Took ${cheapest.name} (\`${cheapest.id}\`) - ${others.length
|
|
3131
|
+
? `the lowest of ${type.name}'s ${fits.length} rates for these nights; the others: ${others.map((r) => `${r.name} (\`${r.id}\`)`).join(', ')}.`
|
|
3132
|
+
: `${type.name}'s only rate for these nights.`}`;
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3093
3135
|
if (!room) {
|
|
3136
|
+
const han = /\p{Script=Han}/u.test(roomRef);
|
|
3094
3137
|
return {
|
|
3095
3138
|
structured: {
|
|
3096
3139
|
view: 'error',
|
|
@@ -3104,10 +3147,18 @@ const putRoomLine = async (deps, t, ctx, key, store, args, detail) => {
|
|
|
3104
3147
|
})),
|
|
3105
3148
|
// The hotel and the nights, so the card can take the guest straight
|
|
3106
3149
|
// back to its rooms rather than to the start (round-2 jury).
|
|
3107
|
-
hotel: {
|
|
3150
|
+
hotel: {
|
|
3151
|
+
id: detail.hotel.id,
|
|
3152
|
+
name: detail.hotel.name,
|
|
3153
|
+
// The card says the hotel in the guest's language (「没能放进行程:…」).
|
|
3154
|
+
...(detail.hotel.names ? { names: detail.hotel.names } : {}),
|
|
3155
|
+
},
|
|
3108
3156
|
stay,
|
|
3109
3157
|
},
|
|
3110
|
-
|
|
3158
|
+
// ⚠️ EVERY OFFER WITH ITS ID: the model that was told "no such room" by
|
|
3159
|
+
// a list of names it had just tried to match by name tried again by name.
|
|
3160
|
+
// The ids are what it can say, and it says them once.
|
|
3161
|
+
text: `${type ? `${detail.hotel.name} has no ${type.name} that sleeps ${stay.guests} and has ${wanted === 1 ? 'a room' : `${wanted} rooms`} left for those nights.` : `${detail.hotel.name} has no room type by that name for those nights.`} It has: ${dated.rooms.map((r) => `${r.name} (${han && r.names?.zh ? `${r.names.zh}, ` : ''}\`${r.id}\`)`).join(', ')}. Call again with the id.`,
|
|
3111
3162
|
};
|
|
3112
3163
|
}
|
|
3113
3164
|
deps.trips.set(store, {
|
|
@@ -3117,7 +3168,8 @@ const putRoomLine = async (deps, t, ctx, key, store, args, detail) => {
|
|
|
3117
3168
|
stay,
|
|
3118
3169
|
rooms: clampCount(args.rooms ?? 1, MAX_ROOMS_PER_LINE),
|
|
3119
3170
|
});
|
|
3120
|
-
|
|
3171
|
+
const now = await currentTrip(deps, key, identity(deps, ctx, key), store);
|
|
3172
|
+
return took ? { ...now, text: `${took}\n${now.text}` } : now;
|
|
3121
3173
|
};
|
|
3122
3174
|
/**
|
|
3123
3175
|
* Books the rooms in one slot of the trip store for a named guest - THE ONE
|
|
@@ -4844,6 +4896,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
|
|
|
4844
4896
|
const plan = await buildItinerary(deps.backend, days.map((d) => ({
|
|
4845
4897
|
label: str(d.label),
|
|
4846
4898
|
note: typeof d.note === 'string' ? d.note : undefined,
|
|
4899
|
+
why: typeof d.why === 'string' ? d.why : undefined,
|
|
4847
4900
|
items: d.items ?? [],
|
|
4848
4901
|
...(Array.isArray(d.plan) ? { plan: d.plan } : {}),
|
|
4849
4902
|
})), {
|
|
@@ -4866,15 +4919,26 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
|
|
|
4866
4919
|
// Each stay as the trip takes it, for a guest who asks in the chat to put
|
|
4867
4920
|
// the plan in the trip: without the room, the model opened the hotel to
|
|
4868
4921
|
// find one - another card - before it could add anything.
|
|
4922
|
+
//
|
|
4923
|
+
// ⚠️ BY ID, NEVER BY NAME. A model asked "put the stays on this plan in the
|
|
4924
|
+
// trip" added three rooms by the names it remembered and was answered "no
|
|
4925
|
+
// such room" three times, a whole error card each (the owner, 2026-10-26,
|
|
4926
|
+
// Atour). Each priced stay is listed with the hotel id, the room id and the
|
|
4927
|
+
// nights, which is exactly what add_to_trip takes.
|
|
4869
4928
|
const stays = plan.days
|
|
4870
4929
|
.flatMap((day) => day.items)
|
|
4871
4930
|
.filter((x) => x.kind === 'hotel' && (x.night ?? 1) === 1 && x.checkIn && x.roomId)
|
|
4872
|
-
.map((x) =>
|
|
4931
|
+
.map((x, i) => `Stay ${i + 1}: ${x.name} (\`${x.id}\`), room \`${x.roomId}\`${x.roomName ? ` (${x.roomName})` : ''}, ${x.checkIn} to ${x.checkOut}${x.nights ? ` (${x.nights} ${x.nights === 1 ? 'night' : 'nights'})` : ''}${x.booked ? ` - booked, ${x.booked}` : x.inTrip ? ' - in the trip' : ''}`);
|
|
4873
4932
|
return {
|
|
4874
4933
|
structured: { view: 'itinerary', ...common(deps, key), ...plan, gap },
|
|
4875
4934
|
text: [
|
|
4876
4935
|
`**${plan.title}** · ${plan.days.length} ${plan.days.length === 1 ? 'day' : 'days'}. ${gap}`,
|
|
4877
|
-
stays.length
|
|
4936
|
+
...(stays.length
|
|
4937
|
+
? [
|
|
4938
|
+
`Priced stays (${plan.stay?.guests ?? 2} guests; ${t.addToTrip} takes each one's hotel id, room id and dates):`,
|
|
4939
|
+
...stays,
|
|
4940
|
+
]
|
|
4941
|
+
: []),
|
|
4878
4942
|
...plan.dropped.map((d) => `Left out: ${d.name} — ${d.why}`),
|
|
4879
4943
|
...fitsManyOf(deps, plan.dropped),
|
|
4880
4944
|
deps.vocab.demo ? noteFor(deps.vocab) : '',
|