@jsm-mit/sultana-agent-tools-package 0.7.0 → 0.9.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/README.md CHANGED
@@ -157,6 +157,11 @@ const tools = session
157
157
  - `search_slots` without `date`/`time` is the package's "as soon as possible" search (the next
158
158
  hour, else up to 24 h ahead); with them it probes from 30 min before to 60 min after the moment.
159
159
  Results: at most 6 salons, soonest first, each with its services and up to 6 free moments.
160
+ - `search_slots` searches around the position the app sent. Optional `lat` + `lng` (both or
161
+ neither) name another place — "near Kolejowa in Warsaw" — and win over the app's position;
162
+ distances are then measured from that place. The package does not geocode: the model gets the
163
+ numbers from `geocode_place` of `@jsm-mit/basic-tools-package`, bound to the same agent. With
164
+ no position and no `lat`/`lng` the tool refuses and tells the model to ask for a place.
160
165
  - A model reads and sends local time: `date` `"YYYY-MM-DD"`, `time` `"HH:mm"`.
161
166
  `get_free_times` folds the 5-minute start grid into ranges.
162
167
  - `book_appointment` picks the first free worker, like the app, and checks the slot on the preview
@@ -186,7 +191,7 @@ const tools = session
186
191
  `unavailable` instead of booking hours off (`timeZone: null` skips the guard; `hostTimeZoneProblem`
187
192
  lets a host check at start-up).
188
193
 
189
- ## Booksy tools (experimental, since 0.7.0)
194
+ ## Booksy tools (experimental, since 0.7.0; logo and cover since 0.9.0)
190
195
 
191
196
  "Copy my salon from Booksy": three read-only tools that take the address of a PUBLIC Booksy profile
192
197
  and return its content in the shape Sultana's write tools take, and one write tool that copies the
@@ -210,8 +215,20 @@ const tools = [
210
215
  | `read_booksy_services` | services ready for `add_service`: `name`, `pricePln`, `durationMinutes`, plus `booksyCategory`, `description`, `workerNames`, `notes`; optional `category` filter |
211
216
  | `download_booksy_photos` | fetches photos (`logo`, `cover`, `salon` by default; also `inspiration`, `service` — the per-service portfolio —, `staff`) and hands each to the host's `BooksyPhotoSink`; up to 150 a call |
212
217
  | `import_booksy_services` | **write**: copies the catalogue into this salon. Arguments: `url`, optional `category`, optional `overrides` (`{name, skip?, serviceTypeIds?}`), `confirmed` |
218
+ | `import_booksy_logo` | **write**: copies the Booksy logo into the salon's media library and sets it as the salon's THUMBNAIL; the cover is handed back untouched. Arguments: `url`, `confirmed` |
219
+ | `import_booksy_cover` | **write**: the same for the Booksy cover → the salon's COVER; the thumbnail is handed back untouched |
213
220
  | `import_booksy_workers` | **placeholder**: says that copying the team is not built yet, lists the people on Booksy and who is missing in the salon; writes nothing |
214
221
 
222
+ The two photo tools exist only when the port can write pictures — `IcSalonCorePort` built with
223
+ `assetsCanisterId`, and an identity the assets canister accepts (a delegation must list that
224
+ canister in its `targets`, next to core). A salon has exactly two photo slots, and the canister
225
+ replaces both at once, so each tool reads the salon first and hands the other slot back. One
226
+ picture, one call to the assets canister: at most 1.9 MB, JPEG / PNG / WebP — anything else is
227
+ refused with a sentence, nothing is set. The asset is named `Booksy · logo · <mark>`; a picture
228
+ already in the library is not stored twice. The preview downloads nothing; its echo carries the
229
+ picture's mark, and a logo changed on Booksy in between makes the confirmation stale. The other
230
+ Booksy pictures (service portfolio, interior, inspirations, staff) are not copied yet.
231
+
215
232
  `import_booksy_services`:
216
233
 
217
234
  - **The model never carries the services.** It names the profile; the tool reads it again itself
@@ -0,0 +1,18 @@
1
+ import type { TurnConfirmations } from "../confirmations.js";
2
+ import { type SalonCorePort } from "../salon-core-port.js";
3
+ import { type AgentTool } from "../types.js";
4
+ import { type FetchFn } from "./fetch-page.js";
5
+ import { BooksyProfileReader } from "./tools.js";
6
+ export interface CreateBooksyPhotoToolsOptions {
7
+ reader?: BooksyProfileReader;
8
+ confirmations?: TurnConfirmations;
9
+ fetchFn?: FetchFn;
10
+ }
11
+ /**
12
+ * Two write tools, one per photo slot of a salon: the Booksy logo becomes the salon's thumbnail,
13
+ * the Booksy cover becomes the salon's cover. Each copies ONE picture into the salon's media
14
+ * library and sets ONE slot; the other slot is read first and handed back untouched. A port that
15
+ * cannot write pictures gives no tools.
16
+ */
17
+ export declare function createBooksyPhotoTools(port: SalonCorePort, options?: CreateBooksyPhotoToolsOptions): AgentTool[];
18
+ //# sourceMappingURL=import-photos-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-photos-tools.d.ts","sourceRoot":"","sources":["../../src/booksy/import-photos-tools.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,EAAiD,KAAK,aAAa,EAAuB,MAAM,uBAAuB,CAAC;AAG/H,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAmD,KAAK,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAEhG,OAAO,EAAE,mBAAmB,EAAa,MAAM,YAAY,CAAC;AAE5D,MAAM,WAAW,6BAA6B;IAC1C,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAyCD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,GAAE,6BAAkC,GAAG,SAAS,EAAE,CAMpH"}
@@ -0,0 +1,124 @@
1
+ import { createHash } from "node:crypto";
2
+ import { toToolError } from "../errors.js";
3
+ import { MAX_IMAGE_UPLOAD_BYTES } from "../salon-core-port.js";
4
+ import { ArgumentError, readBoolean, readOptionalString, readString } from "../tools/args.js";
5
+ import { writeGate } from "../tools/write-gate.js";
6
+ import { err, ok } from "../types.js";
7
+ import { BooksyReadError, fetchPhoto, normalizeBooksyUrl } from "./fetch-page.js";
8
+ import { BooksyProfileReader, URL_FIELD } from "./tools.js";
9
+ const LOGO = {
10
+ toolName: "import_booksy_logo",
11
+ booksyKind: "logo",
12
+ slot: "thumbnailAssetId",
13
+ other: "coverAssetId",
14
+ progress: "Kopiuję logo z Booksy…",
15
+ whatPl: "logo",
16
+ asPl: "miniaturę salonu (kwadratowe zdjęcie na listach i mapie)",
17
+ otherPl: "Okładka salonu",
18
+ };
19
+ const COVER = {
20
+ toolName: "import_booksy_cover",
21
+ booksyKind: "cover",
22
+ slot: "coverAssetId",
23
+ other: "thumbnailAssetId",
24
+ progress: "Kopiuję okładkę z Booksy…",
25
+ whatPl: "okładkę",
26
+ asPl: "okładkę salonu (szerokie zdjęcie na górze strony salonu)",
27
+ otherPl: "Miniatura salonu",
28
+ };
29
+ const UPLOADABLE = new Set(["image/jpeg", "image/png", "image/webp"]);
30
+ /**
31
+ * Two write tools, one per photo slot of a salon: the Booksy logo becomes the salon's thumbnail,
32
+ * the Booksy cover becomes the salon's cover. Each copies ONE picture into the salon's media
33
+ * library and sets ONE slot; the other slot is read first and handed back untouched. A port that
34
+ * cannot write pictures gives no tools.
35
+ */
36
+ export function createBooksyPhotoTools(port, options = {}) {
37
+ if (!port.photos)
38
+ return [];
39
+ const reader = options.reader ?? new BooksyProfileReader(options.fetchFn);
40
+ return [LOGO, COVER].map((slot) => photoTool(slot, port, port.photos, reader, options));
41
+ }
42
+ function photoTool(slot, port, photos, reader, options) {
43
+ const gate = writeGate(slot.toolName, options.confirmations);
44
+ return {
45
+ name: slot.toolName,
46
+ progress: slot.progress,
47
+ description: `Kopiuje ${slot.whatPl} z publicznego profilu Booksy do biblioteki mediów salonu i ustawia jako ${slot.asPl}. ${slot.otherPl} zostaje bez zmian. Przy confirmed=false niczego nie pobiera ani nie zapisuje — oddaje opis zmiany do pokazania właścicielce.`,
48
+ parameters: gate.parameters({
49
+ type: "object",
50
+ properties: {
51
+ url: URL_FIELD,
52
+ photo: { type: "string", description: "Znacznik zdjęcia z podglądu. Narzędzie odmówi, gdy zdjęcie na Booksy zmieniło się od podglądu." },
53
+ confirmed: {
54
+ type: "boolean",
55
+ description: "false dla podglądu: narzędzie nic nie zapisze i odda opis zmiany do zatwierdzenia przez właścicielkę. true dopiero po tym, jak właścicielka potwierdzi.",
56
+ },
57
+ },
58
+ required: ["url", "confirmed"],
59
+ additionalProperties: false,
60
+ }),
61
+ execute: async (args) => {
62
+ try {
63
+ const url = normalizeBooksyUrl(readString(args, "url"));
64
+ const draft = await reader.read(url);
65
+ const picture = draft.photos.find((photo) => photo.kind === slot.booksyKind);
66
+ if (!picture)
67
+ return ok(`Profil Booksy nie ma zdjęcia „${slot.whatPl}” — nie ma czego kopiować.`, { copied: false });
68
+ // The mark ties the confirmation to the picture the owner was told about.
69
+ const mark = markOf(picture.url);
70
+ const expected = readOptionalString(args, "photo") ?? mark;
71
+ const echo = { url, photo: expected };
72
+ const refusal = gate.refusal(args, echo);
73
+ if (refusal)
74
+ return refusal;
75
+ if (expected !== mark) {
76
+ return err("invalid_arguments", "Zdjęcie na Booksy zmieniło się od podglądu — pokaż nowy podgląd (confirmed=false, bez pola photo).");
77
+ }
78
+ const current = await photos.getSalonPhotos();
79
+ if (!readBoolean(args, "confirmed")) {
80
+ const replaced = current[slot.slot] === null ? "Ten slot jest dziś pusty." : "Obecne zdjęcie w tym slocie zostanie zastąpione (zostaje w bibliotece mediów).";
81
+ return gate.preview(`Skopiuję ${slot.whatPl} z Booksy${sizeOf(picture)} do biblioteki mediów i ustawię jako ${slot.asPl}. ${replaced} ${slot.otherPl}: bez zmian.`, echo);
82
+ }
83
+ const refused = gate.redeem(args, echo);
84
+ if (refused)
85
+ return refused;
86
+ const name = `Booksy · ${slot.booksyKind} · ${mark}`;
87
+ const library = await port.listMedia();
88
+ // A picture copied before is not stored twice — a repeated order only sets the slot.
89
+ const known = library.find((media) => media.kind === "image" && media.name === name);
90
+ const assetId = known?.assetId ?? (await photos.uploadImage(await download(picture, name, options.fetchFn)));
91
+ await photos.setSalonPhotos({ ...current, [slot.slot]: assetId });
92
+ return ok(`Ustawiono ${slot.whatPl} z Booksy jako ${slot.asPl}.${known ? " Zdjęcie było już w bibliotece mediów — nie wgrywałem go drugi raz." : ""}`, {
93
+ copied: true,
94
+ assetId,
95
+ });
96
+ }
97
+ catch (error) {
98
+ if (error instanceof ArgumentError)
99
+ return err("invalid_arguments", error.message);
100
+ if (error instanceof BooksyReadError)
101
+ return err(error.code, error.summary);
102
+ return toToolError(error, `Nie udało się skopiować zdjęcia (${slot.whatPl}) z Booksy.`);
103
+ }
104
+ },
105
+ };
106
+ }
107
+ async function download(picture, name, fetchFn) {
108
+ const file = await fetchPhoto(picture.url, fetchFn);
109
+ if (!UPLOADABLE.has(file.contentType)) {
110
+ throw new BooksyReadError("invalid_arguments", `Zdjęcie na Booksy ma format ${file.contentType}, a Sultana przyjmuje JPEG, PNG i WebP.`);
111
+ }
112
+ if (file.bytes.length > MAX_IMAGE_UPLOAD_BYTES) {
113
+ const megabytes = (file.bytes.length / 1_000_000).toFixed(1);
114
+ throw new BooksyReadError("invalid_arguments", `Zdjęcie na Booksy ma ${megabytes} MB, a Sultana przyjmuje do 1,9 MB. Wgraj mniejszą wersję w panelu salonu.`);
115
+ }
116
+ return { name, contentType: file.contentType, bytes: file.bytes };
117
+ }
118
+ /** Eight hex characters of the picture's address: enough to tell a salon's pictures apart. */
119
+ function markOf(pictureUrl) {
120
+ return createHash("sha256").update(pictureUrl).digest("hex").slice(0, 8);
121
+ }
122
+ function sizeOf(picture) {
123
+ return picture.width && picture.height ? ` (${picture.width}×${picture.height})` : "";
124
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"import-tools.d.ts","sourceRoot":"","sources":["../../src/booksy/import-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,KAAK,EAAE,aAAa,EAAkD,MAAM,uBAAuB,CAAC;AAG3G,OAAO,EAAW,KAAK,SAAS,EAAmB,MAAM,aAAa,CAAC;AAGvE,OAAO,EACH,mBAAmB,EAInB,KAAK,8BAA8B,EACtC,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,8BAA8B;IAC3C,8FAA8F;IAC9F,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,aAAa,CAAC,EAAE,iBAAiB,CAAC;CACrC;AAMD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,GAAE,8BAAmC,GAAG,SAAS,EAAE,CAItH;AAED,MAAM,WAAW,wBAAyB,SAAQ,8BAA8B;IAC5E,aAAa,CAAC,EAAE,iBAAiB,CAAC;CACrC;AAED;0EAC0E;AAC1E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,GAAE,wBAA6B,GAAG,SAAS,EAAE,CAO1G"}
1
+ {"version":3,"file":"import-tools.d.ts","sourceRoot":"","sources":["../../src/booksy/import-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,KAAK,EAAE,aAAa,EAAkD,MAAM,uBAAuB,CAAC;AAG3G,OAAO,EAAW,KAAK,SAAS,EAAmB,MAAM,aAAa,CAAC;AAIvE,OAAO,EACH,mBAAmB,EAInB,KAAK,8BAA8B,EACtC,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,8BAA8B;IAC3C,8FAA8F;IAC9F,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,aAAa,CAAC,EAAE,iBAAiB,CAAC;CACrC;AAMD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,GAAE,8BAAmC,GAAG,SAAS,EAAE,CAItH;AAED,MAAM,WAAW,wBAAyB,SAAQ,8BAA8B;IAC5E,aAAa,CAAC,EAAE,iBAAiB,CAAC;CACrC;AAED;0EAC0E;AAC1E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,GAAE,wBAA6B,GAAG,SAAS,EAAE,CAS1G"}
@@ -3,6 +3,7 @@ import { ArgumentError, readBoolean, readOptionalNumber, readOptionalString, rea
3
3
  import { writeGate } from "../tools/write-gate.js";
4
4
  import { err, ok } from "../types.js";
5
5
  import { BooksyReadError, normalizeBooksyUrl } from "./fetch-page.js";
6
+ import { createBooksyPhotoTools } from "./import-photos-tools.js";
6
7
  import { BooksyProfileReader, URL_FIELD, createBooksyReaderTools, degradedSuffix, } from "./tools.js";
7
8
  /** Writes at a time. Enough to bring a 60-service catalogue down from minutes to well under one;
8
9
  * low enough that a canister short on cycles still has room for every call in flight. */
@@ -23,6 +24,8 @@ export function createBooksyTools(port, options = {}) {
23
24
  return [
24
25
  ...createBooksyReaderTools({ ...options, reader }),
25
26
  ...createBooksyImportTools(port, { reader, confirmations: options.confirmations }),
27
+ // None when the port cannot write pictures (no assets canister, or no identity for it).
28
+ ...createBooksyPhotoTools(port, { reader, confirmations: options.confirmations, fetchFn: options.fetchFn }),
26
29
  ];
27
30
  }
28
31
  function importServicesTool(port, reader, confirmations) {
@@ -72,13 +72,13 @@ export interface BookingInput {
72
72
  discountCode?: string;
73
73
  }
74
74
  export interface CustomerCorePort {
75
- /** Where the customer searches around; `null` when the app sent no position. */
75
+ /** The position the app sent; `null` when it sent none. A search may name another place. */
76
76
  readonly location: Coordinates | null;
77
77
  findServiceTypes(query: string): Promise<ServiceTypeView[]>;
78
78
  /** "As soon as possible" around `location`. */
79
- searchAsap(serviceTypeId: string, radiusKm: number, now: Date): Promise<SlotSearchView>;
79
+ searchAsap(location: Coordinates, serviceTypeId: string, radiusKm: number, now: Date): Promise<SlotSearchView>;
80
80
  /** The given start moments around `location`. */
81
- searchAtMoments(serviceTypeId: string, radiusKm: number, moments: number[]): Promise<SlotSearchView>;
81
+ searchAtMoments(location: Coordinates, serviceTypeId: string, radiusKm: number, moments: number[]): Promise<SlotSearchView>;
82
82
  getSalons(salonIds: string[]): Promise<SalonView[]>;
83
83
  listSalonServices(salonId: string): Promise<CustomerServiceView[]>;
84
84
  listSalonPromos(salonId: string): Promise<CustomerPromoView[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"customer-core-port.d.ts","sourceRoot":"","sources":["../../src/customer/customer-core-port.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"customer-core-port.d.ts","sourceRoot":"","sources":["../../src/customer/customer-core-port.ts"],"names":[],"mappings":"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"}
@@ -31,8 +31,8 @@ export declare class IcCustomerCorePort implements CustomerCorePort {
31
31
  private readonly appointments;
32
32
  constructor(options: IcCustomerCorePortOptions);
33
33
  findServiceTypes(query: string): Promise<ServiceTypeView[]>;
34
- searchAsap(serviceTypeId: string, radiusKm: number, now: Date): Promise<SlotSearchView>;
35
- searchAtMoments(serviceTypeId: string, radiusKm: number, moments: number[]): Promise<SlotSearchView>;
34
+ searchAsap(location: Coordinates, serviceTypeId: string, radiusKm: number, now: Date): Promise<SlotSearchView>;
35
+ searchAtMoments(location: Coordinates, serviceTypeId: string, radiusKm: number, moments: number[]): Promise<SlotSearchView>;
36
36
  getSalons(salonIds: string[]): Promise<SalonView[]>;
37
37
  listSalonServices(salonId: string): Promise<CustomerServiceView[]>;
38
38
  listSalonPromos(salonId: string): Promise<CustomerPromoView[]>;
@@ -40,6 +40,5 @@ export declare class IcCustomerCorePort implements CustomerCorePort {
40
40
  book(input: BookingInput): Promise<string>;
41
41
  listMyAppointments(): Promise<AppointmentView[]>;
42
42
  cancel(appointmentId: string): Promise<void>;
43
- private requireLocation;
44
43
  }
45
44
  //# sourceMappingURL=ic-customer-core-port.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ic-customer-core-port.d.ts","sourceRoot":"","sources":["../../src/customer/ic-customer-core-port.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAepD,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,KAAK,EAER,eAAe,EACf,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EAEnB,SAAS,EACT,cAAc,EACjB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,yBAAyB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB;0FACsF;IACtF,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,uFAAuF;IACvF,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;CACpB;AAaD;;;;;;;GAOG;AACH,qBAAa,kBAAmB,YAAW,gBAAgB;IACvD,SAAgB,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAC;IAE7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IACjD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;gBAErC,OAAO,EAAE,yBAAyB;IAajC,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAU3D,UAAU,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC;IAWvF,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC;IAWpG,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAQnD,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAKlE,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAa9D,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAUjF,IAAI,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAW1C,kBAAkB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAehD,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzD,OAAO,CAAC,eAAe;CAI1B"}
1
+ {"version":3,"file":"ic-customer-core-port.d.ts","sourceRoot":"","sources":["../../src/customer/ic-customer-core-port.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAepD,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,KAAK,EAER,eAAe,EACf,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EAEnB,SAAS,EACT,cAAc,EACjB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,yBAAyB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB;0FACsF;IACtF,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,uFAAuF;IACvF,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;CACpB;AAaD;;;;;;;GAOG;AACH,qBAAa,kBAAmB,YAAW,gBAAgB;IACvD,SAAgB,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAC;IAE7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IACjD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;gBAErC,OAAO,EAAE,yBAAyB;IAajC,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAU3D,UAAU,CAAC,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC;IAW9G,eAAe,CAAC,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC;IAW3H,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAQnD,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAKlE,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAa9D,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAUjF,IAAI,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAW1C,kBAAkB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAehD,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAI5D"}
@@ -37,18 +37,18 @@ export class IcCustomerCorePort {
37
37
  category: entry.category,
38
38
  }));
39
39
  }
40
- async searchAsap(serviceTypeId, radiusKm, now) {
40
+ async searchAsap(location, serviceTypeId, radiusKm, now) {
41
41
  const state = await searchAsapAsyncUnsafe(this.search, {
42
- location: this.requireLocation(),
42
+ location,
43
43
  serviceTypeId,
44
44
  radiusKm,
45
45
  now,
46
46
  });
47
47
  return { phase: state.phase, salons: state.results.map(toSalonSlots), failedProbes: state.failedProbes };
48
48
  }
49
- async searchAtMoments(serviceTypeId, radiusKm, moments) {
49
+ async searchAtMoments(location, serviceTypeId, radiusKm, moments) {
50
50
  const state = await searchAtMomentsAsyncUnsafe(this.search, {
51
- location: this.requireLocation(),
51
+ location,
52
52
  serviceTypeId,
53
53
  radiusKm,
54
54
  moments,
@@ -104,11 +104,6 @@ export class IcCustomerCorePort {
104
104
  async cancel(appointmentId) {
105
105
  await this.appointments.cancelAppointmentAsyncUnsafe(appointmentId);
106
106
  }
107
- requireLocation() {
108
- if (!this.location)
109
- throw new Error("IcCustomerCorePort: no location — the tools check it before they search");
110
- return this.location;
111
- }
112
107
  }
113
108
  function toService(service) {
114
109
  return {
@@ -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;AA2BD,yEAAyE;AACzE,eAAO,MAAM,yBAAyB,QAAgC,CAAC"}
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;AA8BD,yEAAyE;AACzE,eAAO,MAAM,yBAAyB,QAAgC,CAAC"}
@@ -24,7 +24,10 @@ const PERSONA = `Jesteś asystentem klienta w aplikacji Sultana: pomagasz znale
24
24
  rezerwujesz i odwołujesz wizyty w imieniu klienta. Rozmawiasz po polsku, krótko i konkretnie.
25
25
 
26
26
  Zasady pracy:
27
- - Szukasz zawsze wokół pozycji klienta, którą podała aplikacja. Nie pytaj o adres.
27
+ - Szukasz wokół pozycji klienta, którą podała aplikacja — sam nie pytaj o adres. Gdy klient wskazuje
28
+ inne miejsce („koło Kolejowej w Warszawie”), zamień je na współrzędne przez geocode_place i podaj
29
+ lat i lng w search_slots. Powiedz klientowi, wokół jakiego miejsca szukałeś. Bez geocode_place
30
+ poproś o włączenie lokalizacji w aplikacji.
28
31
  - Typ usługi ustal przez find_service_type (np. „strzyżenie męskie”). Nigdy nie wymyślaj id.
29
32
  Gdy pasuje kilka typów, dopytaj klienta, o który chodzi.
30
33
  - Terminy znajdź przez search_slots: bez daty = jak najszybciej, z date i time = okolice tej godziny.
@@ -20,6 +20,8 @@ export interface CustomerTexts {
20
20
  salonFallbackName: string;
21
21
  visitFallbackName: string;
22
22
  noLocation: string;
23
+ latAndLngTogether: string;
24
+ badCoordinates: string;
23
25
  bookingOff(problem: string): string;
24
26
  badDay(field: string): string;
25
27
  badTime(field: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"texts.d.ts","sourceRoot":"","sources":["../../src/customer/texts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"texts.d.ts","sourceRoot":"","sources":["../../src/customer/texts.ts"],"names":[],"mappings":"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"}
@@ -10,7 +10,9 @@ const PL = {
10
10
  },
11
11
  salonFallbackName: "Salon",
12
12
  visitFallbackName: "wizyta",
13
- noLocation: "Aplikacja nie podała pozycji klienta, więc nie wiem, gdzie szukać. Poproś klienta, żeby włączył lokalizację w aplikacji i otworzył czat ponownie.",
13
+ noLocation: "Aplikacja nie podała pozycji klienta, więc nie wiem, gdzie szukać. Zapytaj klienta, w okolicy jakiego miejsca szukać (ulica, dzielnica, miasto), i podaj współrzędne tego miejsca w lat i lng. Klient może też włączyć lokalizację w aplikacji i otworzyć czat ponownie.",
14
+ latAndLngTogether: "Podaj lat i lng razem albo pomiń oba (szukanie wokół pozycji z aplikacji).",
15
+ badCoordinates: "lat musi być między -90 a 90, a lng między -180 a 180.",
14
16
  bookingOff: (problem) => `Rezerwacje są chwilowo wyłączone: ${problem}.`,
15
17
  badDay: (field) => `Pole "${field}" musi być datą w formacie RRRR-MM-DD.`,
16
18
  badTime: (field) => `Pole "${field}" musi być godziną w formacie GG:MM.`,
@@ -63,7 +65,9 @@ const EN = {
63
65
  },
64
66
  salonFallbackName: "Salon",
65
67
  visitFallbackName: "visit",
66
- noLocation: "The app sent no position of the customer, so I do not know where to search. Ask the customer to turn location on in the app and open the chat again.",
68
+ noLocation: "The app sent no position of the customer, so I do not know where to search. Ask the customer which place to search around (street, district, city) and give that place's coordinates in lat and lng. The customer can also turn location on in the app and open the chat again.",
69
+ latAndLngTogether: "Give lat and lng together, or leave both out (search around the app's position).",
70
+ badCoordinates: "lat must be between -90 and 90, and lng between -180 and 180.",
67
71
  bookingOff: (problem) => `Booking is switched off for the moment: ${problem}.`,
68
72
  badDay: (field) => `Field "${field}" must be a date in the YYYY-MM-DD format.`,
69
73
  badTime: (field) => `Field "${field}" must be a time in the HH:MM format.`,
@@ -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,EAAmB,gBAAgB,EAAkD,MAAM,yBAAyB,CAAC;AAejI,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;AAkDD,wBAAgB,eAAe,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAkFxE;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,CAqF1H;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"}
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;AAiED,wBAAgB,eAAe,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAsFxE;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,CAqF1H;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"}
@@ -44,6 +44,21 @@ function readTimeMinutes(texts, args, key) {
44
44
  throw new ArgumentError(texts.badTime(key));
45
45
  return minutes;
46
46
  }
47
+ /**
48
+ * Where to search: a place the customer named (the model geocodes it and passes `lat` / `lng`)
49
+ * wins over the position the app sent. `null` = neither is known.
50
+ */
51
+ function readSearchLocation(texts, args, fallback) {
52
+ const lat = readOptionalNumber(args, "lat");
53
+ const lng = readOptionalNumber(args, "lng");
54
+ if (lat === undefined && lng === undefined)
55
+ return fallback;
56
+ if (lat === undefined || lng === undefined)
57
+ throw new ArgumentError(texts.latAndLngTogether);
58
+ if (Math.abs(lat) > 90 || Math.abs(lng) > 180)
59
+ throw new ArgumentError(texts.badCoordinates);
60
+ return { lat, lng };
61
+ }
47
62
  function serviceLine(formats) {
48
63
  return (service) => ({ serviceId: service.id, name: service.name, price: formats.price(service.pricePln), duration: formats.duration(service.durationMinutes) });
49
64
  }
@@ -53,7 +68,7 @@ export function searchSlotsTool(context) {
53
68
  return {
54
69
  name: "search_slots",
55
70
  progress: texts.progress.searchSlots,
56
- description: "Szuka salonów w okolicy klienta, które mają wolny termin na usługę danego typu. 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. serviceTypeId weź z find_service_type.",
71
+ 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. serviceTypeId weź z find_service_type.",
57
72
  parameters: {
58
73
  type: "object",
59
74
  properties: {
@@ -61,6 +76,8 @@ export function searchSlotsTool(context) {
61
76
  date: { type: "string", description: "Dzień RRRR-MM-DD. Pomiń razem z time, żeby szukać jak najszybciej." },
62
77
  time: { type: "string", description: "Godzina GG:MM. Wymagana, gdy podajesz date." },
63
78
  radiusKm: { type: "number", description: `Promień szukania w km, domyślnie ${DEFAULT_RADIUS_KM}, najwyżej ${MAX_RADIUS_KM}.` },
79
+ lat: { type: "number", description: "Szerokość geograficzna miejsca wskazanego przez klienta. Tylko razem z lng; pomiń oba, żeby szukać wokół pozycji z aplikacji." },
80
+ lng: { type: "number", description: "Długość geograficzna miejsca wskazanego przez klienta. Tylko razem z lat." },
64
81
  },
65
82
  required: ["serviceTypeId"],
66
83
  additionalProperties: false,
@@ -70,7 +87,8 @@ export function searchSlotsTool(context) {
70
87
  const refusal = zoneRefusal(context);
71
88
  if (refusal)
72
89
  return refusal;
73
- if (!port.location)
90
+ const location = readSearchLocation(texts, args, port.location);
91
+ if (!location)
74
92
  return err("invalid_arguments", texts.noLocation);
75
93
  const formats = formatsOf(context);
76
94
  const serviceTypeId = readString(args, "serviceTypeId");
@@ -84,7 +102,7 @@ export function searchSlotsTool(context) {
84
102
  let found;
85
103
  let lead;
86
104
  if (dayText === undefined) {
87
- found = await port.searchAsap(serviceTypeId, radiusKm, now);
105
+ found = await port.searchAsap(location, serviceTypeId, radiusKm, now);
88
106
  lead = found.phase === "extended" ? texts.leadExtended : texts.leadSoon;
89
107
  }
90
108
  else {
@@ -95,7 +113,7 @@ export function searchSlotsTool(context) {
95
113
  .filter((moment) => moment > now.getTime() && onSlotGrid(new Date(moment).getMinutes()));
96
114
  if (moments.length === 0)
97
115
  return err("invalid_arguments", texts.momentPassed);
98
- found = await port.searchAtMoments(serviceTypeId, radiusKm, moments);
116
+ found = await port.searchAtMoments(location, serviceTypeId, radiusKm, moments);
99
117
  lead = texts.leadAround(day, timeOfMinutes(at));
100
118
  }
101
119
  if (found.salons.length === 0) {
@@ -1,11 +1,17 @@
1
1
  import type { Identity } from "@icp-sdk/core/agent";
2
- import type { DiscountCodeInput, DiscountCodeView, MediaView, PromoView, PromoWriteInput, SalonAccess, SalonCorePort, TimeRange, ServiceTypeView, ServiceView, ServiceWriteInput, WorkerView } from "./salon-core-port.js";
2
+ import type { DiscountCodeInput, DiscountCodeView, MediaView, PromoView, PromoWriteInput, SalonAccess, SalonCorePort, SalonPhotoPort, TimeRange, ServiceTypeView, ServiceView, ServiceWriteInput, WorkerView } from "./salon-core-port.js";
3
3
  export interface IcSalonCorePortOptions {
4
4
  canisterId: string;
5
5
  /** Omit for an anonymous identity — every write will then be refused, which is only useful in
6
6
  * a read-only demo. */
7
7
  identity?: Identity;
8
8
  salonId: string;
9
+ /**
10
+ * The assets canister this core is wired to. With it the port can write pictures (`photos`);
11
+ * without it there is no `photos`, and the tools that need it are not built. The identity must
12
+ * be one the assets canister accepts — a delegation has to name that canister in its targets.
13
+ */
14
+ assetsCanisterId?: string;
9
15
  /** Language of service-type labels the agent sees and quotes back to the owner. */
10
16
  language: string;
11
17
  /**
@@ -32,6 +38,7 @@ export declare class IcSalonCorePort implements SalonCorePort {
32
38
  private readonly promo;
33
39
  private catalog;
34
40
  private readonly discountCodes;
41
+ readonly photos?: SalonPhotoPort;
35
42
  constructor(options: IcSalonCorePortOptions);
36
43
  listServices(): Promise<ServiceView[]>;
37
44
  addService(input: ServiceWriteInput): Promise<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"ic-salon-core-port.d.ts","sourceRoot":"","sources":["../src/ic-salon-core-port.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ic-salon-core-port.d.ts","sourceRoot":"","sources":["../src/ic-salon-core-port.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,11 @@
1
1
  import { Principal } from "@icp-sdk/core/principal";
2
- import { AssetsActor, AvailabilityActor, DiscountCodesActor, filterServiceTypes, HrActor, PromoActor, promoMusicOf, SalonServicesActor, ServiceTypesActor, } from "@jsm-mit/sultana-core-motoko-package";
2
+ import { AssetsActor, AvailabilityActor, DiscountCodesActor, filterServiceTypes, HrActor, PromoActor, promoMusicOf, SalonsActor, SalonServicesActor, ServiceTypesActor, } from "@jsm-mit/sultana-core-motoko-package";
3
+ import { AssetsRustSalonActor } from "@jsm-mit/sultana-assets-rust-package";
3
4
  const SERVICE_TYPES_CATALOG_TTL_MS = 60_000;
5
+ /** The assets canister registers a stored image in core with a call of its own; the library lists
6
+ * it a moment after the upload returns. Two seconds is the usual wait — this is the ceiling. */
7
+ const IMAGE_REGISTRATION_TIMEOUT_MS = 20_000;
8
+ const IMAGE_REGISTRATION_POLL_MS = 1_000;
4
9
  /**
5
10
  * The port over the published canister wrapper. Conversions live here and nowhere else: the
6
11
  * canister speaks `bigint` and `Principal`, the tools speak numbers and strings.
@@ -17,6 +22,7 @@ export class IcSalonCorePort {
17
22
  promo;
18
23
  catalog = null;
19
24
  discountCodes;
25
+ photos;
20
26
  constructor(options) {
21
27
  this.salonId = options.salonId;
22
28
  this.language = options.language;
@@ -28,6 +34,9 @@ export class IcSalonCorePort {
28
34
  this.assets = new AssetsActor(options.canisterId, options.identity);
29
35
  this.promo = new PromoActor(options.canisterId, options.identity);
30
36
  this.discountCodes = new DiscountCodesActor(options.canisterId, options.identity);
37
+ if (options.assetsCanisterId && this.access === "owner") {
38
+ this.photos = new IcSalonPhotoPort(options.salonId, new SalonsActor(options.canisterId, options.identity), new AssetsRustSalonActor(options.assetsCanisterId, options.identity), () => this.listMedia());
39
+ }
31
40
  }
32
41
  async listServices() {
33
42
  // Both queries return the same candid record, so one mapping serves both.
@@ -176,3 +185,44 @@ function toContent(input) {
176
185
  music: input.music,
177
186
  };
178
187
  }
188
+ class IcSalonPhotoPort {
189
+ salonId;
190
+ salons;
191
+ assetsRust;
192
+ listMedia;
193
+ constructor(salonId, salons, assetsRust, listMedia) {
194
+ this.salonId = salonId;
195
+ this.salons = salons;
196
+ this.assetsRust = assetsRust;
197
+ this.listMedia = listMedia;
198
+ }
199
+ async getSalonPhotos() {
200
+ const salons = await this.salons.getMySalonsAsyncUnsafe();
201
+ const salon = salons.find((candidate) => candidate.id === this.salonId);
202
+ if (!salon)
203
+ throw new Error("CanisterError", { cause: { errorKey: "NotFound", errorMessage: "salon not found among the owner's salons" } });
204
+ return { thumbnailAssetId: salon.thumbnailAssetId[0] ?? null, coverAssetId: salon.coverAssetId[0] ?? null };
205
+ }
206
+ async setSalonPhotos(photos) {
207
+ // The canister REPLACES both slots — an omitted argument clears that slot.
208
+ await this.salons.setSalonPhotosAsyncUnsafe(this.salonId, photos.thumbnailAssetId ?? undefined, photos.coverAssetId ?? undefined);
209
+ }
210
+ async uploadImage(input) {
211
+ const assetId = await this.assetsRust.uploadImageAsyncUnsafe({
212
+ salonId: this.salonId,
213
+ name: input.name,
214
+ contentType: input.contentType,
215
+ data: input.bytes,
216
+ });
217
+ // `setSalonPhotos` refuses an asset the library does not list yet.
218
+ const deadline = Date.now() + IMAGE_REGISTRATION_TIMEOUT_MS;
219
+ for (;;) {
220
+ const media = await this.listMedia();
221
+ if (media.some((entry) => entry.assetId === assetId))
222
+ return assetId;
223
+ if (Date.now() >= deadline)
224
+ throw new Error("CriticalCanisterError");
225
+ await new Promise((resolve) => setTimeout(resolve, IMAGE_REGISTRATION_POLL_MS));
226
+ }
227
+ }
228
+ }
package/dist/index.d.ts CHANGED
@@ -6,6 +6,8 @@ export type { BooksyImage, BooksyImageKind, BooksyOpenHours, BooksyPriceType, Bo
6
6
  export { parseBooksySalon } from "./booksy/parse-salon.js";
7
7
  export { toSultanaDraft } from "./booksy/to-sultana.js";
8
8
  export type { SultanaDraftDay, SultanaPhotoDraft, SultanaSalonDraft, SultanaSalonDraftProfile, SultanaServiceDraft, SultanaWorkerDraft, } from "./booksy/to-sultana.js";
9
+ export { createBooksyPhotoTools } from "./booksy/import-photos-tools.js";
10
+ export type { CreateBooksyPhotoToolsOptions } from "./booksy/import-photos-tools.js";
9
11
  export { createBooksyImportTools, createBooksyTools } from "./booksy/import-tools.js";
10
12
  export type { CreateBooksyImportToolsOptions, CreateBooksyToolsOptions } from "./booksy/import-tools.js";
11
13
  export { BooksyProfileReader, createBooksyReaderTools, readBooksySalonDraft } from "./booksy/tools.js";
@@ -26,7 +28,8 @@ export { hostTimeZoneProblem } from "./customer/time.js";
26
28
  export { IcSalonCorePort } from "./ic-salon-core-port.js";
27
29
  export type { IcSalonCorePortOptions } from "./ic-salon-core-port.js";
28
30
  export { SALON_AGENT_PERSONA_PL, SALON_BOOKSY_PERSONA_PL, SALON_READ_PERSONA_PL } from "./persona.js";
29
- export type { DiscountCodeInput, DiscountCodeView, MediaView, PromoView, PromoWriteInput, SalonAccess, SalonCorePort, SalonPromoPort, ServiceTypeView, ServiceView, ServiceWriteInput, SalonSchedulePort, TimeRange, WorkerView, } from "./salon-core-port.js";
31
+ export type { DiscountCodeInput, DiscountCodeView, ImageUploadInput, MediaView, PromoView, PromoWriteInput, SalonAccess, SalonCorePort, SalonPhotoPort, SalonPhotosView, SalonPromoPort, ServiceTypeView, ServiceView, ServiceWriteInput, SalonSchedulePort, TimeRange, WorkerView, } from "./salon-core-port.js";
32
+ export { MAX_IMAGE_UPLOAD_BYTES } from "./salon-core-port.js";
30
33
  export { err, needsConfirmation, ok } from "./types.js";
31
34
  export type { AgentTool, ToolErrorCode, ToolParametersSchema, ToolResult } from "./types.js";
32
35
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACrF,YAAY,EACR,yBAAyB,EACzB,mBAAmB,EACnB,YAAY,EACZ,iBAAiB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,YAAY,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,YAAY,EACR,WAAW,EACX,eAAe,EACf,eAAe,EACf,eAAe,EACf,WAAW,EACX,aAAa,EACb,qBAAqB,EACrB,aAAa,EACb,aAAa,GAChB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,YAAY,EACR,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,wBAAwB,EACxB,mBAAmB,EACnB,kBAAkB,GACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACtF,YAAY,EAAE,8BAA8B,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACzG,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACvG,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,8BAA8B,EAAE,MAAM,mBAAmB,CAAC;AAC1G,OAAO,EACH,oBAAoB,EACpB,4BAA4B,EAC5B,gBAAgB,EAChB,wBAAwB,GAC3B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACR,2BAA2B,EAC3B,+BAA+B,EAC/B,uBAAuB,GAC1B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACH,uBAAuB,EACvB,+BAA+B,EAC/B,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,GAC1B,MAAM,qCAAqC,CAAC;AAC7C,YAAY,EACR,8BAA8B,EAC9B,kCAAkC,EAClC,0BAA0B,GAC7B,MAAM,qCAAqC,CAAC;AAC7C,YAAY,EACR,qBAAqB,EACrB,eAAe,EACf,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,SAAS,EACT,cAAc,GACjB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,YAAY,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AACrF,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAChF,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AACxF,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACxE,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,YAAY,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACtG,YAAY,EACR,iBAAiB,EACjB,gBAAgB,EAChB,SAAS,EACT,SAAS,EACT,eAAe,EACf,WAAW,EACX,aAAa,EACb,cAAc,EACd,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,iBAAiB,EACjB,SAAS,EACT,UAAU,GACb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,GAAG,EAAE,iBAAiB,EAAE,EAAE,EAAE,MAAM,YAAY,CAAC;AACxD,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@ export { ConfirmationLedger, DEFAULT_CONFIRMATION_TTL_MS } from "./confirmations
2
2
  export { BooksyReadError } from "./booksy/fetch-page.js";
3
3
  export { parseBooksySalon } from "./booksy/parse-salon.js";
4
4
  export { toSultanaDraft } from "./booksy/to-sultana.js";
5
+ export { createBooksyPhotoTools } from "./booksy/import-photos-tools.js";
5
6
  export { createBooksyImportTools, createBooksyTools } from "./booksy/import-tools.js";
6
7
  export { BooksyProfileReader, createBooksyReaderTools, readBooksySalonDraft } from "./booksy/tools.js";
7
8
  export { createSalonReadTools, createSalonReadToolsFromPort, createSalonTools, createSalonToolsFromPort, } from "./create-salon-tools.js";
@@ -13,4 +14,5 @@ export { customerLanguageOf, customerTexts } from "./customer/texts.js";
13
14
  export { hostTimeZoneProblem } from "./customer/time.js";
14
15
  export { IcSalonCorePort } from "./ic-salon-core-port.js";
15
16
  export { SALON_AGENT_PERSONA_PL, SALON_BOOKSY_PERSONA_PL, SALON_READ_PERSONA_PL } from "./persona.js";
17
+ export { MAX_IMAGE_UPLOAD_BYTES } from "./salon-core-port.js";
16
18
  export { err, needsConfirmation, ok } from "./types.js";
package/dist/persona.d.ts CHANGED
@@ -22,5 +22,5 @@ export declare const SALON_READ_PERSONA_PL = "Jeste\u015B asystentem w\u0142a\u0
22
22
  * The rules for the Booksy set (`createBooksyTools`) — appended to `SALON_AGENT_PERSONA_PL` by a
23
23
  * host that hands those tools to the agent. Separate, because a host may run without them.
24
24
  */
25
- export declare const SALON_BOOKSY_PERSONA_PL = "Kopiowanie salonu z Booksy:\n- Gdy w\u0142a\u015Bcicielka chce przenie\u015B\u0107 co\u015B z Booksy, popro\u015B o adres jej profilu\n (https://booksy.com/pl-pl/<numer>_<nazwa>\u2026), je\u015Bli go nie poda\u0142a.\n- read_booksy_salon i read_booksy_services tylko czytaj\u0105 profil \u2014 niczego nie zapisuj\u0105.\n- Us\u0142ugi kopiuj WY\u0141\u0104CZNIE przez import_booksy_services (wszystkie naraz, jedno potwierdzenie).\n Nie przepisuj ich pojedynczo przez add_service.\n- Podgl\u0105d importu poka\u017C w\u0142a\u015Bcicielce w ca\u0142o\u015Bci, razem z uwagami pod list\u0105. Poprawki\n (\u201Epomi\u0144 X\u201D, \u201EX to inny typ\u201D) podaj w overrides i poka\u017C nowy podgl\u0105d.\n- Gdy import wypisze us\u0142ugi, kt\u00F3rym nie dobra\u0142 typu, dobierz typy przez find_service_type,\n podaj je w overrides i poka\u017C nowy podgl\u0105d.\n- Kopiowania pracownik\u00F3w jeszcze nie ma: wywo\u0142aj import_booksy_workers i przeka\u017C, co odpowie.\n- Nazwy, adresu, opisu i godzin otwarcia salonu nie da si\u0119 jeszcze zmieni\u0107 przez czat \u2014 powiedz to\n wprost. Nie wymy\u015Blaj innego powodu.";
25
+ export declare const SALON_BOOKSY_PERSONA_PL = "Kopiowanie salonu z Booksy:\n- Gdy w\u0142a\u015Bcicielka chce przenie\u015B\u0107 co\u015B z Booksy, popro\u015B o adres jej profilu\n (https://booksy.com/pl-pl/<numer>_<nazwa>\u2026), je\u015Bli go nie poda\u0142a.\n- read_booksy_salon i read_booksy_services tylko czytaj\u0105 profil \u2014 niczego nie zapisuj\u0105.\n- Us\u0142ugi kopiuj WY\u0141\u0104CZNIE przez import_booksy_services (wszystkie naraz, jedno potwierdzenie).\n Nie przepisuj ich pojedynczo przez add_service.\n- Podgl\u0105d importu poka\u017C w\u0142a\u015Bcicielce w ca\u0142o\u015Bci, razem z uwagami pod list\u0105. Poprawki\n (\u201Epomi\u0144 X\u201D, \u201EX to inny typ\u201D) podaj w overrides i poka\u017C nowy podgl\u0105d.\n- Gdy import wypisze us\u0142ugi, kt\u00F3rym nie dobra\u0142 typu, dobierz typy przez find_service_type,\n podaj je w overrides i poka\u017C nowy podgl\u0105d.\n- Zdj\u0119cia: import_booksy_logo kopiuje logo i ustawia je jako miniatur\u0119 salonu,\n import_booksy_cover kopiuje ok\u0142adk\u0119 i ustawia j\u0105 jako ok\u0142adk\u0119 salonu. To dwa osobne narz\u0119dzia\n i dwie osobne zgody. Je\u015Bli nie masz tych narz\u0119dzi, powiedz, \u017Ce kopiowanie zdj\u0119\u0107 nie jest tu\n dost\u0119pne. Pozosta\u0142ych zdj\u0119\u0107 (portfolio us\u0142ug, wn\u0119trze, inspiracje, zesp\u00F3\u0142) nie da si\u0119 jeszcze\n skopiowa\u0107 \u2014 powiedz to wprost.\n- Kopiowania pracownik\u00F3w jeszcze nie ma: wywo\u0142aj import_booksy_workers i przeka\u017C, co odpowie.\n- Nazwy, adresu, opisu i godzin otwarcia salonu nie da si\u0119 jeszcze zmieni\u0107 przez czat \u2014 powiedz to\n wprost. Nie wymy\u015Blaj innego powodu.";
26
26
  //# sourceMappingURL=persona.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"persona.d.ts","sourceRoot":"","sources":["../src/persona.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,i2EAyBoB,CAAC;AAExD;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,6wCAee,CAAC;AAElD;;;GAGG;AACH,eAAO,MAAM,uBAAuB,2nCAYE,CAAC"}
1
+ {"version":3,"file":"persona.d.ts","sourceRoot":"","sources":["../src/persona.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,i2EAyBoB,CAAC;AAExD;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,6wCAee,CAAC;AAElD;;;GAGG;AACH,eAAO,MAAM,uBAAuB,soDAiBE,CAAC"}
package/dist/persona.js CHANGED
@@ -72,6 +72,11 @@ export const SALON_BOOKSY_PERSONA_PL = `Kopiowanie salonu z Booksy:
72
72
  („pomiń X”, „X to inny typ”) podaj w overrides i pokaż nowy podgląd.
73
73
  - Gdy import wypisze usługi, którym nie dobrał typu, dobierz typy przez find_service_type,
74
74
  podaj je w overrides i pokaż nowy podgląd.
75
+ - Zdjęcia: import_booksy_logo kopiuje logo i ustawia je jako miniaturę salonu,
76
+ import_booksy_cover kopiuje okładkę i ustawia ją jako okładkę salonu. To dwa osobne narzędzia
77
+ i dwie osobne zgody. Jeśli nie masz tych narzędzi, powiedz, że kopiowanie zdjęć nie jest tu
78
+ dostępne. Pozostałych zdjęć (portfolio usług, wnętrze, inspiracje, zespół) nie da się jeszcze
79
+ skopiować — powiedz to wprost.
75
80
  - Kopiowania pracowników jeszcze nie ma: wywołaj import_booksy_workers i przekaż, co odpowie.
76
81
  - Nazwy, adresu, opisu i godzin otwarcia salonu nie da się jeszcze zmienić przez czat — powiedz to
77
82
  wprost. Nie wymyślaj innego powodu.`;
@@ -48,7 +48,36 @@ export interface ServiceTypeView {
48
48
  label: string;
49
49
  category: string | null;
50
50
  }
51
+ /** The salon's two profile photos — there are exactly two slots, not a gallery. */
52
+ export interface SalonPhotosView {
53
+ /** The square picture of the salon on lists and maps. */
54
+ thumbnailAssetId: string | null;
55
+ /** The wide picture at the top of the salon's page. */
56
+ coverAssetId: string | null;
57
+ }
58
+ export interface ImageUploadInput {
59
+ /** The name in the salon's media library. At most 200 characters. */
60
+ name: string;
61
+ contentType: "image/jpeg" | "image/png" | "image/webp";
62
+ /** At most `MAX_IMAGE_UPLOAD_BYTES`. The bytes stay between a tool and the port — a model never sees them. */
63
+ bytes: Uint8Array;
64
+ }
65
+ /** What the assets canister takes in one call; a larger image has to be downscaled first. */
66
+ export declare const MAX_IMAGE_UPLOAD_BYTES = 1900000;
67
+ /**
68
+ * Writing pictures. Optional on the port, because it needs a second canister — the assets
69
+ * canister — and an identity that canister accepts; a host that has neither gets no photo tools.
70
+ */
71
+ export interface SalonPhotoPort {
72
+ getSalonPhotos(): Promise<SalonPhotosView>;
73
+ /** Sets BOTH slots; `null` clears one. A caller that changes one slot passes the other back. */
74
+ setSalonPhotos(photos: SalonPhotosView): Promise<void>;
75
+ /** Stores the image in the salon's media library and resolves, once the library lists it, to its asset id. */
76
+ uploadImage(input: ImageUploadInput): Promise<string>;
77
+ }
51
78
  export interface SalonCorePort extends SalonSchedulePort, SalonPromoPort {
79
+ /** Present only when the port can write pictures. */
80
+ readonly photos?: SalonPhotoPort;
52
81
  listServices(): Promise<ServiceView[]>;
53
82
  addService(input: ServiceWriteInput): Promise<string>;
54
83
  updateService(serviceId: string, input: ServiceWriteInput): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"salon-core-port.d.ts","sourceRoot":"","sources":["../src/salon-core-port.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE7C,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;IAChB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,iCAAiC;IACjC,SAAS,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;uEACuE;AACvE,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;IAChB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,SAAS,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,MAAM,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,aAAc,SAAQ,iBAAiB,EAAE,cAAc;IACpE,YAAY,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IACvC,UAAU,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACtD,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhD,WAAW,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IAErC;4EACwE;IACxE,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;CAC/D;AAED,mGAAmG;AACnG,MAAM,WAAW,SAAS;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAC9B,gEAAgE;IAChE,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACpE,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElF,0EAA0E;IAC1E,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACnE,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjF,iCAAiC;IACjC,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,gEAAgE;IAChE,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrE;AAED,MAAM,WAAW,SAAS;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;CACrC;AAED,MAAM,WAAW,SAAS;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;0EACsE;IACtE,QAAQ,EAAE,OAAO,CAAC;IAClB,wFAAwF;IACxF,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;yEACyE;AACzE,MAAM,WAAW,eAAe;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,iEAAiE;AACjE,MAAM,WAAW,cAAc;IAC3B,qFAAqF;IACrF,SAAS,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAElC,UAAU,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClD,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5C,iBAAiB,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACjD,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD"}
1
+ {"version":3,"file":"salon-core-port.d.ts","sourceRoot":"","sources":["../src/salon-core-port.ts"],"names":[],"mappings":"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"}
@@ -8,4 +8,5 @@
8
8
  * The port is **salon-scoped**: the implementation carries the salon id, so no method takes one.
9
9
  * That is what makes it impossible for a model to address another salon.
10
10
  */
11
- export {};
11
+ /** What the assets canister takes in one call; a larger image has to be downscaled first. */
12
+ export const MAX_IMAGE_UPLOAD_BYTES = 1_900_000;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsm-mit/sultana-agent-tools-package",
3
- "version": "0.7.0",
3
+ "version": "0.9.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": {
@@ -38,6 +38,7 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@icp-sdk/core": "^6.1.0",
41
+ "@jsm-mit/sultana-assets-rust-package": "^0.1.0",
41
42
  "@jsm-mit/sultana-core-motoko-package": "^0.21.1"
42
43
  },
43
44
  "devDependencies": {