@diegoaltoworks/chatter 0.61.0 → 0.62.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 (125) hide show
  1. package/README.md +55 -17
  2. package/dist/auth/secretStrength.d.ts +1 -1
  3. package/dist/auth/secretStrength.d.ts.map +1 -1
  4. package/dist/bin/create-apikey.js +45 -28
  5. package/dist/bin/wa-pair.js +113 -46
  6. package/dist/channels/backoff.d.ts +10 -0
  7. package/dist/channels/backoff.d.ts.map +1 -0
  8. package/dist/channels/gates.d.ts +15 -8
  9. package/dist/channels/gates.d.ts.map +1 -1
  10. package/dist/channels/index.d.ts +1 -1
  11. package/dist/channels/index.d.ts.map +1 -1
  12. package/dist/channels/index.js +38 -5
  13. package/dist/channels/index.mjs +37 -5
  14. package/dist/channels/matrix/api.d.ts +25 -6
  15. package/dist/channels/matrix/api.d.ts.map +1 -1
  16. package/dist/channels/matrix/channel.d.ts +4 -56
  17. package/dist/channels/matrix/channel.d.ts.map +1 -1
  18. package/dist/channels/matrix/handler.d.ts +16 -9
  19. package/dist/channels/matrix/handler.d.ts.map +1 -1
  20. package/dist/channels/matrix/index.d.ts +2 -2
  21. package/dist/channels/matrix/index.d.ts.map +1 -1
  22. package/dist/channels/matrix/index.js +196 -40
  23. package/dist/channels/matrix/index.mjs +189 -39
  24. package/dist/channels/matrix/sync.d.ts +4 -1
  25. package/dist/channels/matrix/sync.d.ts.map +1 -1
  26. package/dist/channels/matrix/updates.d.ts +39 -10
  27. package/dist/channels/matrix/updates.d.ts.map +1 -1
  28. package/dist/channels/pipeline.d.ts +14 -14
  29. package/dist/channels/pipeline.d.ts.map +1 -1
  30. package/dist/channels/polling.d.ts +67 -0
  31. package/dist/channels/polling.d.ts.map +1 -0
  32. package/dist/channels/telegram/channel.d.ts +2 -56
  33. package/dist/channels/telegram/channel.d.ts.map +1 -1
  34. package/dist/channels/telegram/index.js +53 -23
  35. package/dist/channels/telegram/index.mjs +53 -23
  36. package/dist/channels/telegram/poll.d.ts +5 -1
  37. package/dist/channels/telegram/poll.d.ts.map +1 -1
  38. package/dist/channels/telegram/webhook.d.ts +2 -10
  39. package/dist/channels/telegram/webhook.d.ts.map +1 -1
  40. package/dist/channels/whatsapp/authState.d.ts +40 -9
  41. package/dist/channels/whatsapp/authState.d.ts.map +1 -1
  42. package/dist/channels/whatsapp/channel.d.ts +25 -4
  43. package/dist/channels/whatsapp/channel.d.ts.map +1 -1
  44. package/dist/channels/whatsapp/inbound.d.ts +23 -25
  45. package/dist/channels/whatsapp/inbound.d.ts.map +1 -1
  46. package/dist/channels/whatsapp/index.d.ts +3 -3
  47. package/dist/channels/whatsapp/index.d.ts.map +1 -1
  48. package/dist/channels/whatsapp/index.js +123 -41
  49. package/dist/channels/whatsapp/index.mjs +120 -41
  50. package/dist/channels/whatsapp/pairing.d.ts.map +1 -1
  51. package/dist/client/src/Chat.d.ts.map +1 -1
  52. package/dist/client/src/ChatButton.d.ts.map +1 -1
  53. package/dist/client/src/browser.d.ts.map +1 -1
  54. package/dist/core/answer.d.ts +8 -0
  55. package/dist/core/answer.d.ts.map +1 -1
  56. package/dist/core/buildLock.d.ts +52 -0
  57. package/dist/core/buildLock.d.ts.map +1 -0
  58. package/dist/core/pipeline.d.ts +42 -5
  59. package/dist/core/pipeline.d.ts.map +1 -1
  60. package/dist/core/prompts.d.ts +2 -0
  61. package/dist/core/prompts.d.ts.map +1 -1
  62. package/dist/core/retrieval.d.ts +100 -25
  63. package/dist/core/retrieval.d.ts.map +1 -1
  64. package/dist/core/session.d.ts +8 -0
  65. package/dist/core/session.d.ts.map +1 -1
  66. package/dist/flows/index.d.ts +1 -1
  67. package/dist/flows/index.d.ts.map +1 -1
  68. package/dist/flows/index.js +8 -6
  69. package/dist/flows/index.mjs +7 -6
  70. package/dist/flows/intent.d.ts +1 -1
  71. package/dist/flows/intent.d.ts.map +1 -1
  72. package/dist/flows/loader.d.ts +1 -2
  73. package/dist/flows/loader.d.ts.map +1 -1
  74. package/dist/flows/params.d.ts +1 -1
  75. package/dist/flows/params.d.ts.map +1 -1
  76. package/dist/flows/session.d.ts +4 -2
  77. package/dist/flows/session.d.ts.map +1 -1
  78. package/dist/flows/types.d.ts +2 -3
  79. package/dist/flows/types.d.ts.map +1 -1
  80. package/dist/history/index.d.ts +1 -1
  81. package/dist/history/index.d.ts.map +1 -1
  82. package/dist/history/index.js +6 -3
  83. package/dist/history/index.mjs +5 -3
  84. package/dist/history/tursoStore.d.ts +6 -4
  85. package/dist/history/tursoStore.d.ts.map +1 -1
  86. package/dist/images/index.js +2 -2
  87. package/dist/images/index.mjs +2 -2
  88. package/dist/index.d.ts +7 -5
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +596 -295
  91. package/dist/index.mjs +595 -295
  92. package/dist/mcp-server/index.d.ts +5 -5
  93. package/dist/mcp-server/index.d.ts.map +1 -1
  94. package/dist/mcp-server/logger.d.ts +12 -2
  95. package/dist/mcp-server/logger.d.ts.map +1 -1
  96. package/dist/mcp-server/rate-limiter.d.ts +10 -3
  97. package/dist/mcp-server/rate-limiter.d.ts.map +1 -1
  98. package/dist/mcp-server/types.d.ts +20 -5
  99. package/dist/mcp-server/types.d.ts.map +1 -1
  100. package/dist/mcp-server.js +479 -244
  101. package/dist/mcp-server.mjs +485 -244
  102. package/dist/middleware/apiKey.d.ts +10 -0
  103. package/dist/middleware/apiKey.d.ts.map +1 -0
  104. package/dist/middleware/auth.d.ts.map +1 -1
  105. package/dist/middleware/cors.d.ts.map +1 -1
  106. package/dist/middleware/ratelimit.d.ts.map +1 -1
  107. package/dist/middleware/referrer.d.ts.map +1 -1
  108. package/dist/middleware/session.d.ts +1 -1
  109. package/dist/middleware/session.d.ts.map +1 -1
  110. package/dist/routes/demo.d.ts.map +1 -1
  111. package/dist/routes/openai.d.ts.map +1 -1
  112. package/dist/routes/private.d.ts.map +1 -1
  113. package/dist/routes/public.d.ts.map +1 -1
  114. package/dist/scheduler/index.js +1 -1
  115. package/dist/scheduler/index.mjs +1 -1
  116. package/dist/scheduler/scheduler.d.ts +9 -1
  117. package/dist/scheduler/scheduler.d.ts.map +1 -1
  118. package/dist/server.d.ts.map +1 -1
  119. package/dist/server.js +491 -268
  120. package/dist/server.mjs +497 -268
  121. package/dist/shortener.d.ts +5 -4
  122. package/dist/shortener.d.ts.map +1 -1
  123. package/dist/types.d.ts +129 -75
  124. package/dist/types.d.ts.map +1 -1
  125. package/package.json +4 -1
@@ -67,27 +67,55 @@ function ensureAuthSchema(client) {
67
67
  }
68
68
  return ready;
69
69
  }
70
- async function useTursoAuthState(db, secret, sessionId, runtime) {
71
- await ensureAuthSchema(db);
70
+ function createTursoWaAuthKV(db) {
71
+ return {
72
+ async read(id) {
73
+ await ensureAuthSchema(db);
74
+ const result = await db.execute({
75
+ sql: "SELECT value FROM wa_auth WHERE id = ?",
76
+ args: [id]
77
+ });
78
+ const row = result.rows[0];
79
+ return row ? row.value : null;
80
+ },
81
+ async write(id, value) {
82
+ await ensureAuthSchema(db);
83
+ await db.execute({
84
+ sql: "INSERT OR REPLACE INTO wa_auth (id, value) VALUES (?, ?)",
85
+ args: [id, value]
86
+ });
87
+ },
88
+ async remove(id) {
89
+ await ensureAuthSchema(db);
90
+ await db.execute({ sql: "DELETE FROM wa_auth WHERE id = ?", args: [id] });
91
+ },
92
+ // Mirrors isAuthRowForSession - keep the two in sync.
93
+ async clear(sessionId) {
94
+ await ensureAuthSchema(db);
95
+ if (sessionId === "default") {
96
+ await db.execute("DELETE FROM wa_auth WHERE id NOT LIKE 's:%'");
97
+ } else {
98
+ await db.execute({
99
+ sql: "DELETE FROM wa_auth WHERE id LIKE ?",
100
+ args: [`s:${sessionId}/%`]
101
+ });
102
+ }
103
+ }
104
+ };
105
+ }
106
+ async function useAuthState(store, secret, sessionId, runtime) {
72
107
  const rowId = (id) => sessionId === "default" ? id : `s:${sessionId}/${id}`;
73
108
  async function read(id) {
74
- const result = await db.execute({
75
- sql: "SELECT value FROM wa_auth WHERE id = ?",
76
- args: [rowId(id)]
77
- });
78
- const row = result.rows[0];
79
- if (!row) return null;
80
- return JSON.parse(await decrypt(row.value, secret), runtime.bufferJSON.reviver);
109
+ const value = await store.read(rowId(id));
110
+ if (value === null) return null;
111
+ return JSON.parse(await decrypt(value, secret), runtime.bufferJSON.reviver);
81
112
  }
82
113
  async function write(id, value) {
83
114
  const payload = await encrypt(JSON.stringify(value, runtime.bufferJSON.replacer), secret);
84
- await db.execute({
85
- sql: "INSERT OR REPLACE INTO wa_auth (id, value) VALUES (?, ?)",
86
- args: [rowId(id), payload]
87
- });
115
+ await store.write(rowId(id), payload);
88
116
  }
89
117
  async function remove(id) {
90
- await db.execute({ sql: "DELETE FROM wa_auth WHERE id = ?", args: [rowId(id)] });
118
+ await store.remove(rowId(id));
91
119
  }
92
120
  const creds = await read("creds") ?? runtime.initAuthCreds();
93
121
  return {
@@ -124,18 +152,13 @@ async function useTursoAuthState(db, secret, sessionId, runtime) {
124
152
  }
125
153
  },
126
154
  saveCreds: () => write("creds", creds),
127
- clear: async () => {
128
- if (sessionId === "default") {
129
- await db.execute("DELETE FROM wa_auth WHERE id NOT LIKE 's:%'");
130
- } else {
131
- await db.execute({
132
- sql: "DELETE FROM wa_auth WHERE id LIKE ?",
133
- args: [`s:${sessionId}/%`]
134
- });
135
- }
136
- }
155
+ // Scoped to THIS session - never nuke a sibling number's pairing.
156
+ clear: () => store.clear(sessionId)
137
157
  };
138
158
  }
159
+ async function useTursoAuthState(db, secret, sessionId, runtime) {
160
+ return useAuthState(createTursoWaAuthKV(db), secret, sessionId, runtime);
161
+ }
139
162
 
140
163
  // src/channels/whatsapp/baileys.ts
141
164
  function wrapMissingBaileysError(cause) {
@@ -198,6 +221,11 @@ function createConsoleLogger(level = "info") {
198
221
  };
199
222
  }
200
223
 
224
+ // src/channels/backoff.ts
225
+ function exponentialBackoffMs(baseMs, maxMs, n) {
226
+ return Math.min(baseMs * 2 ** Math.max(0, Math.min(n, 20)), maxMs);
227
+ }
228
+
201
229
  // src/channels/whatsapp/lease.ts
202
230
  var LEASE_STALE_MS = 9e4;
203
231
  var LEASE_HEARTBEAT_MS = 3e4;
@@ -256,7 +284,7 @@ function createTursoWaLeaseStore(client) {
256
284
  var RECONNECT_DELAY_MS = 5e3;
257
285
  var RECONNECT_MAX_DELAY_MS = 10 * 60 * 1e3;
258
286
  function reconnectDelayMs(failures) {
259
- return Math.min(RECONNECT_DELAY_MS * 2 ** Math.min(failures, 20), RECONNECT_MAX_DELAY_MS);
287
+ return exponentialBackoffMs(RECONNECT_DELAY_MS, RECONNECT_MAX_DELAY_MS, failures);
260
288
  }
261
289
  async function shutdownWaSessions(sessions) {
262
290
  await Promise.all(
@@ -327,9 +355,18 @@ async function acquireSessionLease(sessionId, deps) {
327
355
  function senderNameFor(channelName, sessionId) {
328
356
  return sessionId === "default" ? channelName : `${channelName}:${sessionId}`;
329
357
  }
358
+ function normalizeWaMediaPayload(payload) {
359
+ const media = typeof payload === "string" ? { url: payload } : payload;
360
+ if (!media || typeof media !== "object" || typeof media.url !== "string" || !media.url) {
361
+ throw new TypeError(
362
+ `WhatsApp sendMedia payload must be a URL string or { url, caption? }, got: ${typeof payload}`
363
+ );
364
+ }
365
+ return media;
366
+ }
330
367
  function createWhatsAppChannel(config) {
331
368
  assertStrongSecret(config.sessionSecret, "WhatsApp sessionSecret (e.g. WA_SESSION_SECRET)");
332
- const channelName = "whatsapp";
369
+ const channelName = config.name ?? "whatsapp";
333
370
  const sessionIds = config.sessionIds && config.sessionIds.length > 0 ? config.sessionIds : ["default"];
334
371
  const now = config.now ?? Date.now;
335
372
  const schedule = config.schedule ?? ((fn, ms) => void setTimeout(fn, ms));
@@ -347,8 +384,13 @@ function createWhatsAppChannel(config) {
347
384
  name: channelName,
348
385
  async start(deps) {
349
386
  const log = config.logger ?? deps.logger ?? createConsoleLogger();
350
- const db = deps.db;
351
- const leaseStore = createTursoWaLeaseStore(db);
387
+ if ((!config.leaseStore || !config.authStore) && !deps.db) {
388
+ throw new Error(
389
+ "The WhatsApp channel needs a libsql client for auth state and the session lease: set config.database (config.retriever alone does not open one), or supply both config.leaseStore and config.authStore."
390
+ );
391
+ }
392
+ const leaseStore = config.leaseStore ?? createTursoWaLeaseStore(deps.db);
393
+ const authStore = config.authStore ?? createTursoWaAuthKV(deps.db);
352
394
  senders = deps.senders;
353
395
  await loadBaileys2();
354
396
  async function connect(sessionId) {
@@ -359,8 +401,8 @@ function createWhatsAppChannel(config) {
359
401
  initAuthCreds: baileys.initAuthCreds,
360
402
  appStateSyncKeyFromObject: (value) => baileys.proto.Message.AppStateSyncKeyData.fromObject(value)
361
403
  };
362
- const { state, saveCreds } = await useTursoAuthState(
363
- db,
404
+ const { state, saveCreds } = await useAuthState(
405
+ authStore,
364
406
  config.sessionSecret,
365
407
  sessionId,
366
408
  runtime
@@ -404,6 +446,13 @@ function createWhatsAppChannel(config) {
404
446
  sendText: async (chatId, text) => {
405
447
  await sock.sendMessage(chatId, { text });
406
448
  },
449
+ sendMedia: async (chatId, payload) => {
450
+ const { url, caption } = normalizeWaMediaPayload(payload);
451
+ await sock.sendMessage(
452
+ chatId,
453
+ caption !== void 0 ? { image: { url }, caption } : { image: { url } }
454
+ );
455
+ },
407
456
  sendReaction: async (chatId, messageRef, emoji) => {
408
457
  await sock.sendMessage(chatId, {
409
458
  react: { text: emoji, key: messageRef }
@@ -679,7 +728,8 @@ async function answerOnce({
679
728
  async function applyTransformReply(transformReply, input, logger) {
680
729
  if (!transformReply) return input.text;
681
730
  try {
682
- return await transformReply(input);
731
+ const result = await transformReply(input);
732
+ return typeof result === "string" ? result : null;
683
733
  } catch (error) {
684
734
  logger?.error(
685
735
  `transformReply threw for channel "${input.channel}"; sending the original reply`,
@@ -741,6 +791,7 @@ async function prepareChat({
741
791
  sender,
742
792
  rewriteQuery,
743
793
  rerankContext,
794
+ fallbackFn,
744
795
  logger
745
796
  }) {
746
797
  const lastUserMsg = lastUserMessage(messages);
@@ -757,21 +808,39 @@ async function prepareChat({
757
808
  logger?.error("rewriteQuery threw; falling back to the original query", error);
758
809
  }
759
810
  }
760
- let ctx = await store.query(query, topK, buckets ?? defaultBuckets(mode));
811
+ const resolvedBuckets = buckets ?? defaultBuckets(mode);
812
+ const retrievedChunks = await store.query(query, topK, resolvedBuckets);
813
+ let ctx = retrievedChunks;
761
814
  if (rerankContext) {
762
815
  try {
763
- const reranked = await rerankContext({ query, chunks: ctx });
816
+ const reranked = await rerankContext({ query, chunks: [...ctx] });
764
817
  if (Array.isArray(reranked) && reranked.every((c) => typeof c === "string")) ctx = reranked;
765
818
  } catch (error) {
766
819
  logger?.error("rerankContext threw; falling back to the original chunks", error);
767
820
  }
768
821
  }
822
+ let fallback;
823
+ if (ctx.length === 0 && fallbackFn) {
824
+ try {
825
+ const guidance = await fallbackFn({
826
+ query,
827
+ mode,
828
+ buckets: resolvedBuckets,
829
+ sender,
830
+ retrievedChunks
831
+ });
832
+ if (typeof guidance === "string" && guidance.trim()) fallback = guidance.trim();
833
+ } catch (error) {
834
+ logger?.error("fallbackFn threw; proceeding without fallback guidance", error);
835
+ }
836
+ }
769
837
  const persona = personaLayer?.trim() || (mode === "private" ? prompts.privatePersona : prompts.publicPersona);
770
838
  const hint = channelHint?.trim();
771
839
  const system = [
772
840
  prompts.baseSystemRules,
773
841
  persona,
774
842
  ...hint ? [hint] : [],
843
+ ...fallback ? [fallback] : [],
775
844
  `${label}:
776
845
  ${ctx.join("\n\n")}`
777
846
  ].join("\n\n");
@@ -779,7 +848,7 @@ ${ctx.join("\n\n")}`
779
848
  }
780
849
 
781
850
  // src/history/compaction.ts
782
- var DEFAULT_TIMEOUT_MS = 8e3;
851
+ var COMPACTION_TIMEOUT_MS = 8e3;
783
852
  var LOAD_ALL = Number.MAX_SAFE_INTEGER;
784
853
  var SUMMARY_PREFIX = "Summary of earlier conversation: ";
785
854
  function defaultSummarize(client, model) {
@@ -807,7 +876,7 @@ function createHistoryCompactor(deps, options) {
807
876
  throw new Error(`Invalid history compaction keep: ${keep}`);
808
877
  }
809
878
  const summarize = options.summarize ?? defaultSummarize(deps.client, options.model);
810
- const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
879
+ const timeoutMs = options.timeoutMs ?? COMPACTION_TIMEOUT_MS;
811
880
  return {
812
881
  async maybeCompact(store, conversationId) {
813
882
  const turns = await store.load(conversationId, LOAD_ALL);
@@ -848,6 +917,16 @@ function createHistoryCompactor(deps, options) {
848
917
  }
849
918
 
850
919
  // src/channels/pipeline.ts
920
+ function resolveBrainHooks(config, fallback) {
921
+ return {
922
+ answerFn: config.answerFn ?? fallback?.answerFn,
923
+ bucketsFor: config.bucketsFor ?? fallback?.bucketsFor,
924
+ rewriteQuery: config.rewriteQuery ?? fallback?.rewriteQuery,
925
+ rerankContext: config.rerankContext ?? fallback?.rerankContext,
926
+ fallbackFn: config.fallbackFn ?? fallback?.fallbackFn,
927
+ transformReply: config.transformReply ?? fallback?.transformReply
928
+ };
929
+ }
851
930
  var DEFAULT_HISTORY_LIMIT = 20;
852
931
  var DEFAULT_GROUP_RATE_LIMIT = { max: 30, windowMs: 60 * 60 * 1e3 };
853
932
  var DEFAULT_DM_RATE_LIMIT = { max: 20, windowMs: 60 * 60 * 1e3 };
@@ -932,6 +1011,7 @@ function createInboundPipeline(deps, config) {
932
1011
  sender,
933
1012
  rewriteQuery: config.rewriteQuery,
934
1013
  rerankContext: config.rerankContext,
1014
+ fallbackFn: config.fallbackFn,
935
1015
  logger: config.logger
936
1016
  });
937
1017
  const { content: produced } = await answerOnce({
@@ -1067,13 +1147,9 @@ function createWhatsAppInboundHandler(config) {
1067
1147
  { client: config.client, store: config.store, prompts: config.prompts },
1068
1148
  {
1069
1149
  channel: "whatsapp",
1070
- answerFn: config.answerFn,
1071
- bucketsFor: config.bucketsFor,
1072
- rewriteQuery: config.rewriteQuery,
1073
- rerankContext: config.rerankContext,
1074
- transformReply: config.transformReply,
1150
+ ...resolveBrainHooks(config, config.serverConfig),
1075
1151
  model: config.model,
1076
- channelHint: config.channelHint,
1152
+ channelHint: config.channelHint ?? "Channel: WhatsApp.",
1077
1153
  personaResolver: personaResolver ? ({ sender, text }) => personaResolver({ senderPhone: sender, text }) : void 0,
1078
1154
  history: config.history,
1079
1155
  allowedChats,
@@ -1233,6 +1309,7 @@ export {
1233
1309
  LEASE_WAIT_MS,
1234
1310
  acquireSessionLease,
1235
1311
  canAcquireLease,
1312
+ createTursoWaAuthKV,
1236
1313
  createTursoWaLeaseStore,
1237
1314
  createWhatsAppChannel,
1238
1315
  createWhatsAppImageHandler,
@@ -1248,11 +1325,13 @@ export {
1248
1325
  jidsMatch,
1249
1326
  loadBaileys,
1250
1327
  messageContext,
1328
+ normalizeWaMediaPayload,
1251
1329
  reconnectDelayMs,
1252
1330
  resolveWaMessage,
1253
1331
  senderNameFor,
1254
1332
  senderPhoneFor,
1255
1333
  shutdownWaSessions,
1256
1334
  stripOwnMentions,
1335
+ useAuthState,
1257
1336
  useTursoAuthState
1258
1337
  };
@@ -1 +1 @@
1
- {"version":3,"file":"pairing.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/pairing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pairing.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/pairing.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/client/src/Chat.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEvD,qBAAa,IAAI;IACf,OAAO,CAAC,GAAG,CAAU;IACrB,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAC,CAAa;IAE7B,OAAO,CAAC,iBAAiB,CAAe;IACxC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,UAAU,CAAqB;IAEvC,YAAY,MAAM,EAAE,UAAU,EA4B7B;IAED,OAAO,CAAC,MAAM;IAqBd,OAAO,CAAC,oBAAoB;YA+Cd,UAAU;IAkExB,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,oBAAoB;IAQ5B,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,cAAc;IAItB;;OAEG;IACH,KAAK,IAAI,IAAI,CAGZ;IAED;;OAEG;IACH,WAAW,IAAI,WAAW,EAAE,CAE3B;IAED;;OAEG;IACH,OAAO,IAAI,IAAI,CAEd;IAED;;OAEG;IACH,OAAO,CAAC,QAAQ;CAGjB"}
1
+ {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/client/src/Chat.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEvD,qBAAa,IAAI;IACf,OAAO,CAAC,GAAG,CAAU;IACrB,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAC,CAAa;IAE7B,OAAO,CAAC,iBAAiB,CAAe;IACxC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,UAAU,CAAqB;IAEvC,YAAY,MAAM,EAAE,UAAU,EA0B7B;IAED,OAAO,CAAC,MAAM;IAoBd,OAAO,CAAC,oBAAoB;YA8Cd,UAAU;IA4DxB,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,oBAAoB;IAQ5B,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,cAAc;IAItB;;OAEG;IACH,KAAK,IAAI,IAAI,CAGZ;IAED;;OAEG;IACH,WAAW,IAAI,WAAW,EAAE,CAE3B;IAED;;OAEG;IACH,OAAO,IAAI,IAAI,CAEd;IAED;;OAEG;IACH,OAAO,CAAC,QAAQ;CAGjB"}
@@ -1 +1 @@
1
- {"version":3,"file":"ChatButton.d.ts","sourceRoot":"","sources":["../../../src/client/src/ChatButton.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,IAAI,CAAqB;IACjC,OAAO,CAAC,MAAM,CAAS;IAEvB,YAAY,MAAM,EAAE,gBAAgB,EASnC;IAED,OAAO,CAAC,MAAM;IAsBd,OAAO,CAAC,oBAAoB;IAsB5B,OAAO,CAAC,MAAM;IAQd,IAAI,IAAI,IAAI,CAgDX;IAED,KAAK,IAAI,IAAI,CAcZ;IAED;;OAEG;IACH,OAAO,CAAC,QAAQ;IAIhB;;OAEG;IACH,OAAO,IAAI,IAAI,CAId;IAED;;OAEG;IACH,QAAQ,IAAI,OAAO,CAElB;CACF"}
1
+ {"version":3,"file":"ChatButton.d.ts","sourceRoot":"","sources":["../../../src/client/src/ChatButton.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,IAAI,CAAqB;IACjC,OAAO,CAAC,MAAM,CAAS;IAEvB,YAAY,MAAM,EAAE,gBAAgB,EASnC;IAED,OAAO,CAAC,MAAM;IAkBd,OAAO,CAAC,oBAAoB;IAoB5B,OAAO,CAAC,MAAM;IAQd,IAAI,IAAI,IAAI,CA+CX;IAED,KAAK,IAAI,IAAI,CAcZ;IAED;;OAEG;IACH,OAAO,CAAC,QAAQ;IAIhB;;OAEG;IACH,OAAO,IAAI,IAAI,CAId;IAED;;OAEG;IACH,QAAQ,IAAI,OAAO,CAElB;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../src/client/src/browser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,OAAO,EAAE;YACP,OAAO,EAAE,OAAO,OAAO,CAAC;YACxB,IAAI,EAAE,OAAO,IAAI,CAAC;YAClB,UAAU,EAAE,OAAO,UAAU,CAAC;SAC/B,CAAC;KACH;CACF;AAWD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC"}
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../src/client/src/browser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,OAAO,EAAE;YACP,OAAO,EAAE,OAAO,OAAO,CAAC;YACxB,IAAI,EAAE,OAAO,IAAI,CAAC;YAClB,UAAU,EAAE,OAAO,UAAU,CAAC;SAC/B,CAAC;KACH;CACF;AAYD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC"}
@@ -95,6 +95,14 @@ export type TransformReply = (input: TransformReplyInput) => string | null | Pro
95
95
  * Runs `transformReply` if configured. A throw/rejection is logged and the
96
96
  * ORIGINAL text is returned — the delivery invariant: a bug in a plugin hook
97
97
  * must never silently swallow an answer the model already produced.
98
+ *
99
+ * The result crosses the same plugin boundary `runAnswerFn` does, so it gets
100
+ * the same treatment: anything that isn't a string is coerced to `null`
101
+ * (treated as a veto) rather than passed through as whatever a
102
+ * non-conforming hook happened to return (`undefined` from a missing
103
+ * `return`, say). Every call site already treats a falsy result as "nothing
104
+ * delivered", but only a normalized `string | null` matches this function's
105
+ * own declared return type.
98
106
  */
99
107
  export declare function applyTransformReply(transformReply: TransformReply | undefined, input: TransformReplyInput, logger?: Pick<Logger, "error">): Promise<string | null>;
100
108
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../../src/core/answer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAGjC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEhE,gDAAgD;AAChD,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAKD,qEAAqE;AACrE,MAAM,WAAW,aAAa;IAC5B,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,4BAA4B;IAC5B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,uCAAuC;IACvC,IAAI,EAAE,YAAY,CAAC;IACnB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,WAAW,CAAA;CAAE,CAAC;AAE/E;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAAC;AAE1F,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqBD;;GAEG;AACH,wBAAsB,UAAU,CAAC,EAC/B,QAAQ,EACR,MAAM,EACN,MAAM,EACN,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,cAAc,EACd,WAAW,EACX,KAAK,GACN,EAAE,aAAa,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAAC,CAKlE;AAED,4FAA4F;AAC5F,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,mBAAmB,KAAK,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAEpG;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,cAAc,EAAE,cAAc,GAAG,SAAS,EAC1C,KAAK,EAAE,mBAAmB,EAC1B,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAWxB;AAED;;;;;;;;GAQG;AACH,wBAAuB,YAAY,CAAC,EAClC,QAAQ,EACR,MAAM,EACN,MAAM,EACN,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,cAAc,EACd,WAAW,EACX,KAAK,GACN,EAAE,aAAa,GAAG,cAAc,CAAC,MAAM,CAAC,CAaxC"}
1
+ {"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../../src/core/answer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAGjC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEhE,gDAAgD;AAChD,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAKD,qEAAqE;AACrE,MAAM,WAAW,aAAa;IAC5B,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,4BAA4B;IAC5B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,uCAAuC;IACvC,IAAI,EAAE,YAAY,CAAC;IACnB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,WAAW,CAAA;CAAE,CAAC;AAE/E;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAAC;AAE1F,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqBD;;GAEG;AACH,wBAAsB,UAAU,CAAC,EAC/B,QAAQ,EACR,MAAM,EACN,MAAM,EACN,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,cAAc,EACd,WAAW,EACX,KAAK,GACN,EAAE,aAAa,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAAC,CAKlE;AAED,4FAA4F;AAC5F,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,mBAAmB,KAAK,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAEpG;;;;;;;;;;;;GAYG;AACH,wBAAsB,mBAAmB,CACvC,cAAc,EAAE,cAAc,GAAG,SAAS,EAC1C,KAAK,EAAE,mBAAmB,EAC1B,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAYxB;AAED;;;;;;;;GAQG;AACH,wBAAuB,YAAY,CAAC,EAClC,QAAQ,EACR,MAAM,EACN,MAAM,EACN,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,cAAc,EACd,WAAW,EACX,KAAK,GACN,EAAE,aAAa,GAAG,cAAc,CAAC,MAAM,CAAC,CAaxC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Single-writer lock around the destructive half of a knowledge-base build.
3
+ *
4
+ * `VectorStore.build()` is a read-diff-delete-insert sequence: it reads the
5
+ * chunk ids already in the database, computes the ids its own `knowledgeDir`
6
+ * produces, and deletes everything not in that set. Two instances booting
7
+ * against the same database at the same time (a rolling deploy, a manual
8
+ * deploy racing an automated one) each run that sequence against a view of
9
+ * the data the other is still mutating, so one boot's cleanup can delete
10
+ * chunks the other has just written or has not finished writing.
11
+ *
12
+ * The lock lives in the same database (`chatter_build_lock`) so it is visible
13
+ * to every instance, and acquiring or renewing it is a single atomic upsert
14
+ * (see `createTursoBuildLock`) - two instances racing the same key can never
15
+ * both believe they hold it. A holder that dies without releasing leaves a
16
+ * stale row instead of wedging every future boot: after `staleMs` with no
17
+ * heartbeat, the next instance may take it over.
18
+ */
19
+ import type { Client } from "@libsql/client";
20
+ /** Lock key for the knowledge-base build. One build runs per database at a time. */
21
+ export declare const BUILD_LOCK_KEY = "knowledge_build";
22
+ /**
23
+ * A held lock this old with no heartbeat is treated as abandoned. Generous
24
+ * because a first build embeds every chunk it loads, which can take minutes;
25
+ * a holder renews the lock as it goes (see `VectorStore.build`), so only a
26
+ * process that actually died lets the lock go stale.
27
+ */
28
+ export declare const BUILD_LOCK_STALE_MS: number;
29
+ export interface BuildLockRow {
30
+ holderId: string;
31
+ heartbeatAt: number;
32
+ }
33
+ /**
34
+ * Pure: may `holderId` take (or keep) the build lock right now, given the
35
+ * current row (null = never held)? Mirrors the SQL WHERE clause in
36
+ * `createTursoBuildLock` - keep the two in sync.
37
+ */
38
+ export declare function canAcquireBuildLock(existing: BuildLockRow | null, holderId: string, now: number, staleMs?: number): boolean;
39
+ export interface BuildLock {
40
+ /** Atomically acquires or renews the lock; resolves true iff `holderId` now holds it. */
41
+ tryAcquire(key: string, holderId: string, now: number, staleMs: number): Promise<boolean>;
42
+ /** Releases the lock iff still held by `holderId` (no-op otherwise, so a late release never undoes a takeover). */
43
+ release(key: string, holderId: string): Promise<void>;
44
+ }
45
+ /**
46
+ * A libsql-backed {@link BuildLock}. The WHERE clause on DO UPDATE encodes
47
+ * `canAcquireBuildLock`: SQLite skips the update (and RETURNING yields no
48
+ * row) when it is false, so a losing racer sees zero rows back - no separate
49
+ * read-then-write, no race window.
50
+ */
51
+ export declare function createTursoBuildLock(client: Client): BuildLock;
52
+ //# sourceMappingURL=buildLock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildLock.d.ts","sourceRoot":"","sources":["../../src/core/buildLock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAE7C,oFAAoF;AACpF,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,QAAc,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,YAAY,GAAG,IAAI,EAC7B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,MAA4B,GACpC,OAAO,CAIT;AAED,MAAM,WAAW,SAAS;IACxB,yFAAyF;IACzF,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1F,mHAAmH;IACnH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvD;AAwBD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAyB9D"}
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import type { Logger } from "./logger";
12
12
  import type { PromptLoader } from "./prompts";
13
- import type { VectorStore } from "./retrieval";
13
+ import type { Retriever } from "./retrieval";
14
14
  export type PipelineMode = "public" | "private";
15
15
  export interface PipelineMessage {
16
16
  role: "user" | "assistant";
@@ -60,11 +60,43 @@ export interface RerankContextArgs {
60
60
  * fails closed by design; use this hook for relevance/ordering only.
61
61
  */
62
62
  export type RerankContext = (ctx: RerankContextArgs) => string[] | Promise<string[]>;
63
+ /** What `fallbackFn` sees for a turn where retrieval came back empty. */
64
+ export interface FallbackContext {
65
+ /** The query retrieval actually ran with, post-`rewriteQuery` if configured. */
66
+ query: string;
67
+ mode: PipelineMode;
68
+ /** Buckets retrieval ran against for this turn. */
69
+ buckets: string[];
70
+ /** Caller identity, where the surface has one. Absent on anonymous surfaces. */
71
+ sender?: string;
72
+ /**
73
+ * What `store.query` returned, before `rerankContext` ran: always
74
+ * non-empty when this is empty because `rerankContext` filtered
75
+ * everything out, and always empty when the store itself found nothing.
76
+ * A hook can use this to tell "some low-confidence signal existed" apart
77
+ * from "nothing matched at all".
78
+ */
79
+ retrievedChunks: string[];
80
+ }
81
+ /**
82
+ * Runs only when retrieval produced no chunks for a turn (after
83
+ * `rerankContext`, if configured), the deterministic, testable alternative
84
+ * to baking "what if there's nothing to retrieve" judgment calls into
85
+ * freeform system-prompt text. Return a string to inject as an extra system
86
+ * prompt layer scoped to this turn only (e.g. "no matching context was
87
+ * found; offer a clearly-labelled guess or decline"); return `undefined` to
88
+ * leave the prompt exactly as it would be otherwise.
89
+ *
90
+ * A throw/rejection is caught by `prepareChat`, which proceeds without
91
+ * fallback guidance: a broken hook must never break the chat path.
92
+ */
93
+ export type FallbackFn = (ctx: FallbackContext) => string | undefined | Promise<string | undefined>;
63
94
  /**
64
95
  * Run retrieval for the latest user message and assemble the system prompt.
65
96
  *
66
97
  * The assembled system prompt is layered, in order:
67
- * base rules → persona → channel hint (optional) → retrieved context.
98
+ * base rules → persona → channel hint (optional) → fallback guidance
99
+ * (optional, only when retrieval came back empty) → retrieved context.
68
100
  *
69
101
  * `personaLayer` and `channelHint` let a caller shape those middle layers
70
102
  * without hand-rolling its own sandwich around `store`/`prompts`. Both are
@@ -83,10 +115,13 @@ export type RerankContext = (ctx: RerankContextArgs) => string[] | Promise<strin
83
115
  * value falls back to what retrieval would have done unmodified, so a buggy
84
116
  * hook degrades relevance rather than breaking the chat path.
85
117
  *
118
+ * `fallbackFn` runs after both, only when the chunks folded into the prompt
119
+ * would otherwise be empty, see {@link FallbackFn}.
120
+ *
86
121
  * @throws if the conversation contains no user message
87
122
  */
88
- export declare function prepareChat({ store, prompts, mode, messages, personaLayer, channelHint, buckets, sender, rewriteQuery, rerankContext, logger, }: {
89
- store: VectorStore;
123
+ export declare function prepareChat({ store, prompts, mode, messages, personaLayer, channelHint, buckets, sender, rewriteQuery, rerankContext, fallbackFn, logger, }: {
124
+ store: Retriever;
90
125
  prompts: PromptLoader;
91
126
  mode: PipelineMode;
92
127
  messages: PipelineMessage[];
@@ -105,7 +140,9 @@ export declare function prepareChat({ store, prompts, mode, messages, personaLay
105
140
  rewriteQuery?: RewriteQuery;
106
141
  /** Post-processes retrieved chunks before they're folded into the prompt — see {@link RerankContext}. */
107
142
  rerankContext?: RerankContext;
108
- /** Logs a `rewriteQuery`/`rerankContext` throw before falling back. Unset: the failure is silent. */
143
+ /** Injects fallback guidance when retrieval comes back empty, see {@link FallbackFn}. */
144
+ fallbackFn?: FallbackFn;
145
+ /** Logs a `rewriteQuery`/`rerankContext`/`fallbackFn` throw before falling back. Unset: the failure is silent. */
109
146
  logger?: Pick<Logger, "error">;
110
147
  }): Promise<PreparedChat>;
111
148
  //# sourceMappingURL=pipeline.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/core/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEhD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,iIAAiI;IACjI,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,2CAA2C;AAC3C,MAAM,WAAW,gBAAgB;IAC/B,qFAAqF;IACrF,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,YAAY,CAAC;IACnB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,gBAAgB,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAE/E,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IAChC,kFAAkF;IAClF,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,iBAAiB,KAAK,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AAOrF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,WAAW,CAAC,EAChC,KAAK,EACL,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,OAAO,EACP,MAAM,EACN,YAAY,EACZ,aAAa,EACb,MAAM,GACP,EAAE;IACD,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,YAAY,CAAC;IACtB,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,uFAAuF;IACvF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yFAAyF;IACzF,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,yGAAyG;IACzG,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,qGAAqG;IACrG,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,GAAG,OAAO,CAAC,YAAY,CAAC,CAuCxB"}
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/core/pipeline.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,8 @@
3
3
  * Loads prompt templates and interpolates bot configuration
4
4
  */
5
5
  import type { BotIdentity } from "../types";
6
+ /** Default prompts directory when a caller's `config.promptsDir` is unset. */
7
+ export declare const DEFAULT_PROMPTS_DIR = "./config/prompts";
6
8
  /**
7
9
  * Prompt loader class that loads and interpolates prompt templates
8
10
  * with bot identity information
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/core/prompts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C;;;GAGG;AACH,qBAAa,YAAY;IAErB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,GAAG;IAFb,YACU,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,WAAW,EACtB;IAEJ;;OAEG;IACH,OAAO,CAAC,IAAI;IAcZ,+BAA+B;IAC/B,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED,4BAA4B;IAC5B,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,6BAA6B;IAC7B,IAAI,cAAc,IAAI,MAAM,CAE3B;CACF"}
1
+ {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/core/prompts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,8EAA8E;AAC9E,eAAO,MAAM,mBAAmB,qBAAqB,CAAC;AAEtD;;;GAGG;AACH,qBAAa,YAAY;IAErB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,GAAG;IAFb,YACU,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,WAAW,EACtB;IAEJ;;OAEG;IACH,OAAO,CAAC,IAAI;IAcZ,+BAA+B;IAC/B,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED,4BAA4B;IAC5B,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,6BAA6B;IAC7B,IAAI,cAAc,IAAI,MAAM,CAE3B;CACF"}