@jsm-mit/sultana-agent-tools-package 0.2.0 → 0.4.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.
Files changed (44) hide show
  1. package/README.md +106 -8
  2. package/dist/confirmations.d.ts +61 -0
  3. package/dist/confirmations.d.ts.map +1 -0
  4. package/dist/confirmations.js +100 -0
  5. package/dist/create-salon-tools.d.ts +13 -2
  6. package/dist/create-salon-tools.d.ts.map +1 -1
  7. package/dist/create-salon-tools.js +10 -4
  8. package/dist/customer/create-customer-tools.d.ts +38 -0
  9. package/dist/customer/create-customer-tools.d.ts.map +1 -0
  10. package/dist/customer/create-customer-tools.js +47 -0
  11. package/dist/customer/customer-core-port.d.ts +94 -0
  12. package/dist/customer/customer-core-port.d.ts.map +1 -0
  13. package/dist/customer/customer-core-port.js +1 -0
  14. package/dist/customer/ic-customer-core-port.d.ts +45 -0
  15. package/dist/customer/ic-customer-core-port.d.ts.map +1 -0
  16. package/dist/customer/ic-customer-core-port.js +132 -0
  17. package/dist/customer/persona.d.ts +6 -0
  18. package/dist/customer/persona.d.ts.map +1 -0
  19. package/dist/customer/persona.js +27 -0
  20. package/dist/customer/time.d.ts +27 -0
  21. package/dist/customer/time.d.ts.map +1 -0
  22. package/dist/customer/time.js +113 -0
  23. package/dist/customer/tools.d.ts +17 -0
  24. package/dist/customer/tools.d.ts.map +1 -0
  25. package/dist/customer/tools.js +415 -0
  26. package/dist/index.d.ts +10 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +5 -0
  29. package/dist/persona.d.ts +4 -1
  30. package/dist/persona.d.ts.map +1 -1
  31. package/dist/persona.js +18 -7
  32. package/dist/tools/promos.d.ts +2 -1
  33. package/dist/tools/promos.d.ts.map +1 -1
  34. package/dist/tools/promos.js +79 -39
  35. package/dist/tools/schedule.d.ts +2 -1
  36. package/dist/tools/schedule.d.ts.map +1 -1
  37. package/dist/tools/schedule.js +51 -31
  38. package/dist/tools/services.d.ts +4 -2
  39. package/dist/tools/services.d.ts.map +1 -1
  40. package/dist/tools/services.js +75 -30
  41. package/dist/tools/write-gate.d.ts +29 -0
  42. package/dist/tools/write-gate.d.ts.map +1 -0
  43. package/dist/tools/write-gate.js +37 -0
  44. package/package.json +4 -3
@@ -1,7 +1,8 @@
1
1
  import { MAX_ACTIVE_PROMOS_PER_SALON, MAX_PROMOS_PER_SALON } from "@jsm-mit/sultana-core-motoko-package";
2
2
  import { toToolError } from "../errors.js";
3
- import { err, needsConfirmation, ok } from "../types.js";
3
+ import { err, ok } from "../types.js";
4
4
  import { ArgumentError, phrasesMatch, readBoolean, readOptionalNumber, readOptionalString, readOptionalStringArray, readString, } from "./args.js";
5
+ import { writeGate } from "./write-gate.js";
5
6
  const MAX_PROMO_NAME_LENGTH = 60;
6
7
  const MAX_TEXT_LINES = 2;
7
8
  const MAX_CHIPS = 2;
@@ -9,17 +10,17 @@ const CONFIRMED_FIELD = {
9
10
  type: "boolean",
10
11
  description: "false dla podglądu: narzędzie nic nie zapisze i odda opis zmiany do zatwierdzenia przez właściciela. true dopiero po potwierdzeniu.",
11
12
  };
12
- export function createPromoTools(port) {
13
+ export function createPromoTools(port, confirmations) {
13
14
  return [
14
15
  listMediaTool(port),
15
16
  listPromosTool(port),
16
- addPromoTool(port),
17
- updatePromoTool(port),
18
- setPromoActiveTool(port),
19
- removePromoTool(port),
17
+ addPromoTool(port, confirmations),
18
+ updatePromoTool(port, confirmations),
19
+ setPromoActiveTool(port, confirmations),
20
+ removePromoTool(port, confirmations),
20
21
  listDiscountCodesTool(port),
21
- addDiscountCodeTool(port),
22
- removeDiscountCodeTool(port),
22
+ addDiscountCodeTool(port, confirmations),
23
+ removeDiscountCodeTool(port, confirmations),
23
24
  ];
24
25
  }
25
26
  function listMediaTool(port) {
@@ -81,12 +82,13 @@ export function listPromosTool(port, access = "owner") {
81
82
  },
82
83
  };
83
84
  }
84
- function addPromoTool(port) {
85
+ function addPromoTool(port, confirmations) {
86
+ const gate = writeGate("add_promo", confirmations);
85
87
  return {
86
88
  name: "add_promo",
87
89
  progress: "Zakładam promocję…",
88
90
  description: "Zakłada nową promocję na materiale z biblioteki. Najpierw wywołaj list_media i wybierz assetId. Nowa promocja jest nieaktywna — włącz ją potem przez set_promo_active.",
89
- parameters: {
91
+ parameters: gate.parameters({
90
92
  type: "object",
91
93
  properties: {
92
94
  name: { type: "string", description: `Nazwa kampanii, maksymalnie ${MAX_PROMO_NAME_LENGTH} znaków.` },
@@ -104,13 +106,19 @@ function addPromoTool(port) {
104
106
  },
105
107
  required: ["name", "assetId", "confirmed"],
106
108
  additionalProperties: false,
107
- },
109
+ }),
108
110
  execute: async (args) => {
109
111
  try {
112
+ const refusal = gate.refusal(args);
113
+ if (refusal)
114
+ return refusal;
110
115
  const input = await buildPromoInput(port, args, null);
111
116
  if (!readBoolean(args, "confirmed")) {
112
- return needsConfirmation(`Założę promocję ${await describePromo(port, input)}.`, { ...args, confirmed: true });
117
+ return gate.preview(`Założę promocję ${await describePromo(port, input)}.`, args);
113
118
  }
119
+ const refused = gate.redeem(args);
120
+ if (refused)
121
+ return refused;
114
122
  const promoId = await port.addPromo(input);
115
123
  return ok(`Założono promocję „${input.name}”. Jest jeszcze nieaktywna.`, { promoId });
116
124
  }
@@ -122,12 +130,13 @@ function addPromoTool(port) {
122
130
  },
123
131
  };
124
132
  }
125
- function updatePromoTool(port) {
133
+ function updatePromoTool(port, confirmations) {
134
+ const gate = writeGate("update_promo", confirmations);
126
135
  return {
127
136
  name: "update_promo",
128
137
  progress: "Zmieniam promocję…",
129
138
  description: "Zmienia istniejącą promocję. Podaj tylko pola, które mają się zmienić — reszta zostanie zachowana. Promocja zachowuje swoje miejsce w kanale, więc poprawka nie restartuje kampanii.",
130
- parameters: {
139
+ parameters: gate.parameters({
131
140
  type: "object",
132
141
  properties: {
133
142
  promoId: { type: "string", description: "Id promocji z list_promos." },
@@ -142,20 +151,23 @@ function updatePromoTool(port) {
142
151
  },
143
152
  required: ["promoId", "confirmed"],
144
153
  additionalProperties: false,
145
- },
154
+ }),
146
155
  execute: async (args) => {
147
156
  try {
157
+ const refusal = gate.refusal(args);
158
+ if (refusal)
159
+ return refusal;
148
160
  const promoId = readString(args, "promoId");
149
161
  const current = await findPromo(port, promoId);
150
162
  if (!current)
151
163
  return err("not_found", `W tym salonie nie ma promocji o id ${promoId}.`);
152
164
  const input = await buildPromoInput(port, args, current);
153
165
  if (!readBoolean(args, "confirmed")) {
154
- return needsConfirmation(`Zmienię promocję „${current.name}” na: ${await describePromo(port, input)}.`, {
155
- ...args,
156
- confirmed: true,
157
- });
166
+ return gate.preview(`Zmienię promocję „${current.name}” na: ${await describePromo(port, input)}.`, args);
158
167
  }
168
+ const refused = gate.redeem(args);
169
+ if (refused)
170
+ return refused;
159
171
  await port.updatePromo(promoId, input);
160
172
  return ok(`Zmieniono promocję „${input.name}”.`);
161
173
  }
@@ -167,12 +179,13 @@ function updatePromoTool(port) {
167
179
  },
168
180
  };
169
181
  }
170
- function setPromoActiveTool(port) {
182
+ function setPromoActiveTool(port, confirmations) {
183
+ const gate = writeGate("set_promo_active", confirmations);
171
184
  return {
172
185
  name: "set_promo_active",
173
186
  progress: "Zmieniam status promocji…",
174
187
  description: `Włącza promocję w kanale albo ją wyłącza. Naraz może być aktywnych najwyżej ${MAX_ACTIVE_PROMOS_PER_SALON} — żeby włączyć kolejną, najpierw wyłącz inną.`,
175
- parameters: {
188
+ parameters: gate.parameters({
176
189
  type: "object",
177
190
  properties: {
178
191
  promoId: { type: "string", description: "Id promocji z list_promos." },
@@ -181,9 +194,12 @@ function setPromoActiveTool(port) {
181
194
  },
182
195
  required: ["promoId", "active", "confirmed"],
183
196
  additionalProperties: false,
184
- },
197
+ }),
185
198
  execute: async (args) => {
186
199
  try {
200
+ const refusal = gate.refusal(args);
201
+ if (refusal)
202
+ return refusal;
187
203
  const promoId = readString(args, "promoId");
188
204
  const current = await findPromo(port, promoId);
189
205
  if (!current)
@@ -191,11 +207,11 @@ function setPromoActiveTool(port) {
191
207
  const active = readBoolean(args, "active");
192
208
  const verb = active ? "włączę" : "wyłączę";
193
209
  if (!readBoolean(args, "confirmed")) {
194
- return needsConfirmation(`${verb[0].toUpperCase()}${verb.slice(1)} promocję „${current.name}”.`, {
195
- ...args,
196
- confirmed: true,
197
- });
210
+ return gate.preview(`${verb[0].toUpperCase()}${verb.slice(1)} promocję „${current.name}”.`, args);
198
211
  }
212
+ const refused = gate.redeem(args);
213
+ if (refused)
214
+ return refused;
199
215
  await port.setPromoActive(promoId, active);
200
216
  return ok(`Promocja „${current.name}” jest teraz ${active ? "aktywna" : "wyłączona"}.`);
201
217
  }
@@ -207,12 +223,13 @@ function setPromoActiveTool(port) {
207
223
  },
208
224
  };
209
225
  }
210
- function removePromoTool(port) {
226
+ function removePromoTool(port, confirmations) {
227
+ const gate = writeGate("remove_promo", confirmations);
211
228
  return {
212
229
  name: "remove_promo",
213
230
  progress: "Kasuję promocję…",
214
231
  description: "Kasuje promocję z biblioteki. Nieodwracalne — właściciel musi przepisać jej nazwę w confirmationPhrase. Żeby tylko zdjąć ją z kanału, użyj set_promo_active.",
215
- parameters: {
232
+ parameters: gate.parameters({
216
233
  type: "object",
217
234
  properties: {
218
235
  promoId: { type: "string", description: "Id promocji z list_promos." },
@@ -221,20 +238,29 @@ function removePromoTool(port) {
221
238
  },
222
239
  required: ["promoId", "confirmed"],
223
240
  additionalProperties: false,
224
- },
241
+ }),
225
242
  execute: async (args) => {
226
243
  try {
227
244
  const promoId = readString(args, "promoId");
245
+ // No confirmationPhrase in the echo: the name is the owner's to type, so a model that
246
+ // simply sends the echo back cannot delete anything.
247
+ const echo = { promoId };
248
+ const refusal = gate.refusal(args, echo);
249
+ if (refusal)
250
+ return refusal;
228
251
  const current = await findPromo(port, promoId);
229
252
  if (!current)
230
253
  return err("not_found", `W tym salonie nie ma promocji o id ${promoId}.`);
231
254
  if (!readBoolean(args, "confirmed")) {
232
- return needsConfirmation(`Skasuję promocję „${current.name}”. Tego nie da się cofnąć — poproś właściciela o przepisanie nazwy. Jeśli chodzi tylko o zdjęcie z kanału, lepiej ją wyłączyć.`, { promoId, confirmationPhrase: current.name, confirmed: true });
255
+ return gate.preview(`Skasuję promocję „${current.name}”. Tego nie da się cofnąć — poproś właścicielkę, żeby sama napisała dokładną nazwę promocji, i wstaw ją w confirmationPhrase. Jeśli chodzi tylko o zdjęcie z kanału, lepiej ją wyłączyć.`, echo);
233
256
  }
234
257
  const phrase = readOptionalString(args, "confirmationPhrase");
235
258
  if (!phrase || !phrasesMatch(phrase, current.name)) {
236
- return err("invalid_arguments", `Aby skasować promocję, właściciel musi przepisać jej nazwę: „${current.name}”.`);
259
+ return err("invalid_arguments", `Aby skasować promocję, właścicielka musi sama napisać jej dokładną nazwę: „${current.name}”.`);
237
260
  }
261
+ const refused = gate.redeem(args, echo);
262
+ if (refused)
263
+ return refused;
238
264
  await port.removePromo(promoId);
239
265
  return ok(`Skasowano promocję „${current.name}”.`);
240
266
  }
@@ -265,12 +291,13 @@ function listDiscountCodesTool(port) {
265
291
  },
266
292
  };
267
293
  }
268
- function addDiscountCodeTool(port) {
294
+ function addDiscountCodeTool(port, confirmations) {
295
+ const gate = writeGate("add_discount_code", confirmations);
269
296
  return {
270
297
  name: "add_discount_code",
271
298
  progress: "Dodaję kod rabatowy…",
272
299
  description: "Dodaje kod rabatowy salonu. Podaj albo percent, albo amountPln — nie oba. Kod nie ma daty ważności ani limitu użyć; działa, dopóki go nie usuniesz.",
273
- parameters: {
300
+ parameters: gate.parameters({
274
301
  type: "object",
275
302
  properties: {
276
303
  code: { type: "string", description: "Treść kodu, którą wpisze klientka." },
@@ -281,9 +308,12 @@ function addDiscountCodeTool(port) {
281
308
  },
282
309
  required: ["code", "confirmed"],
283
310
  additionalProperties: false,
284
- },
311
+ }),
285
312
  execute: async (args) => {
286
313
  try {
314
+ const refusal = gate.refusal(args);
315
+ if (refusal)
316
+ return refusal;
287
317
  const code = readString(args, "code");
288
318
  const percent = readOptionalNumber(args, "percent");
289
319
  const amountPln = readOptionalNumber(args, "amountPln");
@@ -301,8 +331,11 @@ function addDiscountCodeTool(port) {
301
331
  const value = percent !== undefined ? `${percent}%` : `${amountPln} zł`;
302
332
  const scope = scopeServiceId ? "na jedną usługę" : "na cały salon";
303
333
  if (!readBoolean(args, "confirmed")) {
304
- return needsConfirmation(`Dodam kod rabatowy „${code}”: ${value}, ${scope}.`, { ...args, confirmed: true });
334
+ return gate.preview(`Dodam kod rabatowy „${code}”: ${value}, ${scope}.`, args);
305
335
  }
336
+ const refused = gate.redeem(args);
337
+ if (refused)
338
+ return refused;
306
339
  await port.addDiscountCode({ code, percent, amountPln, scopeServiceId });
307
340
  return ok(`Dodano kod rabatowy „${code}” (${value}, ${scope}).`);
308
341
  }
@@ -314,12 +347,13 @@ function addDiscountCodeTool(port) {
314
347
  },
315
348
  };
316
349
  }
317
- function removeDiscountCodeTool(port) {
350
+ function removeDiscountCodeTool(port, confirmations) {
351
+ const gate = writeGate("remove_discount_code", confirmations);
318
352
  return {
319
353
  name: "remove_discount_code",
320
354
  progress: "Usuwam kod rabatowy…",
321
355
  description: "Usuwa kod rabatowy salonu. Kanister odmówi, dopóki jakakolwiek promocja ten kod rozgłasza — wtedy najpierw zmień promocję.",
322
- parameters: {
356
+ parameters: gate.parameters({
323
357
  type: "object",
324
358
  properties: {
325
359
  code: { type: "string", description: "Treść kodu z list_discount_codes." },
@@ -327,18 +361,24 @@ function removeDiscountCodeTool(port) {
327
361
  },
328
362
  required: ["code", "confirmed"],
329
363
  additionalProperties: false,
330
- },
364
+ }),
331
365
  // No confirmation phrase here, unlike a service or a promo: a removed code can simply be
332
366
  // added back with the same text, and nothing else in the salon points at it.
333
367
  execute: async (args) => {
334
368
  try {
369
+ const refusal = gate.refusal(args);
370
+ if (refusal)
371
+ return refusal;
335
372
  const code = readString(args, "code");
336
373
  const codes = await port.listDiscountCodes();
337
374
  if (!codes.some((entry) => entry.code === code))
338
375
  return err("not_found", `Salon nie ma kodu „${code}”.`);
339
376
  if (!readBoolean(args, "confirmed")) {
340
- return needsConfirmation(`Usunę kod rabatowy „${code}”.`, { ...args, confirmed: true });
377
+ return gate.preview(`Usunę kod rabatowy „${code}”.`, args);
341
378
  }
379
+ const refused = gate.redeem(args);
380
+ if (refused)
381
+ return refused;
342
382
  await port.removeDiscountCode(code);
343
383
  return ok(`Usunięto kod rabatowy „${code}”.`);
344
384
  }
@@ -1,4 +1,5 @@
1
+ import type { TurnConfirmations } from "../confirmations.js";
1
2
  import type { SalonCorePort } from "../salon-core-port.js";
2
3
  import { type AgentTool } from "../types.js";
3
- export declare function createScheduleTools(port: SalonCorePort): AgentTool[];
4
+ export declare function createScheduleTools(port: SalonCorePort, confirmations?: TurnConfirmations): AgentTool[];
4
5
  //# sourceMappingURL=schedule.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../../src/tools/schedule.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAyB,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAA8B,KAAK,SAAS,EAAmB,MAAM,aAAa,CAAC;AAqB1F,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,EAAE,CASpE"}
1
+ {"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../../src/tools/schedule.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,KAAK,EAAE,aAAa,EAAyB,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAsBtD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,aAAa,EAAE,aAAa,CAAC,EAAE,iBAAiB,GAAG,SAAS,EAAE,CASvG"}
@@ -1,7 +1,8 @@
1
1
  import { formatMinutesToTime, parseTimeRangesToSlots, parseTimeToMinutes } from "@jsm-mit/sultana-core-motoko-package";
2
2
  import { toToolError } from "../errors.js";
3
- import { err, needsConfirmation, ok } from "../types.js";
3
+ import { err, ok } from "../types.js";
4
4
  import { ArgumentError, readBoolean, readOptionalString, readString } from "./args.js";
5
+ import { writeGate } from "./write-gate.js";
5
6
  /** 0 = Monday, matching the canister's own day index. */
6
7
  const DAY_NAMES_PL = ["poniedziałek", "wtorek", "środa", "czwartek", "piątek", "sobota", "niedziela"];
7
8
  const CONFIRMED_FIELD = {
@@ -14,14 +15,14 @@ const RANGES_FIELD = {
14
15
  type: "string",
15
16
  description: 'Zakresy godzin oddzielone przecinkiem, np. "10-18" albo "9:30-13,14-18".',
16
17
  };
17
- export function createScheduleTools(port) {
18
+ export function createScheduleTools(port, confirmations) {
18
19
  return [
19
20
  getWeeklyHoursTool(port),
20
- setWeeklyHoursTool(port),
21
+ setWeeklyHoursTool(port, confirmations),
21
22
  getDayScheduleTool(port),
22
- setDayOffTool(port),
23
- setBusyHoursTool(port),
24
- clearDailyOverrideTool(port),
23
+ setDayOffTool(port, confirmations),
24
+ setBusyHoursTool(port, confirmations),
25
+ clearDailyOverrideTool(port, confirmations),
25
26
  ];
26
27
  }
27
28
  function getWeeklyHoursTool(port) {
@@ -53,12 +54,13 @@ function getWeeklyHoursTool(port) {
53
54
  },
54
55
  };
55
56
  }
56
- function setWeeklyHoursTool(port) {
57
+ function setWeeklyHoursTool(port, confirmations) {
58
+ const gate = writeGate("set_weekly_hours", confirmations);
57
59
  return {
58
60
  name: "set_weekly_hours",
59
61
  progress: "Ustawiam grafik…",
60
62
  description: "Ustawia godziny PRACY pracownika w wybranych dniach tygodnia. Zastępuje dotychczasowe godziny w tych dniach. Pusty zakres oznacza dzień niepracujący.",
61
- parameters: {
63
+ parameters: gate.parameters({
62
64
  type: "object",
63
65
  properties: {
64
66
  workerId: WORKER_FIELD,
@@ -72,20 +74,23 @@ function setWeeklyHoursTool(port) {
72
74
  },
73
75
  required: ["workerId", "days", "ranges", "confirmed"],
74
76
  additionalProperties: false,
75
- },
77
+ }),
76
78
  execute: async (args) => {
77
79
  try {
80
+ const refusal = gate.refusal(args);
81
+ if (refusal)
82
+ return refusal;
78
83
  const worker = await resolveWorker(port, readString(args, "workerId"));
79
84
  const days = readDays(args);
80
85
  const parsed = normalizeRanges(readOptionalString(args, "ranges") ?? "");
81
86
  const dayNames = days.map((day) => DAY_NAMES_PL[day]).join(", ");
82
87
  const hours = parsed.ranges.length === 0 ? "dzień wolny" : formatRanges(parsed.ranges).join(", ");
83
88
  if (!readBoolean(args, "confirmed")) {
84
- return needsConfirmation(`Ustawię ${worker.name}: ${dayNames} — ${hours}.${noteSuffix(parsed.notes)}`, {
85
- ...args,
86
- confirmed: true,
87
- });
89
+ return gate.preview(`Ustawię ${worker.name}: ${dayNames} — ${hours}.${noteSuffix(parsed.notes)}`, args);
88
90
  }
91
+ const refused = gate.redeem(args);
92
+ if (refused)
93
+ return refused;
89
94
  for (const day of days) {
90
95
  await port.setWeeklyHours(worker.id, day, parsed.ranges);
91
96
  }
@@ -127,24 +132,31 @@ function getDayScheduleTool(port) {
127
132
  },
128
133
  };
129
134
  }
130
- function setDayOffTool(port) {
135
+ function setDayOffTool(port, confirmations) {
136
+ const gate = writeGate("set_day_off", confirmations);
131
137
  return {
132
138
  name: "set_day_off",
133
139
  progress: "Ustawiam dzień wolny…",
134
140
  description: "Oznacza cały wskazany dzień jako wolny dla pracownika.",
135
- parameters: {
141
+ parameters: gate.parameters({
136
142
  type: "object",
137
143
  properties: { workerId: WORKER_FIELD, date: DATE_FIELD, confirmed: CONFIRMED_FIELD },
138
144
  required: ["workerId", "date", "confirmed"],
139
145
  additionalProperties: false,
140
- },
146
+ }),
141
147
  execute: async (args) => {
142
148
  try {
149
+ const refusal = gate.refusal(args);
150
+ if (refusal)
151
+ return refusal;
143
152
  const worker = await resolveWorker(port, readString(args, "workerId"));
144
153
  const date = readDate(args);
145
154
  if (!readBoolean(args, "confirmed")) {
146
- return needsConfirmation(`Ustawię ${worker.name} dzień wolny: ${date}.`, { ...args, confirmed: true });
155
+ return gate.preview(`Ustawię ${worker.name} dzień wolny: ${date}.`, args);
147
156
  }
157
+ const refused = gate.redeem(args);
158
+ if (refused)
159
+ return refused;
148
160
  await port.setDayOff(worker.id, date);
149
161
  return ok(`${worker.name} ma wolne ${date}.`);
150
162
  }
@@ -156,19 +168,23 @@ function setDayOffTool(port) {
156
168
  },
157
169
  };
158
170
  }
159
- function setBusyHoursTool(port) {
171
+ function setBusyHoursTool(port, confirmations) {
172
+ const gate = writeGate("set_busy_hours", confirmations);
160
173
  return {
161
174
  name: "set_busy_hours",
162
175
  progress: "Zapisuję godziny zajęte…",
163
176
  description: "Ustawia godziny ZAJĘTE w konkretnym dniu — nadpisuje grafik tygodniowy tylko tego dnia. Używaj do jednorazowych nieobecności („we wtorek jestem zajęta od 12 do 15”).",
164
- parameters: {
177
+ parameters: gate.parameters({
165
178
  type: "object",
166
179
  properties: { workerId: WORKER_FIELD, date: DATE_FIELD, ranges: RANGES_FIELD, confirmed: CONFIRMED_FIELD },
167
180
  required: ["workerId", "date", "ranges", "confirmed"],
168
181
  additionalProperties: false,
169
- },
182
+ }),
170
183
  execute: async (args) => {
171
184
  try {
185
+ const refusal = gate.refusal(args);
186
+ if (refusal)
187
+ return refusal;
172
188
  const worker = await resolveWorker(port, readString(args, "workerId"));
173
189
  const date = readDate(args);
174
190
  const parsed = normalizeRanges(readString(args, "ranges"));
@@ -177,11 +193,11 @@ function setBusyHoursTool(port) {
177
193
  }
178
194
  const hours = formatRanges(parsed.ranges).join(", ");
179
195
  if (!readBoolean(args, "confirmed")) {
180
- return needsConfirmation(`Zaznaczę ${worker.name} jako zajętą ${date}: ${hours}.${noteSuffix(parsed.notes)}`, {
181
- ...args,
182
- confirmed: true,
183
- });
196
+ return gate.preview(`Zaznaczę ${worker.name} jako zajętą ${date}: ${hours}.${noteSuffix(parsed.notes)}`, args);
184
197
  }
198
+ const refused = gate.redeem(args);
199
+ if (refused)
200
+ return refused;
185
201
  await port.setDailyBusy(worker.id, date, parsed.ranges);
186
202
  return ok(`${worker.name}, ${date}: zajęte ${hours}.${noteSuffix(parsed.notes)}`);
187
203
  }
@@ -193,27 +209,31 @@ function setBusyHoursTool(port) {
193
209
  },
194
210
  };
195
211
  }
196
- function clearDailyOverrideTool(port) {
212
+ function clearDailyOverrideTool(port, confirmations) {
213
+ const gate = writeGate("clear_daily_override", confirmations);
197
214
  return {
198
215
  name: "clear_daily_override",
199
216
  progress: "Przywracam grafik tygodniowy…",
200
217
  description: "Usuwa nadpisanie dla wskazanego dnia — od tej chwili obowiązuje zwykły grafik tygodniowy.",
201
- parameters: {
218
+ parameters: gate.parameters({
202
219
  type: "object",
203
220
  properties: { workerId: WORKER_FIELD, date: DATE_FIELD, confirmed: CONFIRMED_FIELD },
204
221
  required: ["workerId", "date", "confirmed"],
205
222
  additionalProperties: false,
206
- },
223
+ }),
207
224
  execute: async (args) => {
208
225
  try {
226
+ const refusal = gate.refusal(args);
227
+ if (refusal)
228
+ return refusal;
209
229
  const worker = await resolveWorker(port, readString(args, "workerId"));
210
230
  const date = readDate(args);
211
231
  if (!readBoolean(args, "confirmed")) {
212
- return needsConfirmation(`Usunę nadpisanie dnia ${date} dla ${worker.name} — wróci grafik tygodniowy.`, {
213
- ...args,
214
- confirmed: true,
215
- });
232
+ return gate.preview(`Usunę nadpisanie dnia ${date} dla ${worker.name} — wróci grafik tygodniowy.`, args);
216
233
  }
234
+ const refused = gate.redeem(args);
235
+ if (refused)
236
+ return refused;
217
237
  await port.clearDailyOverride(worker.id, date);
218
238
  return ok(`${worker.name}, ${date}: wrócił grafik tygodniowy.`);
219
239
  }
@@ -1,7 +1,9 @@
1
+ import type { TurnConfirmations } from "../confirmations.js";
1
2
  import type { SalonAccess, SalonCorePort } from "../salon-core-port.js";
2
3
  import { type AgentTool } from "../types.js";
3
- export declare function createServiceTools(port: SalonCorePort): AgentTool[];
4
+ export declare function createServiceTools(port: SalonCorePort, confirmations?: TurnConfirmations): AgentTool[];
4
5
  /** `access` must match the port: `"public"` only for a port that reads the visitor's view. */
5
6
  export declare function listServicesTool(port: SalonCorePort, access?: SalonAccess): AgentTool;
6
- export declare function findServiceTypeTool(port: SalonCorePort): AgentTool;
7
+ /** Takes any port that can search the catalogue — the customer set shares this tool. */
8
+ export declare function findServiceTypeTool(port: Pick<SalonCorePort, "findServiceTypes">): AgentTool;
7
9
  //# sourceMappingURL=services.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/tools/services.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAkC,MAAM,uBAAuB,CAAC;AACxG,OAAO,EAA8B,KAAK,SAAS,EAAmB,MAAM,aAAa,CAAC;AAoB1F,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,EAAE,CASnE;AAoBD,8FAA8F;AAC9F,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,GAAE,WAAqB,GAAG,SAAS,CAmB9F;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,CAiClE"}
1
+ {"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/tools/services.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAkC,MAAM,uBAAuB,CAAC;AACxG,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAwBtD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,EAAE,aAAa,CAAC,EAAE,iBAAiB,GAAG,SAAS,EAAE,CAStG;AAoBD,8FAA8F;AAC9F,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,GAAE,WAAqB,GAAG,SAAS,CAmB9F;AAED,wFAAwF;AACxF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,kBAAkB,CAAC,GAAG,SAAS,CAiC5F"}