@diegoaltoworks/chatter 0.33.0 → 0.35.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 (41) hide show
  1. package/dist/channels/whatsapp/images.d.ts +85 -0
  2. package/dist/channels/whatsapp/images.d.ts.map +1 -0
  3. package/dist/channels/whatsapp/images.test.d.ts +2 -0
  4. package/dist/channels/whatsapp/images.test.d.ts.map +1 -0
  5. package/dist/channels/whatsapp/inbound.d.ts +9 -0
  6. package/dist/channels/whatsapp/inbound.d.ts.map +1 -1
  7. package/dist/channels/whatsapp/index.d.ts +1 -0
  8. package/dist/channels/whatsapp/index.d.ts.map +1 -1
  9. package/dist/channels/whatsapp/index.js +82 -0
  10. package/dist/channels/whatsapp/index.mjs +79 -0
  11. package/dist/scheduler/claimStore.d.ts +27 -0
  12. package/dist/scheduler/claimStore.d.ts.map +1 -0
  13. package/dist/scheduler/claimStore.test.d.ts +2 -0
  14. package/dist/scheduler/claimStore.test.d.ts.map +1 -0
  15. package/dist/scheduler/compose.d.ts +23 -0
  16. package/dist/scheduler/compose.d.ts.map +1 -0
  17. package/dist/scheduler/compose.test.d.ts +2 -0
  18. package/dist/scheduler/compose.test.d.ts.map +1 -0
  19. package/dist/scheduler/deliver.d.ts +10 -0
  20. package/dist/scheduler/deliver.d.ts.map +1 -0
  21. package/dist/scheduler/deliver.test.d.ts +2 -0
  22. package/dist/scheduler/deliver.test.d.ts.map +1 -0
  23. package/dist/scheduler/dueDrops.d.ts +13 -0
  24. package/dist/scheduler/dueDrops.d.ts.map +1 -0
  25. package/dist/scheduler/dueDrops.test.d.ts +2 -0
  26. package/dist/scheduler/dueDrops.test.d.ts.map +1 -0
  27. package/dist/scheduler/index.d.ts +31 -0
  28. package/dist/scheduler/index.d.ts.map +1 -0
  29. package/dist/scheduler/index.js +332 -0
  30. package/dist/scheduler/index.mjs +290 -0
  31. package/dist/scheduler/scheduler.d.ts +44 -0
  32. package/dist/scheduler/scheduler.d.ts.map +1 -0
  33. package/dist/scheduler/scheduler.test.d.ts +2 -0
  34. package/dist/scheduler/scheduler.test.d.ts.map +1 -0
  35. package/dist/scheduler/tick.d.ts +19 -0
  36. package/dist/scheduler/tick.d.ts.map +1 -0
  37. package/dist/scheduler/tick.test.d.ts +2 -0
  38. package/dist/scheduler/tick.test.d.ts.map +1 -0
  39. package/dist/scheduler/types.d.ts +36 -0
  40. package/dist/scheduler/types.d.ts.map +1 -0
  41. package/package.json +8 -2
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Optional image requests in the WhatsApp channel: detect a drawing/picture
3
+ * request (with or without an attached photo), route it through `../../images`,
4
+ * and reply with the generated picture. Inert unless `./images` is
5
+ * configured — `createWhatsAppImageHandler` returns `false` for every
6
+ * message when it isn't, so callers fall through to normal chat.
7
+ *
8
+ * This module ships no bot personality: the detector and subject-cleaning
9
+ * patterns are generic drawing verbs (no hardcoded bot names), and every
10
+ * reply string (ack/limit/moderation/error) is caller-configured with no
11
+ * default text.
12
+ *
13
+ * Downloading the attached photo needs Baileys' `downloadMediaMessage`,
14
+ * which — per this subpath's rule that only `./baileys` touches the
15
+ * optional peer dependency at runtime — is injected as `downloadPhoto`
16
+ * rather than imported here. The host wires it via `./whatsapp`'s
17
+ * `loadBaileys` (see docs/channels.md), not this module.
18
+ *
19
+ * Everything pulled in from `../../images` is type-only: each subpath is
20
+ * built as its own bundle, so a value import here (even just for an
21
+ * `instanceof` check) would inline the whole `./images` module — OpenAI SDK
22
+ * included — into `./whatsapp`, and importing from the `../../images`
23
+ * barrel rather than its leaf files pulls in every sibling module's value
24
+ * imports too. Error mapping below checks `code`/`name` structurally
25
+ * instead, which also means it works when the error crossed a bundle
26
+ * boundary (the two subpaths' `ImageGenerationError` classes are not
27
+ * `instanceof`-compatible with each other).
28
+ */
29
+ import type { WAMessage, WASocket } from "@whiskeysockets/baileys";
30
+ import type { CaptionComposer } from "../../images/captionComposer";
31
+ import type { ImageUploader } from "../../images/uploader";
32
+ export interface DrawRequestDetectorConfig {
33
+ /** Tested against the raw message text to decide whether it's an image request. Defaults to a generic set of drawing verbs — no bot-specific names. */
34
+ detectPattern?: RegExp;
35
+ /** Applied in order to derive the subject from the raw text (mention tokens, greetings/politeness, the draw verb itself). */
36
+ stripPatterns?: readonly RegExp[];
37
+ }
38
+ export declare function isDrawRequest(text: string, config?: DrawRequestDetectorConfig): boolean;
39
+ /** Cleans a draw request into a scene subject: "@1234 please draw me a cat" -> "cat". Falls back to the trimmed original when the patterns strip everything (or nothing). */
40
+ export declare function drawSubject(text: string, config?: DrawRequestDetectorConfig): string;
41
+ /** Neutral, overridable reply strings — this module ships no bot personality. Unset = silent for that outcome. */
42
+ export interface WhatsAppImageStrings {
43
+ /** Sent immediately on accepting the request, before generation starts. */
44
+ ack?: string;
45
+ /** Sent when the per-sender/day cap (`checkAndReserve`) blocks the request. */
46
+ limitReached?: string;
47
+ /** Sent when the image API blocks the request on moderation grounds. */
48
+ moderationBlocked?: string;
49
+ /** Sent for any other generation/upload/download failure. */
50
+ error?: string;
51
+ }
52
+ export interface WhatsAppImageHandlerDeps {
53
+ /** The slice of `./images`' `ImageUploader` this module needs. */
54
+ images: Pick<ImageUploader, "isConfigured" | "peekCached" | "getOrCreateImage">;
55
+ /**
56
+ * Per-sender/day reservation (e.g. `./usage`'s `DailyLimiter.checkAndReserve`).
57
+ * Omit to skip the cap entirely. Checked AFTER the cache, so a cache hit
58
+ * never burns a unit.
59
+ */
60
+ checkAndReserve?: (senderId: string) => Promise<{
61
+ allowed: boolean;
62
+ }>;
63
+ /**
64
+ * Downloads the attached photo as bytes. Omit to ignore attached photos —
65
+ * text-only requests still work. See the module docstring for why this is
66
+ * injected rather than imported.
67
+ */
68
+ downloadPhoto?: (message: WAMessage) => Promise<Uint8Array>;
69
+ /** Best-effort tailored caption for the reply. Omit to send the image with no caption. */
70
+ captionComposer?: Pick<CaptionComposer, "composeCaption">;
71
+ detector?: DrawRequestDetectorConfig;
72
+ strings?: WhatsAppImageStrings;
73
+ }
74
+ export interface WhatsAppImageRequestContext {
75
+ sock: WASocket;
76
+ message: WAMessage;
77
+ chatId: string;
78
+ senderId: string;
79
+ text: string;
80
+ hasPhoto: boolean;
81
+ }
82
+ /** `true` = the message was an image request and this module replied to it (successfully or with a mapped error) — the caller must not also route it through normal chat. `false` = not an image request, or `./images` isn't configured. */
83
+ export type WhatsAppImageHandler = (ctx: WhatsAppImageRequestContext) => Promise<boolean>;
84
+ export declare function createWhatsAppImageHandler(deps: WhatsAppImageHandlerDeps): WhatsAppImageHandler;
85
+ //# sourceMappingURL=images.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"images.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/images.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAkB3D,MAAM,WAAW,yBAAyB;IACxC,uJAAuJ;IACvJ,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6HAA6H;IAC7H,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAOD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,yBAA8B,GAAG,OAAO,CAE3F;AAED,6KAA6K;AAC7K,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,yBAA8B,GAAG,MAAM,CAUxF;AAED,kHAAkH;AAClH,MAAM,WAAW,oBAAoB;IACnC,2EAA2E;IAC3E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,wBAAwB;IACvC,kEAAkE;IAClE,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,cAAc,GAAG,YAAY,GAAG,kBAAkB,CAAC,CAAC;IAChF;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACtE;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,SAAS,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IAC5D,0FAA0F;IAC1F,eAAe,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,gBAAgB,CAAC,CAAC;IAC1D,QAAQ,CAAC,EAAE,yBAAyB,CAAC;IACrC,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC;AAED,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,SAAS,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,6OAA6O;AAC7O,MAAM,MAAM,oBAAoB,GAAG,CAAC,GAAG,EAAE,2BAA2B,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAsB1F,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,wBAAwB,GAAG,oBAAoB,CA4C/F"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=images.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"images.test.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/images.test.ts"],"names":[],"mappings":""}
@@ -50,6 +50,7 @@ import type { PromptLoader } from "../../core/prompts";
50
50
  import type { VectorStore } from "../../core/retrieval";
51
51
  import { type SessionIdentityRegistry } from "../gates";
52
52
  import type { WhatsAppMessageEvent } from "./channel";
53
+ import type { WhatsAppImageHandler } from "./images";
53
54
  export declare function isGroupJid(jid: string): boolean;
54
55
  /** "447700900123:17@s.whatsapp.net" (device-suffixed) and "447700900123@s.whatsapp.net" match. */
55
56
  export declare function jidsMatch(a: string | undefined, b: string | undefined): boolean;
@@ -124,6 +125,14 @@ export interface WhatsAppInboundConfig {
124
125
  senderPhone: string;
125
126
  text: string;
126
127
  }) => string | undefined | Promise<string | undefined>;
128
+ /**
129
+ * Optional image-request routing (see `./images`). Consulted right before
130
+ * the normal chat pipeline; a `true` result means the message was handled
131
+ * as an image request (successfully or with a mapped error string) and
132
+ * this handler returns without answering through chat. Inert unless
133
+ * `./images` is configured.
134
+ */
135
+ images?: WhatsAppImageHandler;
127
136
  now?: () => number;
128
137
  }
129
138
  /** Builds the `onMessage` handler for `createWhatsAppChannel` (see `./channel`) — see the module docstring for how to wire `ServerDependencies` into it. */
@@ -1 +1 @@
1
- {"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/inbound.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,EAAE,KAAK,QAAQ,EAAc,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,KAAK,UAAU,EAAkB,MAAM,oBAAoB,CAAC;AAErE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAKL,KAAK,uBAAuB,EAE7B,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAItD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED,kGAAkG;AAClG,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAI/E;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGpD;AAeD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,SAAS,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CA+BjB;AAID,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,SAAS,GAAG,gBAAgB,CAWnE;AAED,mHAAmH;AACnH,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM,CAWtD;AAID,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;OAOG;IACH,QAAQ,EAAE,uBAAuB,CAAC;IAClC,4HAA4H;IAC5H,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gHAAgH;IAChH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAChD,cAAc,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,oGAAoG;IACpG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;KACd,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACvD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAqBD,4JAA4J;AAC5J,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,qBAAqB,GAC5B,CAAC,KAAK,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CA0GhD"}
1
+ {"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/inbound.ts"],"names":[],"mappings":"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"}
@@ -29,6 +29,7 @@ export type { Baileys } from "./baileys";
29
29
  export { loadBaileys } from "./baileys";
30
30
  export { acquireSessionLease, createWhatsAppChannel, type LeaseGatedConnectDeps, reconnectDelayMs, senderNameFor, shutdownWaSessions, type WaSessionHandle, type WhatsAppChannelConfig, type WhatsAppMessageEvent, } from "./channel";
31
31
  export { decrypt, encrypt } from "./crypto";
32
+ export { createWhatsAppImageHandler, type DrawRequestDetectorConfig, drawSubject, isDrawRequest, type WhatsAppImageHandler, type WhatsAppImageHandlerDeps, type WhatsAppImageRequestContext, type WhatsAppImageStrings, } from "./images";
32
33
  export { createWhatsAppInboundHandler, extractText, isGroupJid, jidsMatch, jidToPhoneNumber, messageContext, senderPhoneFor, type WaMessageContext, type WhatsAppInboundConfig, } from "./inbound";
33
34
  export { canAcquireLease, createTursoWaLeaseStore, LEASE_HEARTBEAT_MS, LEASE_STALE_MS, LEASE_WAIT_MS, type WaLeaseRow, type WaLeaseStore, } from "./lease";
34
35
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,qBAAqB,EAC1B,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EACL,4BAA4B,EAC5B,WAAW,EACX,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,GAC3B,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,KAAK,UAAU,EACf,KAAK,YAAY,GAClB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,qBAAqB,EAC1B,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EACL,0BAA0B,EAC1B,KAAK,yBAAyB,EAC9B,WAAW,EACX,aAAa,EACb,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,2BAA2B,EAChC,KAAK,oBAAoB,GAC1B,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,4BAA4B,EAC5B,WAAW,EACX,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,GAC3B,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,KAAK,UAAU,EACf,KAAK,YAAY,GAClB,MAAM,SAAS,CAAC"}
@@ -37,10 +37,13 @@ __export(index_exports, {
37
37
  canAcquireLease: () => canAcquireLease,
38
38
  createTursoWaLeaseStore: () => createTursoWaLeaseStore,
39
39
  createWhatsAppChannel: () => createWhatsAppChannel,
40
+ createWhatsAppImageHandler: () => createWhatsAppImageHandler,
40
41
  createWhatsAppInboundHandler: () => createWhatsAppInboundHandler,
41
42
  decrypt: () => decrypt,
43
+ drawSubject: () => drawSubject,
42
44
  encrypt: () => encrypt,
43
45
  extractText: () => extractText,
46
+ isDrawRequest: () => isDrawRequest,
44
47
  isGroupJid: () => isGroupJid,
45
48
  jidToPhoneNumber: () => jidToPhoneNumber,
46
49
  jidsMatch: () => jidsMatch,
@@ -467,6 +470,71 @@ function createWhatsAppChannel(config) {
467
470
  };
468
471
  }
469
472
 
473
+ // src/channels/whatsapp/images.ts
474
+ var DEFAULT_DETECT_PATTERN = /\b(draw|paint|sketch|illustrate)\b|\b(make|generate|create)\b[^.!?]{0,20}\b(picture|image|drawing|photo|cartoon)\b/i;
475
+ var DEFAULT_STRIP_PATTERNS = [
476
+ /@\d+/g,
477
+ /^(hey|hi|please|ok)[,!\s]+/i,
478
+ /^(can|could|will)\s+you\s+/i,
479
+ // Longest-alternative-first ((an|a), not (a|an)): a first-match-wins
480
+ // alternation would otherwise take "a" out of "an" and leave a stray "n".
481
+ /^(please\s+)?(draw|paint|sketch|illustrate|make|generate|create)\s+(me|us)?\s*(an|a)?\s*(picture|image|drawing|sketch|photo|cartoon)?\s*(of\s+)?/i
482
+ ];
483
+ function testPattern(pattern, text) {
484
+ return new RegExp(pattern.source, pattern.flags.replace(/[gy]/g, "")).test(text);
485
+ }
486
+ function isDrawRequest(text, config = {}) {
487
+ return testPattern(config.detectPattern ?? DEFAULT_DETECT_PATTERN, text);
488
+ }
489
+ function drawSubject(text, config = {}) {
490
+ let subject = text;
491
+ for (const pattern of config.stripPatterns ?? DEFAULT_STRIP_PATTERNS) {
492
+ subject = subject.replace(pattern, " ").replace(/\s+/g, " ").trim();
493
+ }
494
+ return subject.length > 0 ? subject : text.trim();
495
+ }
496
+ function isModerationError(error) {
497
+ return typeof error === "object" && error !== null && error.name === "ImageGenerationError" && error.code === "moderation";
498
+ }
499
+ async function replyText(sock, chatId, message, text) {
500
+ if (!text) return;
501
+ await sock.sendMessage(chatId, { text }, { quoted: message }).catch(() => void 0);
502
+ }
503
+ function createWhatsAppImageHandler(deps) {
504
+ return async function handleWhatsAppImageRequest(ctx) {
505
+ if (!deps.images.isConfigured()) return false;
506
+ if (!isDrawRequest(ctx.text, deps.detector)) return false;
507
+ const subject = drawSubject(ctx.text, deps.detector);
508
+ try {
509
+ const photoBytes = ctx.hasPhoto ? await deps.downloadPhoto?.(ctx.message) : void 0;
510
+ const inputImages = photoBytes ? [{ bytes: photoBytes }] : void 0;
511
+ const request = { prompt: subject, inputImages };
512
+ if (deps.checkAndReserve) {
513
+ const cached2 = await deps.images.peekCached(request);
514
+ if (!cached2) {
515
+ const reserved = await deps.checkAndReserve(ctx.senderId);
516
+ if (!reserved.allowed) {
517
+ await replyText(ctx.sock, ctx.chatId, ctx.message, deps.strings?.limitReached);
518
+ return true;
519
+ }
520
+ }
521
+ }
522
+ await replyText(ctx.sock, ctx.chatId, ctx.message, deps.strings?.ack);
523
+ const image = await deps.images.getOrCreateImage(request);
524
+ const caption = deps.captionComposer ? await deps.captionComposer.composeCaption({ subject }) : void 0;
525
+ await ctx.sock.sendMessage(
526
+ ctx.chatId,
527
+ caption ? { image: { url: image.url }, caption } : { image: { url: image.url } },
528
+ { quoted: ctx.message }
529
+ );
530
+ } catch (error) {
531
+ const text = isModerationError(error) ? deps.strings?.moderationBlocked : deps.strings?.error;
532
+ await replyText(ctx.sock, ctx.chatId, ctx.message, text);
533
+ }
534
+ return true;
535
+ };
536
+ }
537
+
470
538
  // src/core/guardrails.ts
471
539
  function detectLeakage(text) {
472
540
  const flags = [
@@ -751,6 +819,17 @@ function createWhatsAppInboundHandler(config) {
751
819
  return;
752
820
  }
753
821
  const senderPhone = await senderPhoneFor(sock, message, chatId);
822
+ if (config.images) {
823
+ const handled = await config.images({
824
+ sock,
825
+ message,
826
+ chatId,
827
+ senderId: senderPhone,
828
+ text,
829
+ hasPhoto: Boolean(message.message?.imageMessage)
830
+ });
831
+ if (handled) return;
832
+ }
754
833
  const personaLayer = await resolvePersonaLayer(config, { senderPhone, text });
755
834
  const buckets = await resolveBuckets({
756
835
  mode: "public",
@@ -793,10 +872,13 @@ function createWhatsAppInboundHandler(config) {
793
872
  canAcquireLease,
794
873
  createTursoWaLeaseStore,
795
874
  createWhatsAppChannel,
875
+ createWhatsAppImageHandler,
796
876
  createWhatsAppInboundHandler,
797
877
  decrypt,
878
+ drawSubject,
798
879
  encrypt,
799
880
  extractText,
881
+ isDrawRequest,
800
882
  isGroupJid,
801
883
  jidToPhoneNumber,
802
884
  jidsMatch,
@@ -411,6 +411,71 @@ function createWhatsAppChannel(config) {
411
411
  };
412
412
  }
413
413
 
414
+ // src/channels/whatsapp/images.ts
415
+ var DEFAULT_DETECT_PATTERN = /\b(draw|paint|sketch|illustrate)\b|\b(make|generate|create)\b[^.!?]{0,20}\b(picture|image|drawing|photo|cartoon)\b/i;
416
+ var DEFAULT_STRIP_PATTERNS = [
417
+ /@\d+/g,
418
+ /^(hey|hi|please|ok)[,!\s]+/i,
419
+ /^(can|could|will)\s+you\s+/i,
420
+ // Longest-alternative-first ((an|a), not (a|an)): a first-match-wins
421
+ // alternation would otherwise take "a" out of "an" and leave a stray "n".
422
+ /^(please\s+)?(draw|paint|sketch|illustrate|make|generate|create)\s+(me|us)?\s*(an|a)?\s*(picture|image|drawing|sketch|photo|cartoon)?\s*(of\s+)?/i
423
+ ];
424
+ function testPattern(pattern, text) {
425
+ return new RegExp(pattern.source, pattern.flags.replace(/[gy]/g, "")).test(text);
426
+ }
427
+ function isDrawRequest(text, config = {}) {
428
+ return testPattern(config.detectPattern ?? DEFAULT_DETECT_PATTERN, text);
429
+ }
430
+ function drawSubject(text, config = {}) {
431
+ let subject = text;
432
+ for (const pattern of config.stripPatterns ?? DEFAULT_STRIP_PATTERNS) {
433
+ subject = subject.replace(pattern, " ").replace(/\s+/g, " ").trim();
434
+ }
435
+ return subject.length > 0 ? subject : text.trim();
436
+ }
437
+ function isModerationError(error) {
438
+ return typeof error === "object" && error !== null && error.name === "ImageGenerationError" && error.code === "moderation";
439
+ }
440
+ async function replyText(sock, chatId, message, text) {
441
+ if (!text) return;
442
+ await sock.sendMessage(chatId, { text }, { quoted: message }).catch(() => void 0);
443
+ }
444
+ function createWhatsAppImageHandler(deps) {
445
+ return async function handleWhatsAppImageRequest(ctx) {
446
+ if (!deps.images.isConfigured()) return false;
447
+ if (!isDrawRequest(ctx.text, deps.detector)) return false;
448
+ const subject = drawSubject(ctx.text, deps.detector);
449
+ try {
450
+ const photoBytes = ctx.hasPhoto ? await deps.downloadPhoto?.(ctx.message) : void 0;
451
+ const inputImages = photoBytes ? [{ bytes: photoBytes }] : void 0;
452
+ const request = { prompt: subject, inputImages };
453
+ if (deps.checkAndReserve) {
454
+ const cached2 = await deps.images.peekCached(request);
455
+ if (!cached2) {
456
+ const reserved = await deps.checkAndReserve(ctx.senderId);
457
+ if (!reserved.allowed) {
458
+ await replyText(ctx.sock, ctx.chatId, ctx.message, deps.strings?.limitReached);
459
+ return true;
460
+ }
461
+ }
462
+ }
463
+ await replyText(ctx.sock, ctx.chatId, ctx.message, deps.strings?.ack);
464
+ const image = await deps.images.getOrCreateImage(request);
465
+ const caption = deps.captionComposer ? await deps.captionComposer.composeCaption({ subject }) : void 0;
466
+ await ctx.sock.sendMessage(
467
+ ctx.chatId,
468
+ caption ? { image: { url: image.url }, caption } : { image: { url: image.url } },
469
+ { quoted: ctx.message }
470
+ );
471
+ } catch (error) {
472
+ const text = isModerationError(error) ? deps.strings?.moderationBlocked : deps.strings?.error;
473
+ await replyText(ctx.sock, ctx.chatId, ctx.message, text);
474
+ }
475
+ return true;
476
+ };
477
+ }
478
+
414
479
  // src/core/guardrails.ts
415
480
  function detectLeakage(text) {
416
481
  const flags = [
@@ -695,6 +760,17 @@ function createWhatsAppInboundHandler(config) {
695
760
  return;
696
761
  }
697
762
  const senderPhone = await senderPhoneFor(sock, message, chatId);
763
+ if (config.images) {
764
+ const handled = await config.images({
765
+ sock,
766
+ message,
767
+ chatId,
768
+ senderId: senderPhone,
769
+ text,
770
+ hasPhoto: Boolean(message.message?.imageMessage)
771
+ });
772
+ if (handled) return;
773
+ }
698
774
  const personaLayer = await resolvePersonaLayer(config, { senderPhone, text });
699
775
  const buckets = await resolveBuckets({
700
776
  mode: "public",
@@ -736,10 +812,13 @@ export {
736
812
  canAcquireLease,
737
813
  createTursoWaLeaseStore,
738
814
  createWhatsAppChannel,
815
+ createWhatsAppImageHandler,
739
816
  createWhatsAppInboundHandler,
740
817
  decrypt,
818
+ drawSubject,
741
819
  encrypt,
742
820
  extractText,
821
+ isDrawRequest,
743
822
  isGroupJid,
744
823
  jidToPhoneNumber,
745
824
  jidsMatch,
@@ -0,0 +1,27 @@
1
+ /**
2
+ * A Turso-backed exactly-once claim: an atomic upsert means two instances
3
+ * racing the same entry can never both believe they claimed it — see
4
+ * `canClaim`, which mirrors `canAcquireLease` in
5
+ * `../channels/whatsapp/lease.ts`. A failed send calls `release` to delete
6
+ * the claim row, letting the entry be claimed again by whichever instance
7
+ * ticks next within the grace window. An instance that dies mid-delivery
8
+ * without releasing leaves its claim to go stale instead: after
9
+ * `claimTimeoutMs` with no renewal, another instance may take it over.
10
+ */
11
+ import type { Client } from "@libsql/client";
12
+ import type { ScheduleClaimStore } from "./types";
13
+ export declare const DEFAULT_CLAIM_TABLE = "chatter_schedule_claims";
14
+ /** How long an unreleased claim blocks reclaiming — short relative to a typical grace window, so a crashed claimant frees up in time for a retry to still land within grace. */
15
+ export declare const DEFAULT_CLAIM_TIMEOUT_MS: number;
16
+ export interface ScheduleClaimRow {
17
+ instanceId: string;
18
+ claimedAt: number;
19
+ }
20
+ /**
21
+ * Pure: may `instanceId` take (or keep) the claim on an entry right now,
22
+ * given the current row (null = unclaimed)? Mirrors the SQL WHERE clause in
23
+ * `createTursoScheduleClaimStore` — keep the two in sync.
24
+ */
25
+ export declare function canClaim(existing: ScheduleClaimRow | null, instanceId: string, now: number, claimTimeoutMs: number): boolean;
26
+ export declare function createTursoScheduleClaimStore(client: Client, instanceId: string, tableName?: string, claimTimeoutMs?: number): ScheduleClaimStore;
27
+ //# sourceMappingURL=claimStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claimStore.d.ts","sourceRoot":"","sources":["../../src/scheduler/claimStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD,eAAO,MAAM,mBAAmB,4BAA4B,CAAC;AAC7D,gLAAgL;AAChL,eAAO,MAAM,wBAAwB,QAAa,CAAC;AAKnD,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,gBAAgB,GAAG,IAAI,EACjC,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,EACX,cAAc,EAAE,MAAM,GACrB,OAAO,CAIT;AA8BD,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,MAA4B,EACvC,cAAc,GAAE,MAAiC,GAChD,kBAAkB,CA4BpB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=claimStore.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claimStore.test.d.ts","sourceRoot":"","sources":["../../src/scheduler/claimStore.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,23 @@
1
+ import type OpenAI from "openai";
2
+ import type { AnswerFn } from "../core/answer";
3
+ import type { PipelineMessage, PipelineMode } from "../core/pipeline";
4
+ import type { ComposeFn, ScheduleEntry } from "./types";
5
+ /**
6
+ * Runs the pluggable compose step and guarantees a message comes back: a
7
+ * missing compose function, a blank result, or a throwing/rejecting compose
8
+ * all fall back to `fallbackMessage` rather than blocking delivery.
9
+ */
10
+ export declare function composeMessage(entry: ScheduleEntry, compose: ComposeFn | undefined, fallbackMessage: string): Promise<string>;
11
+ export interface AnswerComposeOptions {
12
+ client: OpenAI;
13
+ system: string;
14
+ /** Turns a schedule entry's opaque payload into the messages sent to the brain. */
15
+ buildMessages: (entry: ScheduleEntry) => PipelineMessage[];
16
+ answerFn?: AnswerFn;
17
+ mode?: PipelineMode;
18
+ temperature?: number;
19
+ model?: string;
20
+ }
21
+ /** Builds a `ComposeFn` that composes via `answerFn`/`completeOnce`, for callers who want an LLM-written message instead of static text. */
22
+ export declare function createAnswerCompose(options: AnswerComposeOptions): ComposeFn;
23
+ //# sourceMappingURL=compose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../src/scheduler/compose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExD;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,SAAS,GAAG,SAAS,EAC9B,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,MAAM,CAAC,CASjB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,mFAAmF;IACnF,aAAa,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,eAAe,EAAE,CAAC;IAC3D,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,4IAA4I;AAC5I,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAa5E"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=compose.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.test.d.ts","sourceRoot":"","sources":["../../src/scheduler/compose.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ import type { ChannelSenderRegistry } from "../channels/senders";
2
+ import type { ScheduleEntry } from "./types";
3
+ export interface DeliverOptions {
4
+ senders: ChannelSenderRegistry;
5
+ /** Try voice first when the channel supports it; text is always the guaranteed fallback. */
6
+ voiceAttempted?: boolean;
7
+ }
8
+ /** Delivers a composed message. Text is guaranteed; voice is only ever a best-effort first attempt. */
9
+ export declare function deliver(entry: ScheduleEntry, message: string, options: DeliverOptions): Promise<boolean>;
10
+ //# sourceMappingURL=deliver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deliver.d.ts","sourceRoot":"","sources":["../../src/scheduler/deliver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,qBAAqB,CAAC;IAC/B,4FAA4F;IAC5F,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,uGAAuG;AACvG,wBAAsB,OAAO,CAC3B,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,OAAO,CAAC,CAMlB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=deliver.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deliver.test.d.ts","sourceRoot":"","sources":["../../src/scheduler/deliver.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Pure fire-time filtering. A schedule fires once its time has passed, but
3
+ * only within a grace window — a stale entry (the process was down, or the
4
+ * tick was delayed) past grace is skipped rather than delivered late.
5
+ */
6
+ import type { ScheduleEntry } from "./types";
7
+ export declare function isDue(entry: ScheduleEntry, now: number): boolean;
8
+ export declare function isWithinGrace(entry: ScheduleEntry, now: number, graceMs: number): boolean;
9
+ /** Due entries still inside their grace window, in caller-supplied order. */
10
+ export declare function dueDrops(entries: ScheduleEntry[], now: number, graceMs: number): ScheduleEntry[];
11
+ /** Due entries whose grace window has already passed — reported so callers can log them, never delivered. */
12
+ export declare function staleDrops(entries: ScheduleEntry[], now: number, graceMs: number): ScheduleEntry[];
13
+ //# sourceMappingURL=dueDrops.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dueDrops.d.ts","sourceRoot":"","sources":["../../src/scheduler/dueDrops.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,wBAAgB,KAAK,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAEzF;AAED,6EAA6E;AAC7E,wBAAgB,QAAQ,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,aAAa,EAAE,CAEhG;AAED,6GAA6G;AAC7G,wBAAgB,UAAU,CACxB,OAAO,EAAE,aAAa,EAAE,EACxB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,GACd,aAAa,EAAE,CAEjB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dueDrops.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dueDrops.test.d.ts","sourceRoot":"","sources":["../../src/scheduler/dueDrops.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Scheduler — exactly-once outbound delivery on a caller-supplied fire time.
3
+ * Published as the `@diegoaltoworks/chatter/scheduler` subpath so the core
4
+ * install stays untouched by it.
5
+ *
6
+ * Content-free: chatter stores no schedule data of its own. A caller
7
+ * supplies candidate {@link ScheduleEntry} objects each tick (its own store,
8
+ * cron, queue, anything); this module adds the parts that are easy to get
9
+ * wrong in a multi-instance deployment — an exactly-once claim in Turso so
10
+ * two instances never both deliver the same entry, a fire-time grace window
11
+ * so a delayed tick doesn't send stale messages, a pluggable compose step
12
+ * (static text or an LLM call via `answerFn`/`completeOnce`) that always
13
+ * falls back rather than blocking delivery, and delivery through the
14
+ * channel sender registry with an optional voice-attempted/text-guaranteed
15
+ * mode.
16
+ *
17
+ * @packageDocumentation
18
+ */
19
+ export type { ScheduleClaimRow } from "./claimStore";
20
+ export { canClaim, createTursoScheduleClaimStore, DEFAULT_CLAIM_TABLE, DEFAULT_CLAIM_TIMEOUT_MS, } from "./claimStore";
21
+ export type { AnswerComposeOptions } from "./compose";
22
+ export { composeMessage, createAnswerCompose } from "./compose";
23
+ export type { DeliverOptions } from "./deliver";
24
+ export { deliver } from "./deliver";
25
+ export { dueDrops, isDue, isWithinGrace, staleDrops } from "./dueDrops";
26
+ export type { Scheduler, SchedulerConfig } from "./scheduler";
27
+ export { createScheduler, DEFAULT_FALLBACK_MESSAGE, DEFAULT_GRACE_MS, DEFAULT_INTERVAL_MS, } from "./scheduler";
28
+ export type { TickOptions } from "./tick";
29
+ export { runTick } from "./tick";
30
+ export type { ComposeFn, ScheduleClaimStore, ScheduleEntry, TickResult } from "./types";
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scheduler/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EACL,QAAQ,EACR,6BAA6B,EAC7B,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChE,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxE,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACjC,YAAY,EAAE,SAAS,EAAE,kBAAkB,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC"}