@diegoaltoworks/chatter 0.56.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.
Files changed (38) hide show
  1. package/dist/channels/index.js +46 -6
  2. package/dist/channels/index.mjs +46 -6
  3. package/dist/channels/pipeline.d.ts +13 -0
  4. package/dist/channels/pipeline.d.ts.map +1 -1
  5. package/dist/channels/telegram/channel.d.ts +12 -0
  6. package/dist/channels/telegram/channel.d.ts.map +1 -1
  7. package/dist/channels/telegram/index.js +48 -6
  8. package/dist/channels/telegram/index.mjs +48 -6
  9. package/dist/channels/whatsapp/inbound.d.ts +13 -0
  10. package/dist/channels/whatsapp/inbound.d.ts.map +1 -1
  11. package/dist/channels/whatsapp/index.js +48 -6
  12. package/dist/channels/whatsapp/index.mjs +48 -6
  13. package/dist/core/pipeline.d.ts +52 -1
  14. package/dist/core/pipeline.d.ts.map +1 -1
  15. package/dist/history/index.d.ts +1 -0
  16. package/dist/history/index.d.ts.map +1 -1
  17. package/dist/history/index.js +23 -2
  18. package/dist/history/index.mjs +23 -2
  19. package/dist/history/tursoStore.d.ts +13 -1
  20. package/dist/history/tursoStore.d.ts.map +1 -1
  21. package/dist/history/types.d.ts +2 -0
  22. package/dist/history/types.d.ts.map +1 -1
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +74 -9
  26. package/dist/index.mjs +74 -9
  27. package/dist/mcp-server.d.ts.map +1 -1
  28. package/dist/mcp-server.js +31 -4
  29. package/dist/mcp-server.mjs +31 -4
  30. package/dist/routes/demo.d.ts.map +1 -1
  31. package/dist/routes/openai.d.ts.map +1 -1
  32. package/dist/routes/private.d.ts.map +1 -1
  33. package/dist/routes/public.d.ts.map +1 -1
  34. package/dist/server.js +66 -7
  35. package/dist/server.mjs +66 -7
  36. package/dist/types.d.ts +25 -0
  37. package/dist/types.d.ts.map +1 -1
  38. package/package.json +1 -1
@@ -221,14 +221,35 @@ async function prepareChat({
221
221
  messages,
222
222
  personaLayer,
223
223
  channelHint,
224
- buckets
224
+ buckets,
225
+ sender,
226
+ rewriteQuery,
227
+ rerankContext,
228
+ logger
225
229
  }) {
226
230
  const lastUserMsg = lastUserMessage(messages);
227
231
  if (!lastUserMsg) {
228
232
  throw new Error("no user message found in conversation");
229
233
  }
230
234
  const { topK, label } = MODE_SETTINGS[mode];
231
- const ctx = await store.query(lastUserMsg.content, topK, buckets ?? defaultBuckets(mode));
235
+ let query = lastUserMsg.content;
236
+ if (rewriteQuery) {
237
+ try {
238
+ const rewritten = await rewriteQuery({ query, mode, sender });
239
+ if (typeof rewritten === "string" && rewritten.trim()) query = rewritten;
240
+ } catch (error) {
241
+ logger?.error("rewriteQuery threw; falling back to the original query", error);
242
+ }
243
+ }
244
+ let ctx = await store.query(query, topK, buckets ?? defaultBuckets(mode));
245
+ if (rerankContext) {
246
+ try {
247
+ const reranked = await rerankContext({ query, chunks: ctx });
248
+ if (Array.isArray(reranked) && reranked.every((c) => typeof c === "string")) ctx = reranked;
249
+ } catch (error) {
250
+ logger?.error("rerankContext threw; falling back to the original chunks", error);
251
+ }
252
+ }
232
253
  const persona = personaLayer?.trim() || (mode === "private" ? prompts.privatePersona : prompts.publicPersona);
233
254
  const hint = channelHint?.trim();
234
255
  const system = [
@@ -253,6 +274,16 @@ async function resolvePersonaLayer(config, ctx) {
253
274
  return void 0;
254
275
  }
255
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
+ }
256
287
  function createInboundPipeline(deps, config) {
257
288
  const mutedChats = /* @__PURE__ */ new Set();
258
289
  const now = config.now ?? Date.now;
@@ -292,12 +323,15 @@ function createInboundPipeline(deps, config) {
292
323
  }
293
324
  const personaLayer = await resolvePersonaLayer(config, { sender, text: msg.text });
294
325
  const buckets = await resolveBuckets({ mode, sender, bucketsFor: config.bucketsFor });
295
- 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(
296
328
  conversationId,
297
329
  config.history.limit ?? DEFAULT_HISTORY_LIMIT
298
330
  ) : [];
299
331
  const messages = [...priorTurns, { role: "user", content: msg.text }];
300
- 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
+ }
301
335
  const { system } = await prepareChat({
302
336
  store: deps.store,
303
337
  prompts: deps.prompts,
@@ -305,7 +339,11 @@ function createInboundPipeline(deps, config) {
305
339
  messages,
306
340
  personaLayer,
307
341
  channelHint: config.channelHint,
308
- buckets
342
+ buckets,
343
+ sender,
344
+ rewriteQuery: config.rewriteQuery,
345
+ rerankContext: config.rerankContext,
346
+ logger: config.logger
309
347
  });
310
348
  const { content: produced } = await answerOnce({
311
349
  answerFn: config.answerFn,
@@ -325,7 +363,9 @@ function createInboundPipeline(deps, config) {
325
363
  if (!content) {
326
364
  return { action: "ignore" };
327
365
  }
328
- await config.history?.store.append(conversationId, { role: "assistant", content });
366
+ if (historyEnabled) {
367
+ await config.history?.store.append(conversationId, { role: "assistant", content });
368
+ }
329
369
  await turn.reply.sendAnswer(msg.chatId, content);
330
370
  return { action: "reply", content };
331
371
  };
@@ -188,14 +188,35 @@ async function prepareChat({
188
188
  messages,
189
189
  personaLayer,
190
190
  channelHint,
191
- buckets
191
+ buckets,
192
+ sender,
193
+ rewriteQuery,
194
+ rerankContext,
195
+ logger
192
196
  }) {
193
197
  const lastUserMsg = lastUserMessage(messages);
194
198
  if (!lastUserMsg) {
195
199
  throw new Error("no user message found in conversation");
196
200
  }
197
201
  const { topK, label } = MODE_SETTINGS[mode];
198
- const ctx = await store.query(lastUserMsg.content, topK, buckets ?? defaultBuckets(mode));
202
+ let query = lastUserMsg.content;
203
+ if (rewriteQuery) {
204
+ try {
205
+ const rewritten = await rewriteQuery({ query, mode, sender });
206
+ if (typeof rewritten === "string" && rewritten.trim()) query = rewritten;
207
+ } catch (error) {
208
+ logger?.error("rewriteQuery threw; falling back to the original query", error);
209
+ }
210
+ }
211
+ let ctx = await store.query(query, topK, buckets ?? defaultBuckets(mode));
212
+ if (rerankContext) {
213
+ try {
214
+ const reranked = await rerankContext({ query, chunks: ctx });
215
+ if (Array.isArray(reranked) && reranked.every((c) => typeof c === "string")) ctx = reranked;
216
+ } catch (error) {
217
+ logger?.error("rerankContext threw; falling back to the original chunks", error);
218
+ }
219
+ }
199
220
  const persona = personaLayer?.trim() || (mode === "private" ? prompts.privatePersona : prompts.publicPersona);
200
221
  const hint = channelHint?.trim();
201
222
  const system = [
@@ -220,6 +241,16 @@ async function resolvePersonaLayer(config, ctx) {
220
241
  return void 0;
221
242
  }
222
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
+ }
223
254
  function createInboundPipeline(deps, config) {
224
255
  const mutedChats = /* @__PURE__ */ new Set();
225
256
  const now = config.now ?? Date.now;
@@ -259,12 +290,15 @@ function createInboundPipeline(deps, config) {
259
290
  }
260
291
  const personaLayer = await resolvePersonaLayer(config, { sender, text: msg.text });
261
292
  const buckets = await resolveBuckets({ mode, sender, bucketsFor: config.bucketsFor });
262
- 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(
263
295
  conversationId,
264
296
  config.history.limit ?? DEFAULT_HISTORY_LIMIT
265
297
  ) : [];
266
298
  const messages = [...priorTurns, { role: "user", content: msg.text }];
267
- 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
+ }
268
302
  const { system } = await prepareChat({
269
303
  store: deps.store,
270
304
  prompts: deps.prompts,
@@ -272,7 +306,11 @@ function createInboundPipeline(deps, config) {
272
306
  messages,
273
307
  personaLayer,
274
308
  channelHint: config.channelHint,
275
- buckets
309
+ buckets,
310
+ sender,
311
+ rewriteQuery: config.rewriteQuery,
312
+ rerankContext: config.rerankContext,
313
+ logger: config.logger
276
314
  });
277
315
  const { content: produced } = await answerOnce({
278
316
  answerFn: config.answerFn,
@@ -292,7 +330,9 @@ function createInboundPipeline(deps, config) {
292
330
  if (!content) {
293
331
  return { action: "ignore" };
294
332
  }
295
- await config.history?.store.append(conversationId, { role: "assistant", content });
333
+ if (historyEnabled) {
334
+ await config.history?.store.append(conversationId, { role: "assistant", content });
335
+ }
296
336
  await turn.reply.sendAnswer(msg.chatId, content);
297
337
  return { action: "reply", content };
298
338
  };
@@ -17,6 +17,7 @@ import type OpenAI from "openai";
17
17
  import type { AnswerFn, TransformReply } from "../core/answer";
18
18
  import type { BucketsFor } from "../core/buckets";
19
19
  import type { Logger } from "../core/logger";
20
+ import { type RerankContext, type RewriteQuery } from "../core/pipeline";
20
21
  import type { PromptLoader } from "../core/prompts";
21
22
  import type { VectorStore } from "../core/retrieval";
22
23
  import type { HistoryStore } from "../history/types";
@@ -41,6 +42,10 @@ export interface InboundPipelineConfig {
41
42
  channel?: string;
42
43
  answerFn?: AnswerFn;
43
44
  bucketsFor?: BucketsFor;
45
+ /** Rewrites the retrieval query before it reaches the vector store — see `ChatterConfig.rewriteQuery`. */
46
+ rewriteQuery?: RewriteQuery;
47
+ /** Post-processes retrieved chunks before they're folded into the prompt — see `ChatterConfig.rerankContext`. */
48
+ rerankContext?: RerankContext;
44
49
  /** Modifies or vetoes the produced reply before delivery — see `ChatterConfig.transformReply`. */
45
50
  transformReply?: TransformReply;
46
51
  model?: string;
@@ -56,6 +61,14 @@ export interface InboundPipelineConfig {
56
61
  store: HistoryStore;
57
62
  /** Most recent turns to load per reply. @default 20 */
58
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>;
59
72
  };
60
73
  /** Group chats eligible for a reply. Empty (default) = every group. Has no effect on DMs, which always reply. */
61
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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"}
@@ -20,6 +20,7 @@
20
20
  import type { AnswerFn, TransformReply } from "../../core/answer";
21
21
  import type { BucketsFor } from "../../core/buckets";
22
22
  import { type Logger } from "../../core/logger";
23
+ import type { RerankContext, RewriteQuery } from "../../core/pipeline";
23
24
  import type { HistoryStore } from "../../history/types";
24
25
  import type { Channel } from "../index";
25
26
  import { type TelegramApi } from "./api";
@@ -32,6 +33,10 @@ export interface TelegramChannelConfig {
32
33
  allowedChats?: string[];
33
34
  answerFn?: AnswerFn;
34
35
  bucketsFor?: BucketsFor;
36
+ /** Rewrites the retrieval query before it reaches the vector store — see `ChatterConfig.rewriteQuery`. Falls back to the server's own. */
37
+ rewriteQuery?: RewriteQuery;
38
+ /** Post-processes retrieved chunks before they're folded into the prompt — see `ChatterConfig.rerankContext`. Falls back to the server's own. */
39
+ rerankContext?: RerankContext;
35
40
  /** Modifies or vetoes the produced reply before delivery — see `ChatterConfig.transformReply`. Falls back to the server's own. */
36
41
  transformReply?: TransformReply;
37
42
  model?: string;
@@ -45,7 +50,14 @@ export interface TelegramChannelConfig {
45
50
  /** Off by default — the channel stays single-turn until a store is configured. */
46
51
  history?: {
47
52
  store: HistoryStore;
53
+ /** Most recent turns to load per reply. @default 20 */
48
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>;
49
61
  };
50
62
  muteRegex?: RegExp;
51
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,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,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,CAkI5E"}
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"}
@@ -388,14 +388,35 @@ async function prepareChat({
388
388
  messages,
389
389
  personaLayer,
390
390
  channelHint,
391
- buckets
391
+ buckets,
392
+ sender,
393
+ rewriteQuery,
394
+ rerankContext,
395
+ logger
392
396
  }) {
393
397
  const lastUserMsg = lastUserMessage(messages);
394
398
  if (!lastUserMsg) {
395
399
  throw new Error("no user message found in conversation");
396
400
  }
397
401
  const { topK, label } = MODE_SETTINGS[mode];
398
- const ctx = await store.query(lastUserMsg.content, topK, buckets ?? defaultBuckets(mode));
402
+ let query = lastUserMsg.content;
403
+ if (rewriteQuery) {
404
+ try {
405
+ const rewritten = await rewriteQuery({ query, mode, sender });
406
+ if (typeof rewritten === "string" && rewritten.trim()) query = rewritten;
407
+ } catch (error) {
408
+ logger?.error("rewriteQuery threw; falling back to the original query", error);
409
+ }
410
+ }
411
+ let ctx = await store.query(query, topK, buckets ?? defaultBuckets(mode));
412
+ if (rerankContext) {
413
+ try {
414
+ const reranked = await rerankContext({ query, chunks: ctx });
415
+ if (Array.isArray(reranked) && reranked.every((c) => typeof c === "string")) ctx = reranked;
416
+ } catch (error) {
417
+ logger?.error("rerankContext threw; falling back to the original chunks", error);
418
+ }
419
+ }
399
420
  const persona = personaLayer?.trim() || (mode === "private" ? prompts.privatePersona : prompts.publicPersona);
400
421
  const hint = channelHint?.trim();
401
422
  const system = [
@@ -420,6 +441,16 @@ async function resolvePersonaLayer(config, ctx) {
420
441
  return void 0;
421
442
  }
422
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
+ }
423
454
  function createInboundPipeline(deps, config) {
424
455
  const mutedChats = /* @__PURE__ */ new Set();
425
456
  const now = config.now ?? Date.now;
@@ -459,12 +490,15 @@ function createInboundPipeline(deps, config) {
459
490
  }
460
491
  const personaLayer = await resolvePersonaLayer(config, { sender, text: msg.text });
461
492
  const buckets = await resolveBuckets({ mode, sender, bucketsFor: config.bucketsFor });
462
- 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(
463
495
  conversationId,
464
496
  config.history.limit ?? DEFAULT_HISTORY_LIMIT
465
497
  ) : [];
466
498
  const messages = [...priorTurns, { role: "user", content: msg.text }];
467
- 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
+ }
468
502
  const { system } = await prepareChat({
469
503
  store: deps.store,
470
504
  prompts: deps.prompts,
@@ -472,7 +506,11 @@ function createInboundPipeline(deps, config) {
472
506
  messages,
473
507
  personaLayer,
474
508
  channelHint: config.channelHint,
475
- buckets
509
+ buckets,
510
+ sender,
511
+ rewriteQuery: config.rewriteQuery,
512
+ rerankContext: config.rerankContext,
513
+ logger: config.logger
476
514
  });
477
515
  const { content: produced } = await answerOnce({
478
516
  answerFn: config.answerFn,
@@ -492,7 +530,9 @@ function createInboundPipeline(deps, config) {
492
530
  if (!content) {
493
531
  return { action: "ignore" };
494
532
  }
495
- await config.history?.store.append(conversationId, { role: "assistant", content });
533
+ if (historyEnabled) {
534
+ await config.history?.store.append(conversationId, { role: "assistant", content });
535
+ }
496
536
  await turn.reply.sendAnswer(msg.chatId, content);
497
537
  return { action: "reply", content };
498
538
  };
@@ -625,6 +665,8 @@ function createTelegramChannel(config) {
625
665
  channel: channelName,
626
666
  answerFn: config.answerFn ?? deps.config.answerFn,
627
667
  bucketsFor: config.bucketsFor ?? deps.config.bucketsFor,
668
+ rewriteQuery: config.rewriteQuery ?? deps.config.rewriteQuery,
669
+ rerankContext: config.rerankContext ?? deps.config.rerankContext,
628
670
  transformReply: config.transformReply ?? deps.config.transformReply,
629
671
  model: config.model,
630
672
  channelHint: config.channelHint ?? "Channel: Telegram.",
@@ -346,14 +346,35 @@ async function prepareChat({
346
346
  messages,
347
347
  personaLayer,
348
348
  channelHint,
349
- buckets
349
+ buckets,
350
+ sender,
351
+ rewriteQuery,
352
+ rerankContext,
353
+ logger
350
354
  }) {
351
355
  const lastUserMsg = lastUserMessage(messages);
352
356
  if (!lastUserMsg) {
353
357
  throw new Error("no user message found in conversation");
354
358
  }
355
359
  const { topK, label } = MODE_SETTINGS[mode];
356
- const ctx = await store.query(lastUserMsg.content, topK, buckets ?? defaultBuckets(mode));
360
+ let query = lastUserMsg.content;
361
+ if (rewriteQuery) {
362
+ try {
363
+ const rewritten = await rewriteQuery({ query, mode, sender });
364
+ if (typeof rewritten === "string" && rewritten.trim()) query = rewritten;
365
+ } catch (error) {
366
+ logger?.error("rewriteQuery threw; falling back to the original query", error);
367
+ }
368
+ }
369
+ let ctx = await store.query(query, topK, buckets ?? defaultBuckets(mode));
370
+ if (rerankContext) {
371
+ try {
372
+ const reranked = await rerankContext({ query, chunks: ctx });
373
+ if (Array.isArray(reranked) && reranked.every((c) => typeof c === "string")) ctx = reranked;
374
+ } catch (error) {
375
+ logger?.error("rerankContext threw; falling back to the original chunks", error);
376
+ }
377
+ }
357
378
  const persona = personaLayer?.trim() || (mode === "private" ? prompts.privatePersona : prompts.publicPersona);
358
379
  const hint = channelHint?.trim();
359
380
  const system = [
@@ -378,6 +399,16 @@ async function resolvePersonaLayer(config, ctx) {
378
399
  return void 0;
379
400
  }
380
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
+ }
381
412
  function createInboundPipeline(deps, config) {
382
413
  const mutedChats = /* @__PURE__ */ new Set();
383
414
  const now = config.now ?? Date.now;
@@ -417,12 +448,15 @@ function createInboundPipeline(deps, config) {
417
448
  }
418
449
  const personaLayer = await resolvePersonaLayer(config, { sender, text: msg.text });
419
450
  const buckets = await resolveBuckets({ mode, sender, bucketsFor: config.bucketsFor });
420
- 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(
421
453
  conversationId,
422
454
  config.history.limit ?? DEFAULT_HISTORY_LIMIT
423
455
  ) : [];
424
456
  const messages = [...priorTurns, { role: "user", content: msg.text }];
425
- 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
+ }
426
460
  const { system } = await prepareChat({
427
461
  store: deps.store,
428
462
  prompts: deps.prompts,
@@ -430,7 +464,11 @@ function createInboundPipeline(deps, config) {
430
464
  messages,
431
465
  personaLayer,
432
466
  channelHint: config.channelHint,
433
- buckets
467
+ buckets,
468
+ sender,
469
+ rewriteQuery: config.rewriteQuery,
470
+ rerankContext: config.rerankContext,
471
+ logger: config.logger
434
472
  });
435
473
  const { content: produced } = await answerOnce({
436
474
  answerFn: config.answerFn,
@@ -450,7 +488,9 @@ function createInboundPipeline(deps, config) {
450
488
  if (!content) {
451
489
  return { action: "ignore" };
452
490
  }
453
- await config.history?.store.append(conversationId, { role: "assistant", content });
491
+ if (historyEnabled) {
492
+ await config.history?.store.append(conversationId, { role: "assistant", content });
493
+ }
454
494
  await turn.reply.sendAnswer(msg.chatId, content);
455
495
  return { action: "reply", content };
456
496
  };
@@ -583,6 +623,8 @@ function createTelegramChannel(config) {
583
623
  channel: channelName,
584
624
  answerFn: config.answerFn ?? deps.config.answerFn,
585
625
  bucketsFor: config.bucketsFor ?? deps.config.bucketsFor,
626
+ rewriteQuery: config.rewriteQuery ?? deps.config.rewriteQuery,
627
+ rerankContext: config.rerankContext ?? deps.config.rerankContext,
586
628
  transformReply: config.transformReply ?? deps.config.transformReply,
587
629
  model: config.model,
588
630
  channelHint: config.channelHint ?? "Channel: Telegram.",
@@ -36,6 +36,8 @@
36
36
  * prompts: deps.prompts,
37
37
  * answerFn: deps.config.answerFn,
38
38
  * bucketsFor: deps.config.bucketsFor,
39
+ * rewriteQuery: deps.config.rewriteQuery,
40
+ * rerankContext: deps.config.rerankContext,
39
41
  * transformReply: deps.config.transformReply,
40
42
  * registry: new Map(),
41
43
  * logger: deps.logger,
@@ -49,6 +51,7 @@ import type OpenAI from "openai";
49
51
  import type { AnswerFn, TransformReply } from "../../core/answer";
50
52
  import type { BucketsFor } from "../../core/buckets";
51
53
  import { type Logger } from "../../core/logger";
54
+ import type { RerankContext, RewriteQuery } from "../../core/pipeline";
52
55
  import type { PromptLoader } from "../../core/prompts";
53
56
  import type { VectorStore } from "../../core/retrieval";
54
57
  import type { HistoryStore } from "../../history/types";
@@ -129,6 +132,10 @@ export interface WhatsAppInboundConfig {
129
132
  prompts: PromptLoader;
130
133
  answerFn?: AnswerFn;
131
134
  bucketsFor?: BucketsFor;
135
+ /** Rewrites the retrieval query before it reaches the vector store — see `ChatterConfig.rewriteQuery`. */
136
+ rewriteQuery?: RewriteQuery;
137
+ /** Post-processes retrieved chunks before they're folded into the prompt — see `ChatterConfig.rerankContext`. */
138
+ rerankContext?: RerankContext;
132
139
  /** Modifies or vetoes the produced reply before delivery — see `ChatterConfig.transformReply`. */
133
140
  transformReply?: TransformReply;
134
141
  model?: string;
@@ -186,6 +193,12 @@ export interface WhatsAppInboundConfig {
186
193
  store: HistoryStore;
187
194
  /** Most recent turns to load per reply. @default 20 */
188
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>;
189
202
  };
190
203
  now?: () => number;
191
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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"}
@@ -799,14 +799,35 @@ async function prepareChat({
799
799
  messages,
800
800
  personaLayer,
801
801
  channelHint,
802
- buckets
802
+ buckets,
803
+ sender,
804
+ rewriteQuery,
805
+ rerankContext,
806
+ logger
803
807
  }) {
804
808
  const lastUserMsg = lastUserMessage(messages);
805
809
  if (!lastUserMsg) {
806
810
  throw new Error("no user message found in conversation");
807
811
  }
808
812
  const { topK, label } = MODE_SETTINGS[mode];
809
- const ctx = await store.query(lastUserMsg.content, topK, buckets ?? defaultBuckets(mode));
813
+ let query = lastUserMsg.content;
814
+ if (rewriteQuery) {
815
+ try {
816
+ const rewritten = await rewriteQuery({ query, mode, sender });
817
+ if (typeof rewritten === "string" && rewritten.trim()) query = rewritten;
818
+ } catch (error) {
819
+ logger?.error("rewriteQuery threw; falling back to the original query", error);
820
+ }
821
+ }
822
+ let ctx = await store.query(query, topK, buckets ?? defaultBuckets(mode));
823
+ if (rerankContext) {
824
+ try {
825
+ const reranked = await rerankContext({ query, chunks: ctx });
826
+ if (Array.isArray(reranked) && reranked.every((c) => typeof c === "string")) ctx = reranked;
827
+ } catch (error) {
828
+ logger?.error("rerankContext threw; falling back to the original chunks", error);
829
+ }
830
+ }
810
831
  const persona = personaLayer?.trim() || (mode === "private" ? prompts.privatePersona : prompts.publicPersona);
811
832
  const hint = channelHint?.trim();
812
833
  const system = [
@@ -831,6 +852,16 @@ async function resolvePersonaLayer(config, ctx) {
831
852
  return void 0;
832
853
  }
833
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
+ }
834
865
  function createInboundPipeline(deps, config) {
835
866
  const mutedChats = /* @__PURE__ */ new Set();
836
867
  const now = config.now ?? Date.now;
@@ -870,12 +901,15 @@ function createInboundPipeline(deps, config) {
870
901
  }
871
902
  const personaLayer = await resolvePersonaLayer(config, { sender, text: msg.text });
872
903
  const buckets = await resolveBuckets({ mode, sender, bucketsFor: config.bucketsFor });
873
- 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(
874
906
  conversationId,
875
907
  config.history.limit ?? DEFAULT_HISTORY_LIMIT
876
908
  ) : [];
877
909
  const messages = [...priorTurns, { role: "user", content: msg.text }];
878
- 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
+ }
879
913
  const { system } = await prepareChat({
880
914
  store: deps.store,
881
915
  prompts: deps.prompts,
@@ -883,7 +917,11 @@ function createInboundPipeline(deps, config) {
883
917
  messages,
884
918
  personaLayer,
885
919
  channelHint: config.channelHint,
886
- buckets
920
+ buckets,
921
+ sender,
922
+ rewriteQuery: config.rewriteQuery,
923
+ rerankContext: config.rerankContext,
924
+ logger: config.logger
887
925
  });
888
926
  const { content: produced } = await answerOnce({
889
927
  answerFn: config.answerFn,
@@ -903,7 +941,9 @@ function createInboundPipeline(deps, config) {
903
941
  if (!content) {
904
942
  return { action: "ignore" };
905
943
  }
906
- await config.history?.store.append(conversationId, { role: "assistant", content });
944
+ if (historyEnabled) {
945
+ await config.history?.store.append(conversationId, { role: "assistant", content });
946
+ }
907
947
  await turn.reply.sendAnswer(msg.chatId, content);
908
948
  return { action: "reply", content };
909
949
  };
@@ -1010,6 +1050,8 @@ function createWhatsAppInboundHandler(config) {
1010
1050
  channel: "whatsapp",
1011
1051
  answerFn: config.answerFn,
1012
1052
  bucketsFor: config.bucketsFor,
1053
+ rewriteQuery: config.rewriteQuery,
1054
+ rerankContext: config.rerankContext,
1013
1055
  transformReply: config.transformReply,
1014
1056
  model: config.model,
1015
1057
  channelHint: config.channelHint,