@diegoaltoworks/chatter 0.57.0 → 0.59.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.
@@ -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.59.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",