@jsm-mit/sultana-agent-tools-package 0.11.0 → 0.13.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/dist/customer/persona.d.ts.map +1 -1
- package/dist/customer/persona.js +3 -1
- package/dist/customer/texts.d.ts +4 -2
- package/dist/customer/texts.d.ts.map +1 -1
- package/dist/customer/texts.js +6 -4
- package/dist/customer/tools.d.ts.map +1 -1
- package/dist/customer/tools.js +31 -8
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persona.d.ts","sourceRoot":"","sources":["../../src/customer/persona.ts"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAE5D;
|
|
1
|
+
{"version":3,"file":"persona.d.ts","sourceRoot":"","sources":["../../src/customer/persona.ts"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAE5D;AAoCD,yEAAyE;AACzE,eAAO,MAAM,yBAAyB,QAAgC,CAAC"}
|
package/dist/customer/persona.js
CHANGED
|
@@ -31,7 +31,9 @@ Zasady pracy:
|
|
|
31
31
|
- Typ usługi ustal przez find_service_type (np. „strzyżenie męskie”). Nigdy nie wymyślaj id.
|
|
32
32
|
Gdy klient pyta ogólnie („paznokcie”) i pasuje kilka typów, szukaj po wszystkich naraz
|
|
33
33
|
(serviceTypeIds w search_slots). Dopytaj tylko wtedy, gdy typy znaczą wyraźnie różne rzeczy.
|
|
34
|
-
- Terminy znajdź przez search_slots: bez daty = jak najszybciej,
|
|
34
|
+
- Terminy znajdź przez search_slots: bez daty = jak najszybciej, sama date = najwcześniejsze terminy
|
|
35
|
+
tego dnia („w poniedziałek”), date i time = okolice tej godziny. Nie dopytuj o godzinę, gdy klient
|
|
36
|
+
jej nie podał.
|
|
35
37
|
Dzisiejszą datę weź z get_current_date — „jutro o 10” przelicz na RRRR-MM-DD i GG:MM.
|
|
36
38
|
- Salony z wyniku search_slots aplikacja pokazuje pod Twoją odpowiedzią jako karty z godzinami
|
|
37
39
|
do kliknięcia. Nie wypisuj ich listy: w jednym–dwóch zdaniach powiedz, ile salonów znalazłeś
|
package/dist/customer/texts.d.ts
CHANGED
|
@@ -25,13 +25,15 @@ export interface CustomerTexts {
|
|
|
25
25
|
bookingOff(problem: string): string;
|
|
26
26
|
badDay(field: string): string;
|
|
27
27
|
badTime(field: string): string;
|
|
28
|
-
|
|
28
|
+
timeNeedsDate: string;
|
|
29
29
|
serviceTypeRequired: string;
|
|
30
30
|
tooManyServiceTypes: (max: number) => string;
|
|
31
31
|
leadSoon: string;
|
|
32
32
|
leadExtended: string;
|
|
33
33
|
leadAround(day: string, time: string): string;
|
|
34
|
-
|
|
34
|
+
leadDay(day: string): string;
|
|
35
|
+
/** `asked` and `now` as "RRRR-MM-DD GG:MM" — the model sees which date it got wrong. */
|
|
36
|
+
momentPassed(asked: string, now: string): string;
|
|
35
37
|
probesFailed: string;
|
|
36
38
|
nothingFree(radius: string): string;
|
|
37
39
|
found(lead: string, shown: number, total: number): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"texts.d.ts","sourceRoot":"","sources":["../../src/customer/texts.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC1B,QAAQ,EAAE;QACN,eAAe,EAAE,MAAM,CAAC;QACxB,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,IAAI,EAAE,MAAM,CAAC;QACb,gBAAgB,EAAE,MAAM,CAAC;QACzB,MAAM,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAE1B,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAE/B,
|
|
1
|
+
{"version":3,"file":"texts.d.ts","sourceRoot":"","sources":["../../src/customer/texts.ts"],"names":[],"mappings":"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"}
|
package/dist/customer/texts.js
CHANGED
|
@@ -16,13 +16,14 @@ const PL = {
|
|
|
16
16
|
bookingOff: (problem) => `Rezerwacje są chwilowo wyłączone: ${problem}.`,
|
|
17
17
|
badDay: (field) => `Pole "${field}" musi być datą w formacie RRRR-MM-DD.`,
|
|
18
18
|
badTime: (field) => `Pole "${field}" musi być godziną w formacie GG:MM.`,
|
|
19
|
-
|
|
19
|
+
timeNeedsDate: "Godzina (time) wymaga dnia (date). Sam dzień bez godziny szuka najwcześniejszych terminów tego dnia.",
|
|
20
20
|
serviceTypeRequired: "Podaj serviceTypeId albo serviceTypeIds (id z find_service_type).",
|
|
21
21
|
tooManyServiceTypes: (max) => `Najwyżej ${max} typów usług w jednym szukaniu.`,
|
|
22
22
|
leadSoon: "Najbliższe wolne terminy:",
|
|
23
23
|
leadExtended: "W najbliższej godzinie nic wolnego, na które klient zdąży. Najbliższe terminy:",
|
|
24
24
|
leadAround: (day, time) => `Wolne terminy w okolicach ${day} ${time}:`,
|
|
25
|
-
|
|
25
|
+
leadDay: (day) => `Najwcześniejsze wolne terminy w dniu ${day}:`,
|
|
26
|
+
momentPassed: (asked, now) => `Termin ${asked} już minął — teraz jest ${now}. Sprawdź, czy dobrze przeliczyłeś datę (np. „jutro” = dzień po ${now.slice(0, 10)}), i szukaj jeszcze raz. Klienta pytaj o inny termin tylko wtedy, gdy to on podał godzinę, która minęła.`,
|
|
26
27
|
probesFailed: "Nie udało się sprawdzić terminów — kanister nie odpowiadał. Spróbuj ponownie za chwilę.",
|
|
27
28
|
nothingFree: (radius) => `Nic wolnego w promieniu ${radius}. Zaproponuj inną godzinę, inny dzień albo większy promień.`,
|
|
28
29
|
found: (lead, shown, total) => `${lead} ${shown} salon(y).${total > shown ? ` Pokazuję ${shown} z ${total} salonów.` : ""} Terminy to początek wizyty; inne godziny w danym salonie sprawdź przez get_free_times.`,
|
|
@@ -73,13 +74,14 @@ const EN = {
|
|
|
73
74
|
bookingOff: (problem) => `Booking is switched off for the moment: ${problem}.`,
|
|
74
75
|
badDay: (field) => `Field "${field}" must be a date in the YYYY-MM-DD format.`,
|
|
75
76
|
badTime: (field) => `Field "${field}" must be a time in the HH:MM format.`,
|
|
76
|
-
|
|
77
|
+
timeNeedsDate: "A time needs a date. A date alone searches for that day's earliest slots.",
|
|
77
78
|
serviceTypeRequired: "Give serviceTypeId or serviceTypeIds (ids from find_service_type).",
|
|
78
79
|
tooManyServiceTypes: (max) => `At most ${max} service types in one search.`,
|
|
79
80
|
leadSoon: "The nearest free slots:",
|
|
80
81
|
leadExtended: "Nothing free within the next hour that the customer can reach in time. The nearest slots:",
|
|
81
82
|
leadAround: (day, time) => `Free slots around ${day} ${time}:`,
|
|
82
|
-
|
|
83
|
+
leadDay: (day) => `The earliest free slots on ${day}:`,
|
|
84
|
+
momentPassed: (asked, now) => `${asked} has already passed — it is ${now} now. Check the date you computed ("tomorrow" = the day after ${now.slice(0, 10)}) and search again. Ask the customer for another time only when the time they gave has really passed.`,
|
|
83
85
|
probesFailed: "The slots could not be checked — the canister did not answer. Try again in a moment.",
|
|
84
86
|
nothingFree: (radius) => `Nothing free within ${radius}. Suggest another time, another day or a larger radius.`,
|
|
85
87
|
found: (lead, shown, total) => `${lead} ${shown} salon(s).${total > shown ? ` Showing ${shown} of ${total} salons.` : ""} A slot is the start of the visit; check other times in a salon with get_free_times.`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/customer/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAI7D,OAAO,EAAW,KAAK,SAAS,EAAmB,MAAM,aAAa,CAAC;AACvE,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,KAAK,EAAgC,gBAAgB,EAAkD,MAAM,yBAAyB,CAAC;AAe9I,MAAM,WAAW,oBAAoB;IACjC,IAAI,EAAE,gBAAgB,CAAC;IACvB,kCAAkC;IAClC,GAAG,EAAE,MAAM,IAAI,CAAC;IAChB,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,qFAAqF;IACrF,KAAK,CAAC,EAAE,aAAa,CAAC;CACzB;
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/customer/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAI7D,OAAO,EAAW,KAAK,SAAS,EAAmB,MAAM,aAAa,CAAC;AACvE,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,KAAK,EAAgC,gBAAgB,EAAkD,MAAM,yBAAyB,CAAC;AAe9I,MAAM,WAAW,oBAAoB;IACjC,IAAI,EAAE,gBAAgB,CAAC;IACvB,kCAAkC;IAClC,GAAG,EAAE,MAAM,IAAI,CAAC;IAChB,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,qFAAqF;IACrF,KAAK,CAAC,EAAE,aAAa,CAAC;CACzB;AAqED,wBAAgB,eAAe,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,CA4GxE;AAUD,wBAAgB,YAAY,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAmCrE;AAkCD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,CA4CzE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,iBAAiB,GAAG,SAAS,GAAG,SAAS,CAsF1H;AA+CD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAqC/E;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,iBAAiB,GAAG,SAAS,GAAG,SAAS,CA6C5H"}
|
package/dist/customer/tools.js
CHANGED
|
@@ -21,6 +21,8 @@ const MAX_RADIUS_KM = 50;
|
|
|
21
21
|
const MAX_SERVICE_TYPE_IDS = 30;
|
|
22
22
|
const MAX_SALONS_SHOWN = 6;
|
|
23
23
|
const MAX_TIMES_SHOWN = 6;
|
|
24
|
+
/** A day asked without an hour is searched from this time of that morning (minutes). */
|
|
25
|
+
const DAY_SEARCH_FROM_MINUTES = 6 * 60;
|
|
24
26
|
/** "Pick a time": the asked moment, a little before it and the hour after it. */
|
|
25
27
|
const AROUND_TIME_MINUTES = [-30, -15, 0, 15, 30, 45, 60];
|
|
26
28
|
const CONFIRMED_FIELD = {
|
|
@@ -70,14 +72,14 @@ export function searchSlotsTool(context) {
|
|
|
70
72
|
return {
|
|
71
73
|
name: "search_slots",
|
|
72
74
|
progress: texts.progress.searchSlots,
|
|
73
|
-
description: "Szuka salonów w okolicy klienta, które mają wolny termin na usługę danego typu. Szuka wokół pozycji z aplikacji; gdy klient wskazuje inne miejsce („koło Kolejowej w Warszawie”), podaj lat i lng tego miejsca — odległości liczą się wtedy od niego. Bez date i time szuka „jak najszybciej” (najbliższa godzina, a gdy nic nie ma — do 24 godzin naprzód). Z date i time sprawdza okolice tej godziny (pół godziny wcześniej do godziny później). Zwraca salony z usługami (serviceId, cena, czas) i wolnymi terminami. Odległość, cenę i czas trwania dostajesz jako gotowy tekst — przepisz go bez zmian. Id typów weź z find_service_type: jeden typ w serviceTypeId albo kilka w serviceTypeIds — kilka typów to jedno szukanie, salon z usługą pasującą do paru typów wraca raz.",
|
|
75
|
+
description: "Szuka salonów w okolicy klienta, które mają wolny termin na usługę danego typu. Szuka wokół pozycji z aplikacji; gdy klient wskazuje inne miejsce („koło Kolejowej w Warszawie”), podaj lat i lng tego miejsca — odległości liczą się wtedy od niego. Bez date i time szuka „jak najszybciej” (najbliższa godzina, a gdy nic nie ma — do 24 godzin naprzód). Z samą date (np. „w poniedziałek”) szuka najwcześniejszych wolnych terminów tego dnia. Z date i time sprawdza okolice tej godziny (pół godziny wcześniej do godziny później). Zwraca salony z usługami (serviceId, cena, czas) i wolnymi terminami. Odległość, cenę i czas trwania dostajesz jako gotowy tekst — przepisz go bez zmian. Id typów weź z find_service_type: jeden typ w serviceTypeId albo kilka w serviceTypeIds — kilka typów to jedno szukanie, salon z usługą pasującą do paru typów wraca raz.",
|
|
74
76
|
parameters: {
|
|
75
77
|
type: "object",
|
|
76
78
|
properties: {
|
|
77
79
|
serviceTypeId: { type: "string", description: "Id typu usługi z find_service_type." },
|
|
78
80
|
serviceTypeIds: { type: "array", items: { type: "string" }, description: `Kilka id typów usług z find_service_type (najwyżej ${MAX_SERVICE_TYPE_IDS}), gdy klient pyta ogólnie i pasuje kilka typów. Zamiast serviceTypeId.` },
|
|
79
|
-
date: { type: "string", description: "Dzień RRRR-MM-DD.
|
|
80
|
-
time: { type: "string", description: "Godzina GG:MM
|
|
81
|
+
date: { type: "string", description: "Dzień RRRR-MM-DD. Bez time: najwcześniejsze wolne terminy tego dnia. Pomiń, żeby szukać jak najszybciej." },
|
|
82
|
+
time: { type: "string", description: "Godzina GG:MM, tylko razem z date: szuka w okolicach tej godziny." },
|
|
81
83
|
radiusKm: { type: "number", description: `Promień szukania w km, domyślnie ${DEFAULT_RADIUS_KM}, najwyżej ${MAX_RADIUS_KM}.` },
|
|
82
84
|
lat: { type: "number", description: "Szerokość geograficzna miejsca wskazanego przez klienta. Tylko razem z lng; pomiń oba, żeby szukać wokół pozycji z aplikacji." },
|
|
83
85
|
lng: { type: "number", description: "Długość geograficzna miejsca wskazanego przez klienta. Tylko razem z lat." },
|
|
@@ -102,8 +104,8 @@ export function searchSlotsTool(context) {
|
|
|
102
104
|
const dayText = readOptionalString(args, "date");
|
|
103
105
|
const timeText = readOptionalString(args, "time");
|
|
104
106
|
const now = context.now();
|
|
105
|
-
if (
|
|
106
|
-
throw new ArgumentError(texts.
|
|
107
|
+
if (dayText === undefined && timeText !== undefined) {
|
|
108
|
+
throw new ArgumentError(texts.timeNeedsDate);
|
|
107
109
|
}
|
|
108
110
|
let found;
|
|
109
111
|
let lead;
|
|
@@ -111,6 +113,26 @@ export function searchSlotsTool(context) {
|
|
|
111
113
|
found = await port.searchAsap(location, serviceTypeIds, radiusKm, now);
|
|
112
114
|
lead = found.phase === "extended" ? texts.leadExtended : texts.leadSoon;
|
|
113
115
|
}
|
|
116
|
+
else if (timeText === undefined) {
|
|
117
|
+
// A day without an hour ("on Monday"): the day's earliest slots — the
|
|
118
|
+
// "as soon as possible" search run from that morning (from now, today),
|
|
119
|
+
// keeping only what falls on that day.
|
|
120
|
+
const day = readDay(texts, args, "date");
|
|
121
|
+
const from = Math.max(momentOf(day, DAY_SEARCH_FROM_MINUTES), now.getTime());
|
|
122
|
+
const dayEnd = momentOf(day, 24 * 60);
|
|
123
|
+
if (from >= dayEnd)
|
|
124
|
+
return err("invalid_arguments", texts.momentPassed(`${day} 23:59`, localStampOf(now)));
|
|
125
|
+
const all = await port.searchAsap(location, serviceTypeIds, radiusKm, new Date(from));
|
|
126
|
+
const salons = all.salons
|
|
127
|
+
.map((salon) => {
|
|
128
|
+
const availableAt = salon.availableAt.filter((at) => at < dayEnd);
|
|
129
|
+
const soonestAt = salon.soonestAt !== null && salon.soonestAt < dayEnd ? salon.soonestAt : (availableAt[0] ?? null);
|
|
130
|
+
return { ...salon, availableAt, soonestAt };
|
|
131
|
+
})
|
|
132
|
+
.filter((salon) => salon.availableAt.length > 0);
|
|
133
|
+
found = { ...all, salons };
|
|
134
|
+
lead = texts.leadDay(day);
|
|
135
|
+
}
|
|
114
136
|
else {
|
|
115
137
|
const day = readDay(texts, args, "date");
|
|
116
138
|
const at = readTimeMinutes(texts, args, "time");
|
|
@@ -118,7 +140,7 @@ export function searchSlotsTool(context) {
|
|
|
118
140
|
// Only moments of the asked day's grid that are still ahead.
|
|
119
141
|
.filter((moment) => moment > now.getTime() && onSlotGrid(new Date(moment).getMinutes()));
|
|
120
142
|
if (moments.length === 0)
|
|
121
|
-
return err("invalid_arguments", texts.momentPassed);
|
|
143
|
+
return err("invalid_arguments", texts.momentPassed(`${day} ${timeOfMinutes(at)}`, localStampOf(now)));
|
|
122
144
|
found = await port.searchAtMoments(location, serviceTypeIds, radiusKm, moments);
|
|
123
145
|
lead = texts.leadAround(day, timeOfMinutes(at));
|
|
124
146
|
}
|
|
@@ -307,8 +329,9 @@ export function bookAppointmentTool(context, confirmations) {
|
|
|
307
329
|
if (refusal)
|
|
308
330
|
return refusal;
|
|
309
331
|
const startAt = momentOf(day, startMinutes);
|
|
310
|
-
|
|
311
|
-
|
|
332
|
+
const now = context.now();
|
|
333
|
+
if (startAt <= now.getTime())
|
|
334
|
+
return err("invalid_arguments", texts.momentPassed(`${day} ${timeOfMinutes(startMinutes)}`, localStampOf(now)));
|
|
312
335
|
const service = await findService(port, texts, salonId, serviceId);
|
|
313
336
|
if ("status" in service)
|
|
314
337
|
return service;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsm-mit/sultana-agent-tools-package",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Agent tool layer for Sultana: framework-agnostic tools over the Sultana core canister wrapper — a salon owner's set (services, promos, schedule) and a customer's set (search, free times, booking), each with a read-only set that needs no identity.",
|
|
5
5
|
"homepage": "https://github.com/JSM-Sultana/sultana-agent-tools-package#readme",
|
|
6
6
|
"bugs": {
|