@curless/hotel-storefront 0.16.1 → 0.18.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.
@@ -1 +1 @@
1
- {"version":3,"file":"surface.js","sourceRoot":"","sources":["../src/surface.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"surface.js","sourceRoot":"","sources":["../src/surface.ts"],"names":[],"mappings":"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"}
package/dist/tools.d.ts CHANGED
@@ -42,6 +42,26 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
42
42
  readonly type: "number";
43
43
  readonly description: "How many of the matches to skip: the answer says where the next page starts";
44
44
  };
45
+ readonly brand: {
46
+ readonly type: "string";
47
+ readonly description: "One of the group's brands, e.g. \"Kerry\" or \"JEN\"";
48
+ };
49
+ readonly facilities: {
50
+ readonly type: "array";
51
+ readonly items: {
52
+ readonly type: "string";
53
+ readonly enum: string[];
54
+ };
55
+ readonly description: `What every ${string} listed must have`;
56
+ };
57
+ readonly freeCancellation: {
58
+ readonly type: "boolean";
59
+ readonly description: `Only ${string} with a room that can be cancelled free - and, with dates, that room is the one priced`;
60
+ };
61
+ readonly breakfast: {
62
+ readonly type: "boolean";
63
+ readonly description: `Only ${string} with a room that includes breakfast - and, with dates, that room is the one priced`;
64
+ };
45
65
  readonly all: {
46
66
  readonly type: "boolean";
47
67
  readonly description: `Unnarrowed, or narrowed to a country of many cities, the answer is the places to choose from; true lists the ${string} themselves`;
@@ -245,7 +265,7 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
245
265
  };
246
266
  }, {
247
267
  readonly name: `list_my_${string}bookings`;
248
- readonly description: "The bookings made in this conversation, newest to oldest, with their status, dates and confirmation codes. Given an email it also finds bookings made with that email.";
268
+ 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.";
249
269
  readonly annotations: {
250
270
  readonly title: string;
251
271
  readonly readOnlyHint: true;
@@ -256,9 +276,13 @@ export declare const buildTools: (n: ToolNames, v: Vocabulary) => readonly [{
256
276
  readonly inputSchema: {
257
277
  readonly type: "object";
258
278
  readonly properties: {
279
+ readonly booking: {
280
+ readonly type: "string";
281
+ readonly description: "A confirmation code, with `email`, for a booking made elsewhere";
282
+ };
259
283
  readonly email: {
260
284
  readonly type: "string";
261
- readonly description: "The email a booking was made with";
285
+ readonly description: "The email that booking was made with";
262
286
  };
263
287
  readonly limit: {
264
288
  readonly type: "number";
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAahD,OAAO,EAGL,KAAK,SAAS,EAGf,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAUV,eAAe,EAIhB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAkExD,eAAO,MAAM,UAAU,GAAI,GAAG,SAAS,EAAE,GAAG,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4arD,CAAC;AAgFF,eAAO,MAAM,kBAAkB,GAAI,GAAG,SAAS,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAyLtE,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;CACrB,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;AA0f/E;;;;GAIG;AACH,eAAO,MAAM,eAAe,IAAI,CAAC;AA6FjC,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":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAahD,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,eAAO,MAAM,UAAU,GAAI,GAAG,SAAS,EAAE,GAAG,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAicrD,CAAC;AAgFF,eAAO,MAAM,kBAAkB,GAAI,GAAG,SAAS,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAyLtE,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;CACrB,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;AA4jB/E;;;;GAIG;AACH,eAAO,MAAM,eAAe,IAAI,CAAC;AA6FjC,eAAO,MAAM,UAAU,GACrB,MAAM,QAAQ,EACd,MAAM,MAAM,EACZ,SAAS,OAAO,EAChB,KAAK,WAAW,KACf,OAAO,CAAC,UAAU,CAMpB,CAAC"}
package/dist/tools.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { LASTING_REQUESTS, MAX_PAGE, REQUEST_KINDS, REQUEST_NOTE_MAX } from './backend.js';
2
2
  import { isDay, nights as nightsBetween } from './dates.js';
3
+ import { FACILITIES } from './facilities.js';
3
4
  import { DESCRIPTION_MAX, POLICY_MAX, hotelText } from './fence.js';
4
5
  import { formatMoney, money, multiplyMoney, toMinor } from './money.js';
5
6
  import { MAX_COMPARED, MAX_CONS, MAX_DAYS, MAX_ITEMS_PER_DAY, MAX_PROS, buildItinerary, compareHotels, } from './present.js';
@@ -97,6 +98,23 @@ export const buildTools = (n, v) => {
97
98
  type: 'number',
98
99
  description: 'How many of the matches to skip: the answer says where the next page starts',
99
100
  },
101
+ brand: {
102
+ type: 'string',
103
+ description: `One of the group's brands, e.g. "Kerry" or "JEN"`,
104
+ },
105
+ facilities: {
106
+ type: 'array',
107
+ items: { type: 'string', enum: FACILITIES.map((x) => x.key) },
108
+ description: `What every ${v.noun} listed must have`,
109
+ },
110
+ freeCancellation: {
111
+ type: 'boolean',
112
+ description: `Only ${v.nounPlural} with a room that can be cancelled free - and, with dates, that room is the one priced`,
113
+ },
114
+ breakfast: {
115
+ type: 'boolean',
116
+ description: `Only ${v.nounPlural} with a room that includes breakfast - and, with dates, that room is the one priced`,
117
+ },
100
118
  all: {
101
119
  type: 'boolean',
102
120
  description: `Unnarrowed, or narrowed to a country of many cities, the answer is the places to choose from; true lists the ${v.nounPlural} themselves`,
@@ -217,12 +235,16 @@ export const buildTools = (n, v) => {
217
235
  },
218
236
  {
219
237
  name: n.listBookings,
220
- description: 'The bookings made in this conversation, newest to oldest, with their status, dates and confirmation codes. Given an email it also finds bookings made with that email.',
238
+ 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.',
221
239
  annotations: reads(`My ${v.brand} bookings`, open),
222
240
  inputSchema: {
223
241
  type: 'object',
224
242
  properties: {
225
- email: { type: 'string', description: 'The email a booking was made with' },
243
+ booking: {
244
+ type: 'string',
245
+ description: 'A confirmation code, with `email`, for a booking made elsewhere',
246
+ },
247
+ email: { type: 'string', description: 'The email that booking was made with' },
226
248
  limit: { type: 'number', description: 'Default 10' },
227
249
  },
228
250
  },
@@ -1208,6 +1230,61 @@ const currentTrip = async (deps, key, who) => {
1208
1230
  .join('\n\n'),
1209
1231
  };
1210
1232
  };
1233
+ /** The filters a list call asked for, or undefined when it asked for none. */
1234
+ const filtersOf = (args) => {
1235
+ const keys = FACILITIES.map((x) => x.key);
1236
+ const facilities = Array.isArray(args.facilities)
1237
+ ? [...new Set(args.facilities.map(String).filter((k) => keys.includes(k)))]
1238
+ : [];
1239
+ const f = {
1240
+ ...(str(args.brand) ? { brand: str(args.brand) } : {}),
1241
+ ...(facilities.length ? { facilities } : {}),
1242
+ ...(args.freeCancellation === true ? { freeCancellation: true } : {}),
1243
+ ...(args.breakfast === true ? { breakfast: true } : {}),
1244
+ };
1245
+ return Object.keys(f).length ? f : undefined;
1246
+ };
1247
+ /** The filters in words, for the one line the model reads. */
1248
+ const filterWords = (f) => [
1249
+ f.brand,
1250
+ ...(f.facilities ?? []).map((k) => FACILITIES.find((x) => x.key === k)?.label.toLowerCase()),
1251
+ f.freeCancellation ? 'free cancellation' : '',
1252
+ f.breakfast ? 'breakfast included' : '',
1253
+ ]
1254
+ .filter(Boolean)
1255
+ .join(', ');
1256
+ /**
1257
+ * The filter chips a list offers, each with what it would leave.
1258
+ *
1259
+ * ⚠️ ONLY WHAT NARROWS. A chip every hotel on the list passes - breakfast
1260
+ * where every hotel has a breakfast rate, one brand where only one is here -
1261
+ * changes nothing when pressed, and a panel of those is a panel of noise. One
1262
+ * that is ON always shows, so it can be turned off again.
1263
+ */
1264
+ const refineFor = (r, on, total,
1265
+ /**
1266
+ * A dated list. There a room term changes which room is PRICED even where
1267
+ * every hotel keeps one - so free cancellation and breakfast always show.
1268
+ */
1269
+ dated = false) => {
1270
+ const narrows = (count, active) => active || (count > 0 && count < total);
1271
+ const brands = r.brands.filter((b) => narrows(b.count, b.name === on?.brand));
1272
+ return {
1273
+ brands: brands.length > 1 || on?.brand ? brands.map((b) => ({ ...b, on: b.name === on?.brand })) : [],
1274
+ facilities: r.facilities
1275
+ .filter((x) => narrows(x.count, !!on?.facilities?.includes(x.key)))
1276
+ .map((x) => ({ ...x, on: !!on?.facilities?.includes(x.key) })),
1277
+ room: [
1278
+ {
1279
+ key: 'freeCancellation',
1280
+ label: 'Free cancellation',
1281
+ count: r.freeCancellation,
1282
+ on: !!on?.freeCancellation,
1283
+ },
1284
+ { key: 'breakfast', label: 'Breakfast included', count: r.breakfast, on: !!on?.breakfast },
1285
+ ].filter((x) => narrows(x.count, x.on) || (dated && x.count > 0)),
1286
+ };
1287
+ };
1211
1288
  /**
1212
1289
  * From how many cities a country is a list of cities to choose from rather
1213
1290
  * than a list of hotels. China is forty; Malaysia's six hotels in three cities
@@ -1305,8 +1382,12 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
1305
1382
  offset: Number(args.offset) > 0 ? Math.floor(Number(args.offset)) : undefined,
1306
1383
  sort,
1307
1384
  maxNightly: ceiling,
1385
+ stay: asStay(args),
1386
+ rooms: typeof args.rooms === 'number' ? args.rooms : undefined,
1387
+ filters: filtersOf(args),
1308
1388
  };
1309
- let answer = await deps.backend.searchHotels(ask);
1389
+ const who = identity(deps, ctx, key, args);
1390
+ let answer = await deps.backend.searchHotels(ask, who);
1310
1391
  // The search the answer actually came from - the widened one, when it was
1311
1392
  // widened - so its next page is the next page of THAT.
1312
1393
  let asked = ask;
@@ -1326,10 +1407,13 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
1326
1407
  // comment was written to forbid, committed by the code under it.
1327
1408
  // `overBudget` is how the seam says the place and the words did match.
1328
1409
  let widened;
1329
- if (answer.total === 0 && !answer.overBudget) {
1410
+ // ⚠️ A FILTER THAT EMPTIED THE LIST IS NOT A SEARCH THAT FAILED either,
1411
+ // for the same reason as the ceiling below: the place matched.
1412
+ const filteredEmpty = !!ask.filters && (answer.beforeFilters ?? 0) > 0;
1413
+ if (answer.total === 0 && !answer.overBudget && !filteredEmpty) {
1330
1414
  const wider = widen(ask);
1331
1415
  if (wider) {
1332
- const second = await deps.backend.searchHotels(wider.ask);
1416
+ const second = await deps.backend.searchHotels(wider.ask, who);
1333
1417
  widened = { from: wider.from, to: wider.to, found: second.total > 0 };
1334
1418
  if (second.total > 0) {
1335
1419
  answer = second;
@@ -1380,10 +1464,21 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
1380
1464
  // only a country with many cities - was named, the answer is the PLACES:
1381
1465
  // regions, countries and cities, each with how many there are, and a city
1382
1466
  // is the way to its hotels. `all` still lists the hotels themselves.
1467
+ // The nights and rooms, as a search asks for them again: kept on a place,
1468
+ // a sort, a next page.
1469
+ const stayArgs = ask.stay
1470
+ ? {
1471
+ checkIn: ask.stay.checkIn,
1472
+ checkOut: ask.stay.checkOut,
1473
+ guests: ask.stay.guests,
1474
+ ...(ask.rooms && ask.rooms > 1 ? { rooms: ask.rooms } : {}),
1475
+ }
1476
+ : {};
1383
1477
  const namedCountry = !!asked.city &&
1384
1478
  facets?.countries.length === 1 &&
1385
1479
  facets.countries[0]?.name.toLowerCase() === asked.city.toLowerCase();
1386
1480
  const choosing = facets &&
1481
+ !asked.filters &&
1387
1482
  !asked.query &&
1388
1483
  sort === 'group' &&
1389
1484
  !asked.maxNightly &&
@@ -1411,7 +1506,8 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
1411
1506
  ...common(deps, key),
1412
1507
  total,
1413
1508
  scope: only,
1414
- ask: { city: asked.city },
1509
+ ask: { city: asked.city, ...stayArgs },
1510
+ ...(ask.stay ? { stay: ask.stay } : {}),
1415
1511
  },
1416
1512
  text: `${total} ${deps.vocab.brand} ${n} in ${only?.name}, in ${only?.cities.length} cities - the card lists the cities to choose from; the most are in ${top(only?.cities ?? [])}. \`all\` lists the ${n} themselves.`,
1417
1513
  };
@@ -1428,7 +1524,14 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
1428
1524
  .values(),
1429
1525
  ].sort((a, b) => b.count - a.count);
1430
1526
  return {
1431
- structured: { view: 'destinations', ...common(deps, key), total, regions, ask: {} },
1527
+ structured: {
1528
+ view: 'destinations',
1529
+ ...common(deps, key),
1530
+ total,
1531
+ regions,
1532
+ ask: { ...stayArgs },
1533
+ ...(ask.stay ? { stay: ask.stay } : {}),
1534
+ },
1432
1535
  text: `${total} ${deps.vocab.brand} ${n} in ${facets.cities.length} cities across ${countries.length} countries - the card sets them out by region, country and city for the guest to choose from; the most are in ${top(countries)}.`,
1433
1536
  };
1434
1537
  }
@@ -1448,23 +1551,48 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
1448
1551
  total: 0,
1449
1552
  query,
1450
1553
  ...(nearest.length ? { nearest, overBudget: answer.overBudget } : {}),
1554
+ // The way back out of a filter that emptied it: the same search,
1555
+ // with the chips to take one off.
1556
+ ...(filteredEmpty
1557
+ ? {
1558
+ ask: {
1559
+ ...(asked.query ? { query: asked.query } : {}),
1560
+ ...(asked.city ? { city: asked.city } : {}),
1561
+ ...(asked.filters ?? {}),
1562
+ ...stayArgs,
1563
+ },
1564
+ ...(answer.refine ? { refine: refineFor(answer.refine, asked.filters, 0) } : {}),
1565
+ beforeFilters: answer.beforeFilters,
1566
+ }
1567
+ : {}),
1451
1568
  },
1452
- text: nearest.length
1453
- ? `Nothing under ${formatMoney(money(toMinor(Number(ceiling), capCurrency), capCurrency))} a night. The cheapest that matched is ${answer.nearest?.[0]?.name} at ${cheapest ? formatMoney(cheapest) : '-'}${answer.overBudget && answer.overBudget > 1 ? `, of ${answer.overBudget} over the ceiling` : ''}.`
1454
- : [
1455
- `No ${deps.vocab.brand} ${deps.vocab.nounPlural} match that.`,
1456
- // What was tried and also failed is worth a sentence: it tells a
1457
- // guest the word itself is the problem, not the narrowing.
1458
- widened ? `${sentence(widened.to)} found nothing either.` : '',
1459
- ]
1460
- .filter(Boolean)
1461
- .join(' '),
1569
+ text: filteredEmpty
1570
+ ? `None of the ${answer.beforeFilters} ${deps.vocab.nounPlural} here has ${filterWords(asked.filters ?? {})} together. Taking one of those off widens it.`
1571
+ : nearest.length
1572
+ ? `Nothing under ${formatMoney(money(toMinor(Number(ceiling), capCurrency), capCurrency))} a night. The cheapest that matched is ${answer.nearest?.[0]?.name} at ${cheapest ? formatMoney(cheapest) : '-'}${answer.overBudget && answer.overBudget > 1 ? `, of ${answer.overBudget} over the ceiling` : ''}.`
1573
+ : [
1574
+ `No ${deps.vocab.brand} ${deps.vocab.nounPlural} match that.`,
1575
+ // What was tried and also failed is worth a sentence: it tells a
1576
+ // guest the word itself is the problem, not the narrowing.
1577
+ widened ? `${sentence(widened.to)} found nothing either.` : '',
1578
+ ]
1579
+ .filter(Boolean)
1580
+ .join(' '),
1462
1581
  };
1463
1582
  }
1464
1583
  const forCard = hotels.map((h) => ({
1465
1584
  ...h,
1466
1585
  summary: hotelText('description', h.name, h.summary, DESCRIPTION_MAX).text,
1467
1586
  fromNightly: h.fromNightly ? formatMoney(h.fromNightly) : undefined,
1587
+ ...(h.forStay
1588
+ ? {
1589
+ forStay: {
1590
+ ...h.forStay,
1591
+ total: formatMoney(h.forStay.total),
1592
+ nightly: formatMoney(h.forStay.nightly),
1593
+ },
1594
+ }
1595
+ : {}),
1468
1596
  }));
1469
1597
  // The whole shortlist, never one pick: a city is not a hotel.
1470
1598
  const where = cities.length === 1 ? ` in ${cities[0]}` : '';
@@ -1486,6 +1614,14 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
1486
1614
  answer.overBudget && hotels.length
1487
1615
  ? `${answer.overBudget} more matched and came in over the ceiling.`
1488
1616
  : '',
1617
+ asked.filters ? `Filtered to ${filterWords(asked.filters)}.` : '',
1618
+ // What the prices on a dated list are, in one line - and how many cannot
1619
+ // be had at all, so a sold-out hotel is not described as a choice.
1620
+ ask.stay
1621
+ ? `Priced for ${ask.stay.checkIn} to ${ask.stay.checkOut}${ask.rooms && ask.rooms > 1 ? `, ${ask.rooms} rooms` : ''}: each price is that ${deps.vocab.noun}'s cheapest room for the whole stay, taxes included${hotels.some((h) => h.noRoomsForStay)
1622
+ ? `; ${hotels.filter((h) => h.noRoomsForStay).length} in this answer have no room for those nights`
1623
+ : ''}.`
1624
+ : '',
1489
1625
  ].filter(Boolean);
1490
1626
  return {
1491
1627
  structured: {
@@ -1505,14 +1641,20 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
1505
1641
  ...(asked.city ? { city: asked.city } : {}),
1506
1642
  ...(applied !== 'group' ? { sort: applied } : {}),
1507
1643
  ...(asked.maxNightly ? { maxNightly: asked.maxNightly } : {}),
1644
+ ...(asked.filters ?? {}),
1645
+ ...stayArgs,
1508
1646
  },
1647
+ ...(ask.stay ? { stay: ask.stay } : {}),
1648
+ ...(answer.refine
1649
+ ? { refine: refineFor(answer.refine, asked.filters, total, !!ask.stay) }
1650
+ : {}),
1509
1651
  ...(places.length ? { places } : {}),
1510
1652
  ...(answer.overBudget ? { overBudget: answer.overBudget } : {}),
1511
1653
  ...(widened?.found ? { widened } : {}),
1512
1654
  ...(answer.currencies.length > 1 ? { currencies: answer.currencies } : {}),
1513
1655
  },
1514
1656
  text: [
1515
- `${total} ${deps.vocab.brand} ${total === 1 ? deps.vocab.noun : deps.vocab.nounPlural}${where}`,
1657
+ `${total} ${deps.vocab.brand} ${total === 1 ? deps.vocab.noun : deps.vocab.nounPlural}${where}.`,
1516
1658
  // ⚠️ NO "BELOW", NO "TAP". This sentence is read out by a voice
1517
1659
  // assistant as often as it is drawn next to a card, and a guest whose
1518
1660
  // host has no card at all cannot tap the one it tells them to tap. The
@@ -1654,10 +1796,10 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
1654
1796
  // transfer had nowhere to go. Asked with a booking, this lists that
1655
1797
  // hotel's add-ons for that booking's nights, with what it already holds.
1656
1798
  const code = str(args.booking);
1657
- const booked = code
1658
- ? (await deps.backend.listBookings(identity(deps, ctx, key, args), 50)).find((b) => (b.confirmationCode === code || b.id === code) &&
1659
- (b.status === 'requested' || b.status === 'confirmed'))
1660
- : undefined;
1799
+ const found = code
1800
+ ? await deps.backend.findBooking(code, identity(deps, ctx, key, args))
1801
+ : null;
1802
+ const booked = found && (found.status === 'requested' || found.status === 'confirmed') ? found : undefined;
1661
1803
  if (code && !booked) {
1662
1804
  return {
1663
1805
  structured: { view: 'booking-not-found', ...common(deps, key), query: code },
@@ -1942,17 +2084,27 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
1942
2084
  }
1943
2085
  }
1944
2086
  if (name === t.listBookings) {
1945
- const bookings = await deps.backend.listBookings(identity(deps, ctx, key, args), typeof args.limit === 'number' ? args.limit : undefined);
2087
+ const who = identity(deps, ctx, key, args);
2088
+ const here = await deps.backend.listBookings(who, typeof args.limit === 'number' ? args.limit : undefined);
2089
+ // Elsewhere, only with the code AND the email - see `listBookings`.
2090
+ const code = str(args.booking);
2091
+ const there = code && str(args.email) ? await deps.backend.findBooking(code, who) : null;
2092
+ const bookings = there && !here.some((b) => b.id === there.id) ? [there, ...here] : here;
1946
2093
  return {
1947
2094
  structured: {
1948
2095
  view: 'bookings',
1949
2096
  ...common(deps, key),
1950
2097
  bookings: bookings.map((b) => ({ ...b, quote: quoteForCard(b.quote) })),
1951
2098
  count: bookings.length,
2099
+ // What was looked for, so a card can say THAT did not match rather
2100
+ // than "nothing in this conversation".
2101
+ ...(code && !there ? { notFound: code } : {}),
1952
2102
  },
1953
2103
  text: bookings.length
1954
2104
  ? bookings.map(bookingLine).join('\n')
1955
- : 'No bookings here. A booking made in another conversation can be found with the email it was made with.',
2105
+ : code
2106
+ ? 'No booking matches that confirmation code and email. Both are needed, as they are on the confirmation.'
2107
+ : 'No bookings in this conversation. A booking made elsewhere is found with its confirmation code and the email it was made with.',
1956
2108
  };
1957
2109
  }
1958
2110
  if (name === t.previewChange || name === t.changeBooking) {