@diegoaltoworks/chatter 0.57.0 → 0.58.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.
@@ -274,6 +274,16 @@ async function resolvePersonaLayer(config, ctx) {
274
274
  return void 0;
275
275
  }
276
276
  }
277
+ async function isHistoryEnabled(config, sender) {
278
+ const predicate = config.history?.historyEnabledFor;
279
+ if (!predicate) return true;
280
+ try {
281
+ return await predicate(sender);
282
+ } catch (error) {
283
+ config.logger?.error("historyEnabledFor threw; disabling history for this turn", error);
284
+ return false;
285
+ }
286
+ }
277
287
  function createInboundPipeline(deps, config) {
278
288
  const mutedChats = /* @__PURE__ */ new Set();
279
289
  const now = config.now ?? Date.now;
@@ -313,12 +323,15 @@ function createInboundPipeline(deps, config) {
313
323
  }
314
324
  const personaLayer = await resolvePersonaLayer(config, { sender, text: msg.text });
315
325
  const buckets = await resolveBuckets({ mode, sender, bucketsFor: config.bucketsFor });
316
- const priorTurns = config.history ? await config.history.store.load(
326
+ const historyEnabled = config.history ? await isHistoryEnabled(config, sender) : false;
327
+ const priorTurns = config.history && historyEnabled ? await config.history.store.load(
317
328
  conversationId,
318
329
  config.history.limit ?? DEFAULT_HISTORY_LIMIT
319
330
  ) : [];
320
331
  const messages = [...priorTurns, { role: "user", content: msg.text }];
321
- await config.history?.store.append(conversationId, { role: "user", content: msg.text });
332
+ if (historyEnabled) {
333
+ await config.history?.store.append(conversationId, { role: "user", content: msg.text });
334
+ }
322
335
  const { system } = await prepareChat({
323
336
  store: deps.store,
324
337
  prompts: deps.prompts,
@@ -350,7 +363,9 @@ function createInboundPipeline(deps, config) {
350
363
  if (!content) {
351
364
  return { action: "ignore" };
352
365
  }
353
- await config.history?.store.append(conversationId, { role: "assistant", content });
366
+ if (historyEnabled) {
367
+ await config.history?.store.append(conversationId, { role: "assistant", content });
368
+ }
354
369
  await turn.reply.sendAnswer(msg.chatId, content);
355
370
  return { action: "reply", content };
356
371
  };
@@ -241,6 +241,16 @@ async function resolvePersonaLayer(config, ctx) {
241
241
  return void 0;
242
242
  }
243
243
  }
244
+ async function isHistoryEnabled(config, sender) {
245
+ const predicate = config.history?.historyEnabledFor;
246
+ if (!predicate) return true;
247
+ try {
248
+ return await predicate(sender);
249
+ } catch (error) {
250
+ config.logger?.error("historyEnabledFor threw; disabling history for this turn", error);
251
+ return false;
252
+ }
253
+ }
244
254
  function createInboundPipeline(deps, config) {
245
255
  const mutedChats = /* @__PURE__ */ new Set();
246
256
  const now = config.now ?? Date.now;
@@ -280,12 +290,15 @@ function createInboundPipeline(deps, config) {
280
290
  }
281
291
  const personaLayer = await resolvePersonaLayer(config, { sender, text: msg.text });
282
292
  const buckets = await resolveBuckets({ mode, sender, bucketsFor: config.bucketsFor });
283
- const priorTurns = config.history ? await config.history.store.load(
293
+ const historyEnabled = config.history ? await isHistoryEnabled(config, sender) : false;
294
+ const priorTurns = config.history && historyEnabled ? await config.history.store.load(
284
295
  conversationId,
285
296
  config.history.limit ?? DEFAULT_HISTORY_LIMIT
286
297
  ) : [];
287
298
  const messages = [...priorTurns, { role: "user", content: msg.text }];
288
- await config.history?.store.append(conversationId, { role: "user", content: msg.text });
299
+ if (historyEnabled) {
300
+ await config.history?.store.append(conversationId, { role: "user", content: msg.text });
301
+ }
289
302
  const { system } = await prepareChat({
290
303
  store: deps.store,
291
304
  prompts: deps.prompts,
@@ -317,7 +330,9 @@ function createInboundPipeline(deps, config) {
317
330
  if (!content) {
318
331
  return { action: "ignore" };
319
332
  }
320
- await config.history?.store.append(conversationId, { role: "assistant", content });
333
+ if (historyEnabled) {
334
+ await config.history?.store.append(conversationId, { role: "assistant", content });
335
+ }
321
336
  await turn.reply.sendAnswer(msg.chatId, content);
322
337
  return { action: "reply", content };
323
338
  };
@@ -61,6 +61,14 @@ export interface InboundPipelineConfig {
61
61
  store: HistoryStore;
62
62
  /** Most recent turns to load per reply. @default 20 */
63
63
  limit?: number;
64
+ /**
65
+ * Excludes a sender from history entirely — checked before both `load`
66
+ * and `append`, so an opted-out sender's turns are never read back and
67
+ * never written. A throw/rejection fails closed ("not enabled"): a
68
+ * broken predicate must never start recording a sender who may have
69
+ * opted out. @default every sender is enabled
70
+ */
71
+ historyEnabledFor?: (sender: string) => boolean | Promise<boolean>;
64
72
  };
65
73
  /** Group chats eligible for a reply. Empty (default) = every group. Has no effect on DMs, which always reply. */
66
74
  allowedChats?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/channels/pipeline.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/channels/pipeline.ts"],"names":[],"mappings":"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"}
@@ -50,7 +50,14 @@ export interface TelegramChannelConfig {
50
50
  /** Off by default — the channel stays single-turn until a store is configured. */
51
51
  history?: {
52
52
  store: HistoryStore;
53
+ /** Most recent turns to load per reply. @default 20 */
53
54
  limit?: number;
55
+ /**
56
+ * Excludes a sender from history entirely — see
57
+ * `InboundPipelineConfig.history.historyEnabledFor` in `./channels`.
58
+ * @default every sender is enabled
59
+ */
60
+ historyEnabledFor?: (sender: string) => boolean | Promise<boolean>;
54
61
  };
55
62
  muteRegex?: RegExp;
56
63
  unmuteRegex?: RegExp;
@@ -1 +1 @@
1
- {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../../src/channels/telegram/channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAuB,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAGxC,OAAO,EAAqB,KAAK,WAAW,EAAuB,MAAM,OAAO,CAAC;AAOjF,MAAM,WAAW,qBAAqB;IACpC,qFAAqF;IACrF,QAAQ,EAAE,MAAM,CAAC;IACjB,8GAA8G;IAC9G,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iHAAiH;IACjH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,0IAA0I;IAC1I,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,iJAAiJ;IACjJ,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,kIAAkI;IAClI,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kIAAkI;IAClI,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4GAA4G;IAC5G,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;KACd,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACvD,kFAAkF;IAClF,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,YAAY,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAClD,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,kBAAkB;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,0GAA0G;IAC1G,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iGAAiG;IACjG,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,mGAAmG;IACnG,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qJAAqJ;IACrJ,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB,qEAAqE;IACrE,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,uGAAuG;IACvG,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AASD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAoI5E"}
1
+ {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../../src/channels/telegram/channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAuB,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAGxC,OAAO,EAAqB,KAAK,WAAW,EAAuB,MAAM,OAAO,CAAC;AAOjF,MAAM,WAAW,qBAAqB;IACpC,qFAAqF;IACrF,QAAQ,EAAE,MAAM,CAAC;IACjB,8GAA8G;IAC9G,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iHAAiH;IACjH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,0IAA0I;IAC1I,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,iJAAiJ;IACjJ,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,kIAAkI;IAClI,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kIAAkI;IAClI,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4GAA4G;IAC5G,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;KACd,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACvD,kFAAkF;IAClF,OAAO,CAAC,EAAE;QACR,KAAK,EAAE,YAAY,CAAC;QACpB,uDAAuD;QACvD,KAAK,CAAC,EAAE,MAAM,CAAC;QACf;;;;WAIG;QACH,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KACpE,CAAC;IACF,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,kBAAkB;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,0GAA0G;IAC1G,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iGAAiG;IACjG,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,mGAAmG;IACnG,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qJAAqJ;IACrJ,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB,qEAAqE;IACrE,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,uGAAuG;IACvG,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AASD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAoI5E"}
@@ -441,6 +441,16 @@ async function resolvePersonaLayer(config, ctx) {
441
441
  return void 0;
442
442
  }
443
443
  }
444
+ async function isHistoryEnabled(config, sender) {
445
+ const predicate = config.history?.historyEnabledFor;
446
+ if (!predicate) return true;
447
+ try {
448
+ return await predicate(sender);
449
+ } catch (error) {
450
+ config.logger?.error("historyEnabledFor threw; disabling history for this turn", error);
451
+ return false;
452
+ }
453
+ }
444
454
  function createInboundPipeline(deps, config) {
445
455
  const mutedChats = /* @__PURE__ */ new Set();
446
456
  const now = config.now ?? Date.now;
@@ -480,12 +490,15 @@ function createInboundPipeline(deps, config) {
480
490
  }
481
491
  const personaLayer = await resolvePersonaLayer(config, { sender, text: msg.text });
482
492
  const buckets = await resolveBuckets({ mode, sender, bucketsFor: config.bucketsFor });
483
- const priorTurns = config.history ? await config.history.store.load(
493
+ const historyEnabled = config.history ? await isHistoryEnabled(config, sender) : false;
494
+ const priorTurns = config.history && historyEnabled ? await config.history.store.load(
484
495
  conversationId,
485
496
  config.history.limit ?? DEFAULT_HISTORY_LIMIT
486
497
  ) : [];
487
498
  const messages = [...priorTurns, { role: "user", content: msg.text }];
488
- await config.history?.store.append(conversationId, { role: "user", content: msg.text });
499
+ if (historyEnabled) {
500
+ await config.history?.store.append(conversationId, { role: "user", content: msg.text });
501
+ }
489
502
  const { system } = await prepareChat({
490
503
  store: deps.store,
491
504
  prompts: deps.prompts,
@@ -517,7 +530,9 @@ function createInboundPipeline(deps, config) {
517
530
  if (!content) {
518
531
  return { action: "ignore" };
519
532
  }
520
- await config.history?.store.append(conversationId, { role: "assistant", content });
533
+ if (historyEnabled) {
534
+ await config.history?.store.append(conversationId, { role: "assistant", content });
535
+ }
521
536
  await turn.reply.sendAnswer(msg.chatId, content);
522
537
  return { action: "reply", content };
523
538
  };
@@ -399,6 +399,16 @@ async function resolvePersonaLayer(config, ctx) {
399
399
  return void 0;
400
400
  }
401
401
  }
402
+ async function isHistoryEnabled(config, sender) {
403
+ const predicate = config.history?.historyEnabledFor;
404
+ if (!predicate) return true;
405
+ try {
406
+ return await predicate(sender);
407
+ } catch (error) {
408
+ config.logger?.error("historyEnabledFor threw; disabling history for this turn", error);
409
+ return false;
410
+ }
411
+ }
402
412
  function createInboundPipeline(deps, config) {
403
413
  const mutedChats = /* @__PURE__ */ new Set();
404
414
  const now = config.now ?? Date.now;
@@ -438,12 +448,15 @@ function createInboundPipeline(deps, config) {
438
448
  }
439
449
  const personaLayer = await resolvePersonaLayer(config, { sender, text: msg.text });
440
450
  const buckets = await resolveBuckets({ mode, sender, bucketsFor: config.bucketsFor });
441
- const priorTurns = config.history ? await config.history.store.load(
451
+ const historyEnabled = config.history ? await isHistoryEnabled(config, sender) : false;
452
+ const priorTurns = config.history && historyEnabled ? await config.history.store.load(
442
453
  conversationId,
443
454
  config.history.limit ?? DEFAULT_HISTORY_LIMIT
444
455
  ) : [];
445
456
  const messages = [...priorTurns, { role: "user", content: msg.text }];
446
- await config.history?.store.append(conversationId, { role: "user", content: msg.text });
457
+ if (historyEnabled) {
458
+ await config.history?.store.append(conversationId, { role: "user", content: msg.text });
459
+ }
447
460
  const { system } = await prepareChat({
448
461
  store: deps.store,
449
462
  prompts: deps.prompts,
@@ -475,7 +488,9 @@ function createInboundPipeline(deps, config) {
475
488
  if (!content) {
476
489
  return { action: "ignore" };
477
490
  }
478
- await config.history?.store.append(conversationId, { role: "assistant", content });
491
+ if (historyEnabled) {
492
+ await config.history?.store.append(conversationId, { role: "assistant", content });
493
+ }
479
494
  await turn.reply.sendAnswer(msg.chatId, content);
480
495
  return { action: "reply", content };
481
496
  };
@@ -193,6 +193,12 @@ export interface WhatsAppInboundConfig {
193
193
  store: HistoryStore;
194
194
  /** Most recent turns to load per reply. @default 20 */
195
195
  limit?: number;
196
+ /**
197
+ * Excludes a sender from history entirely — see
198
+ * `InboundPipelineConfig.history.historyEnabledFor` in `./channels`.
199
+ * @default every sender is enabled
200
+ */
201
+ historyEnabledFor?: (sender: string) => boolean | Promise<boolean>;
196
202
  };
197
203
  now?: () => number;
198
204
  /** Logger for gate/dispatch diagnostics. Default: a console logger writing to stderr. */
@@ -1 +1 @@
1
- {"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/inbound.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/inbound.ts"],"names":[],"mappings":"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"}
@@ -852,6 +852,16 @@ async function resolvePersonaLayer(config, ctx) {
852
852
  return void 0;
853
853
  }
854
854
  }
855
+ async function isHistoryEnabled(config, sender) {
856
+ const predicate = config.history?.historyEnabledFor;
857
+ if (!predicate) return true;
858
+ try {
859
+ return await predicate(sender);
860
+ } catch (error) {
861
+ config.logger?.error("historyEnabledFor threw; disabling history for this turn", error);
862
+ return false;
863
+ }
864
+ }
855
865
  function createInboundPipeline(deps, config) {
856
866
  const mutedChats = /* @__PURE__ */ new Set();
857
867
  const now = config.now ?? Date.now;
@@ -891,12 +901,15 @@ function createInboundPipeline(deps, config) {
891
901
  }
892
902
  const personaLayer = await resolvePersonaLayer(config, { sender, text: msg.text });
893
903
  const buckets = await resolveBuckets({ mode, sender, bucketsFor: config.bucketsFor });
894
- const priorTurns = config.history ? await config.history.store.load(
904
+ const historyEnabled = config.history ? await isHistoryEnabled(config, sender) : false;
905
+ const priorTurns = config.history && historyEnabled ? await config.history.store.load(
895
906
  conversationId,
896
907
  config.history.limit ?? DEFAULT_HISTORY_LIMIT
897
908
  ) : [];
898
909
  const messages = [...priorTurns, { role: "user", content: msg.text }];
899
- await config.history?.store.append(conversationId, { role: "user", content: msg.text });
910
+ if (historyEnabled) {
911
+ await config.history?.store.append(conversationId, { role: "user", content: msg.text });
912
+ }
900
913
  const { system } = await prepareChat({
901
914
  store: deps.store,
902
915
  prompts: deps.prompts,
@@ -928,7 +941,9 @@ function createInboundPipeline(deps, config) {
928
941
  if (!content) {
929
942
  return { action: "ignore" };
930
943
  }
931
- await config.history?.store.append(conversationId, { role: "assistant", content });
944
+ if (historyEnabled) {
945
+ await config.history?.store.append(conversationId, { role: "assistant", content });
946
+ }
932
947
  await turn.reply.sendAnswer(msg.chatId, content);
933
948
  return { action: "reply", content };
934
949
  };
@@ -790,6 +790,16 @@ async function resolvePersonaLayer(config, ctx) {
790
790
  return void 0;
791
791
  }
792
792
  }
793
+ async function isHistoryEnabled(config, sender) {
794
+ const predicate = config.history?.historyEnabledFor;
795
+ if (!predicate) return true;
796
+ try {
797
+ return await predicate(sender);
798
+ } catch (error) {
799
+ config.logger?.error("historyEnabledFor threw; disabling history for this turn", error);
800
+ return false;
801
+ }
802
+ }
793
803
  function createInboundPipeline(deps, config) {
794
804
  const mutedChats = /* @__PURE__ */ new Set();
795
805
  const now = config.now ?? Date.now;
@@ -829,12 +839,15 @@ function createInboundPipeline(deps, config) {
829
839
  }
830
840
  const personaLayer = await resolvePersonaLayer(config, { sender, text: msg.text });
831
841
  const buckets = await resolveBuckets({ mode, sender, bucketsFor: config.bucketsFor });
832
- const priorTurns = config.history ? await config.history.store.load(
842
+ const historyEnabled = config.history ? await isHistoryEnabled(config, sender) : false;
843
+ const priorTurns = config.history && historyEnabled ? await config.history.store.load(
833
844
  conversationId,
834
845
  config.history.limit ?? DEFAULT_HISTORY_LIMIT
835
846
  ) : [];
836
847
  const messages = [...priorTurns, { role: "user", content: msg.text }];
837
- await config.history?.store.append(conversationId, { role: "user", content: msg.text });
848
+ if (historyEnabled) {
849
+ await config.history?.store.append(conversationId, { role: "user", content: msg.text });
850
+ }
838
851
  const { system } = await prepareChat({
839
852
  store: deps.store,
840
853
  prompts: deps.prompts,
@@ -866,7 +879,9 @@ function createInboundPipeline(deps, config) {
866
879
  if (!content) {
867
880
  return { action: "ignore" };
868
881
  }
869
- await config.history?.store.append(conversationId, { role: "assistant", content });
882
+ if (historyEnabled) {
883
+ await config.history?.store.append(conversationId, { role: "assistant", content });
884
+ }
870
885
  await turn.reply.sendAnswer(msg.chatId, content);
871
886
  return { action: "reply", content };
872
887
  };
@@ -12,6 +12,7 @@
12
12
  *
13
13
  * @packageDocumentation
14
14
  */
15
+ export type { TursoHistoryStoreOptions } from "./tursoStore";
15
16
  export { createTursoHistoryStore } from "./tursoStore";
16
17
  export type { HistoryMessage, HistoryStore } from "./types";
17
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/history/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/history/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,YAAY,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC"}
@@ -58,19 +58,32 @@ function ensureHistorySchema(client, tableName) {
58
58
  var VALID_TABLE_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
59
59
  var VALID_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
60
60
  var DEFAULT_MAX_PER_CONVERSATION = 200;
61
- function createTursoHistoryStore(client, tableName = "chat_history", maxPerConversation = DEFAULT_MAX_PER_CONVERSATION) {
61
+ function createTursoHistoryStore(client, tableName = "chat_history", maxPerConversation = DEFAULT_MAX_PER_CONVERSATION, options) {
62
62
  if (!VALID_TABLE_NAME.test(tableName)) {
63
63
  throw new Error(`Invalid history store table name: ${tableName}`);
64
64
  }
65
65
  if (!Number.isInteger(maxPerConversation) || maxPerConversation < 1) {
66
66
  throw new Error(`Invalid history store maxPerConversation: ${maxPerConversation}`);
67
67
  }
68
+ if (options?.ttlMs !== void 0 && (!Number.isInteger(options.ttlMs) || options.ttlMs < 1)) {
69
+ throw new Error(`Invalid history store ttlMs: ${options.ttlMs}`);
70
+ }
71
+ const ttlMs = options?.ttlMs;
72
+ const now = options?.now ?? Date.now;
73
+ async function pruneExpired(conversationId) {
74
+ if (ttlMs === void 0) return;
75
+ await client.execute({
76
+ sql: `DELETE FROM ${tableName} WHERE conversation_id = ? AND created_at < ?`,
77
+ args: [conversationId, now() - ttlMs]
78
+ });
79
+ }
68
80
  return {
69
81
  async append(conversationId, message) {
70
82
  await ensureHistorySchema(client, tableName);
83
+ await pruneExpired(conversationId);
71
84
  await client.execute({
72
85
  sql: `INSERT INTO ${tableName} (conversation_id, role, content, created_at) VALUES (?, ?, ?, ?)`,
73
- args: [conversationId, message.role, message.content, Date.now()]
86
+ args: [conversationId, message.role, message.content, now()]
74
87
  });
75
88
  await client.execute({
76
89
  sql: `DELETE FROM ${tableName} WHERE conversation_id = ? AND id NOT IN (
@@ -83,6 +96,7 @@ function createTursoHistoryStore(client, tableName = "chat_history", maxPerConve
83
96
  const boundedLimit = Math.max(0, Math.trunc(limit));
84
97
  if (boundedLimit === 0) return [];
85
98
  await ensureHistorySchema(client, tableName);
99
+ await pruneExpired(conversationId);
86
100
  const result = await client.execute({
87
101
  sql: `SELECT role, content FROM ${tableName} WHERE conversation_id = ? ORDER BY id DESC LIMIT ?`,
88
102
  args: [conversationId, boundedLimit]
@@ -91,6 +105,13 @@ function createTursoHistoryStore(client, tableName = "chat_history", maxPerConve
91
105
  role: String(row.role),
92
106
  content: String(row.content)
93
107
  })).filter((row) => VALID_ROLES.has(row.role)).reverse();
108
+ },
109
+ async clear(conversationId) {
110
+ await ensureHistorySchema(client, tableName);
111
+ await client.execute({
112
+ sql: `DELETE FROM ${tableName} WHERE conversation_id = ?`,
113
+ args: [conversationId]
114
+ });
94
115
  }
95
116
  };
96
117
  }
@@ -32,19 +32,32 @@ function ensureHistorySchema(client, tableName) {
32
32
  var VALID_TABLE_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
33
33
  var VALID_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
34
34
  var DEFAULT_MAX_PER_CONVERSATION = 200;
35
- function createTursoHistoryStore(client, tableName = "chat_history", maxPerConversation = DEFAULT_MAX_PER_CONVERSATION) {
35
+ function createTursoHistoryStore(client, tableName = "chat_history", maxPerConversation = DEFAULT_MAX_PER_CONVERSATION, options) {
36
36
  if (!VALID_TABLE_NAME.test(tableName)) {
37
37
  throw new Error(`Invalid history store table name: ${tableName}`);
38
38
  }
39
39
  if (!Number.isInteger(maxPerConversation) || maxPerConversation < 1) {
40
40
  throw new Error(`Invalid history store maxPerConversation: ${maxPerConversation}`);
41
41
  }
42
+ if (options?.ttlMs !== void 0 && (!Number.isInteger(options.ttlMs) || options.ttlMs < 1)) {
43
+ throw new Error(`Invalid history store ttlMs: ${options.ttlMs}`);
44
+ }
45
+ const ttlMs = options?.ttlMs;
46
+ const now = options?.now ?? Date.now;
47
+ async function pruneExpired(conversationId) {
48
+ if (ttlMs === void 0) return;
49
+ await client.execute({
50
+ sql: `DELETE FROM ${tableName} WHERE conversation_id = ? AND created_at < ?`,
51
+ args: [conversationId, now() - ttlMs]
52
+ });
53
+ }
42
54
  return {
43
55
  async append(conversationId, message) {
44
56
  await ensureHistorySchema(client, tableName);
57
+ await pruneExpired(conversationId);
45
58
  await client.execute({
46
59
  sql: `INSERT INTO ${tableName} (conversation_id, role, content, created_at) VALUES (?, ?, ?, ?)`,
47
- args: [conversationId, message.role, message.content, Date.now()]
60
+ args: [conversationId, message.role, message.content, now()]
48
61
  });
49
62
  await client.execute({
50
63
  sql: `DELETE FROM ${tableName} WHERE conversation_id = ? AND id NOT IN (
@@ -57,6 +70,7 @@ function createTursoHistoryStore(client, tableName = "chat_history", maxPerConve
57
70
  const boundedLimit = Math.max(0, Math.trunc(limit));
58
71
  if (boundedLimit === 0) return [];
59
72
  await ensureHistorySchema(client, tableName);
73
+ await pruneExpired(conversationId);
60
74
  const result = await client.execute({
61
75
  sql: `SELECT role, content FROM ${tableName} WHERE conversation_id = ? ORDER BY id DESC LIMIT ?`,
62
76
  args: [conversationId, boundedLimit]
@@ -65,6 +79,13 @@ function createTursoHistoryStore(client, tableName = "chat_history", maxPerConve
65
79
  role: String(row.role),
66
80
  content: String(row.content)
67
81
  })).filter((row) => VALID_ROLES.has(row.role)).reverse();
82
+ },
83
+ async clear(conversationId) {
84
+ await ensureHistorySchema(client, tableName);
85
+ await client.execute({
86
+ sql: `DELETE FROM ${tableName} WHERE conversation_id = ?`,
87
+ args: [conversationId]
88
+ });
68
89
  }
69
90
  };
70
91
  }
@@ -10,6 +10,18 @@
10
10
  */
11
11
  import type { Client } from "@libsql/client";
12
12
  import type { HistoryStore } from "./types";
13
+ export interface TursoHistoryStoreOptions {
14
+ /**
15
+ * Retention window: rows older than this are pruned whenever the
16
+ * conversation they belong to is accessed (`append` or `load`) — there is
17
+ * no background sweep. Unset: rows never expire on their own (only the
18
+ * `maxPerConversation` count cap and explicit `clear` remove them). Must be
19
+ * a positive integer, validated at construction like `maxPerConversation`.
20
+ */
21
+ ttlMs?: number;
22
+ /** Injectable clock for tests. @default Date.now */
23
+ now?: () => number;
24
+ }
13
25
  /**
14
26
  * Creates a history store keyed by an arbitrary conversation id (a WhatsApp
15
27
  * chat jid, a phone call sid, a web session id) on `tableName`.
@@ -22,5 +34,5 @@ import type { HistoryStore } from "./types";
22
34
  * or disable pruning entirely (SQLite reads a negative `LIMIT` as
23
35
  * unlimited), so it is validated at construction the same way `tableName` is.
24
36
  */
25
- export declare function createTursoHistoryStore(client: Client, tableName?: string, maxPerConversation?: number): HistoryStore;
37
+ export declare function createTursoHistoryStore(client: Client, tableName?: string, maxPerConversation?: number, options?: TursoHistoryStoreOptions): HistoryStore;
26
38
  //# sourceMappingURL=tursoStore.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tursoStore.d.ts","sourceRoot":"","sources":["../../src/history/tursoStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,SAAS,CAAC;AAkD5D;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,SAAS,SAAiB,EAC1B,kBAAkB,SAA+B,GAChD,YAAY,CAkDd"}
1
+ {"version":3,"file":"tursoStore.d.ts","sourceRoot":"","sources":["../../src/history/tursoStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,SAAS,CAAC;AAkD5D,MAAM,WAAW,wBAAwB;IACvC;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,SAAS,SAAiB,EAC1B,kBAAkB,SAA+B,EACjD,OAAO,CAAC,EAAE,wBAAwB,GACjC,YAAY,CAyEd"}
@@ -14,5 +14,7 @@ export interface HistoryStore {
14
14
  append(conversationId: string, message: HistoryMessage): Promise<void>;
15
15
  /** Load up to `limit` most recent turns for `conversationId`, oldest first. */
16
16
  load(conversationId: string, limit: number): Promise<HistoryMessage[]>;
17
+ /** Reset primitive: erases all of `conversationId`'s history. Other conversations are untouched. */
18
+ clear(conversationId: string): Promise<void>;
17
19
  }
18
20
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/history/types.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,+EAA+E;IAC/E,IAAI,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;CACxE"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/history/types.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,+EAA+E;IAC/E,IAAI,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IACvE,oGAAoG;IACpG,KAAK,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegoaltoworks/chatter",
3
- "version": "0.57.0",
3
+ "version": "0.58.0",
4
4
  "description": "Embeddable AI chatbot framework with RAG, authentication, and customizable widgets",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",