@diegoaltoworks/chatter 0.45.0 → 0.48.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 (120) hide show
  1. package/README.md +19 -5
  2. package/dist/auth/apikeys.d.ts.map +1 -1
  3. package/dist/auth/secretStrength.d.ts +8 -0
  4. package/dist/auth/secretStrength.d.ts.map +1 -0
  5. package/dist/bin/create-apikey.js +22 -3
  6. package/dist/bin/wa-pair.js +59 -11
  7. package/dist/channels/gates.d.ts +9 -0
  8. package/dist/channels/gates.d.ts.map +1 -1
  9. package/dist/channels/index.d.ts +1 -0
  10. package/dist/channels/index.d.ts.map +1 -1
  11. package/dist/channels/index.js +252 -8
  12. package/dist/channels/index.mjs +250 -8
  13. package/dist/channels/pipeline.d.ts +106 -0
  14. package/dist/channels/pipeline.d.ts.map +1 -0
  15. package/dist/channels/senders.d.ts +2 -1
  16. package/dist/channels/senders.d.ts.map +1 -1
  17. package/dist/channels/whatsapp/channel.d.ts +4 -0
  18. package/dist/channels/whatsapp/channel.d.ts.map +1 -1
  19. package/dist/channels/whatsapp/crypto.d.ts +17 -3
  20. package/dist/channels/whatsapp/crypto.d.ts.map +1 -1
  21. package/dist/channels/whatsapp/inbound.d.ts +34 -4
  22. package/dist/channels/whatsapp/inbound.d.ts.map +1 -1
  23. package/dist/channels/whatsapp/index.d.ts +2 -1
  24. package/dist/channels/whatsapp/index.d.ts.map +1 -1
  25. package/dist/channels/whatsapp/index.js +395 -159
  26. package/dist/channels/whatsapp/index.mjs +394 -160
  27. package/dist/channels/whatsapp/router.d.ts +107 -0
  28. package/dist/channels/whatsapp/router.d.ts.map +1 -0
  29. package/dist/core/answer.d.ts +5 -2
  30. package/dist/core/answer.d.ts.map +1 -1
  31. package/dist/core/logger.d.ts +23 -0
  32. package/dist/core/logger.d.ts.map +1 -0
  33. package/dist/core/messages.d.ts +8 -0
  34. package/dist/core/messages.d.ts.map +1 -1
  35. package/dist/core/pipeline.d.ts +2 -0
  36. package/dist/core/pipeline.d.ts.map +1 -1
  37. package/dist/core/retrieval.d.ts +4 -0
  38. package/dist/core/retrieval.d.ts.map +1 -1
  39. package/dist/core/serve-static.d.ts +38 -0
  40. package/dist/core/serve-static.d.ts.map +1 -0
  41. package/dist/flows/engine.d.ts +3 -0
  42. package/dist/flows/engine.d.ts.map +1 -1
  43. package/dist/flows/index.d.ts +1 -0
  44. package/dist/flows/index.d.ts.map +1 -1
  45. package/dist/flows/index.js +91 -25
  46. package/dist/flows/index.mjs +91 -26
  47. package/dist/flows/intent.d.ts +2 -1
  48. package/dist/flows/intent.d.ts.map +1 -1
  49. package/dist/flows/loader.d.ts +5 -3
  50. package/dist/flows/loader.d.ts.map +1 -1
  51. package/dist/flows/types.d.ts +9 -0
  52. package/dist/flows/types.d.ts.map +1 -1
  53. package/dist/history/index.d.ts +17 -0
  54. package/dist/history/index.d.ts.map +1 -0
  55. package/dist/history/index.js +100 -0
  56. package/dist/history/index.mjs +73 -0
  57. package/dist/history/tursoStore.d.ts +26 -0
  58. package/dist/history/tursoStore.d.ts.map +1 -0
  59. package/dist/history/types.d.ts +18 -0
  60. package/dist/history/types.d.ts.map +1 -0
  61. package/dist/index.d.ts +5 -2
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +1202 -32462
  64. package/dist/index.mjs +1118 -32414
  65. package/dist/mcp-server/cost-tracker.d.ts +11 -6
  66. package/dist/mcp-server/cost-tracker.d.ts.map +1 -1
  67. package/dist/mcp-server/logger.d.ts +5 -2
  68. package/dist/mcp-server/logger.d.ts.map +1 -1
  69. package/dist/mcp-server/sdk.d.ts +23 -0
  70. package/dist/mcp-server/sdk.d.ts.map +1 -0
  71. package/dist/mcp-server/tool-input.d.ts +12 -0
  72. package/dist/mcp-server/tool-input.d.ts.map +1 -0
  73. package/dist/mcp-server/types.d.ts +10 -3
  74. package/dist/mcp-server/types.d.ts.map +1 -1
  75. package/dist/mcp-server.d.ts +1 -2
  76. package/dist/mcp-server.d.ts.map +1 -1
  77. package/dist/mcp-server.js +192 -111
  78. package/dist/mcp-server.mjs +179 -98
  79. package/dist/middleware/auth.d.ts.map +1 -1
  80. package/dist/middleware/bodyLimit.d.ts +11 -0
  81. package/dist/middleware/bodyLimit.d.ts.map +1 -0
  82. package/dist/middleware/clientKey.d.ts +15 -0
  83. package/dist/middleware/clientKey.d.ts.map +1 -0
  84. package/dist/middleware/cors.d.ts.map +1 -1
  85. package/dist/middleware/jwt.d.ts +0 -3
  86. package/dist/middleware/jwt.d.ts.map +1 -1
  87. package/dist/middleware/originMatch.d.ts +27 -0
  88. package/dist/middleware/originMatch.d.ts.map +1 -0
  89. package/dist/middleware/ratelimit.d.ts +0 -6
  90. package/dist/middleware/ratelimit.d.ts.map +1 -1
  91. package/dist/middleware/referrer.d.ts +5 -1
  92. package/dist/middleware/referrer.d.ts.map +1 -1
  93. package/dist/middleware/securityHeaders.d.ts +20 -0
  94. package/dist/middleware/securityHeaders.d.ts.map +1 -0
  95. package/dist/middleware/session.d.ts +2 -1
  96. package/dist/middleware/session.d.ts.map +1 -1
  97. package/dist/middleware/windowBucket.d.ts +19 -0
  98. package/dist/middleware/windowBucket.d.ts.map +1 -0
  99. package/dist/personas/index.js +20 -1
  100. package/dist/personas/index.mjs +20 -1
  101. package/dist/personas/resolver.d.ts +3 -0
  102. package/dist/personas/resolver.d.ts.map +1 -1
  103. package/dist/routes/demo.d.ts.map +1 -1
  104. package/dist/routes/openai.d.ts.map +1 -1
  105. package/dist/routes/private.d.ts.map +1 -1
  106. package/dist/routes/public.d.ts.map +1 -1
  107. package/dist/scheduler/compose.d.ts +2 -1
  108. package/dist/scheduler/compose.d.ts.map +1 -1
  109. package/dist/scheduler/index.js +29 -9
  110. package/dist/scheduler/index.mjs +29 -9
  111. package/dist/scheduler/scheduler.d.ts +3 -0
  112. package/dist/scheduler/scheduler.d.ts.map +1 -1
  113. package/dist/scheduler/tick.d.ts +2 -0
  114. package/dist/scheduler/tick.d.ts.map +1 -1
  115. package/dist/server.d.ts.map +1 -1
  116. package/dist/server.js +726 -538
  117. package/dist/server.mjs +708 -527
  118. package/dist/types.d.ts +87 -0
  119. package/dist/types.d.ts.map +1 -1
  120. package/package.json +30 -9
@@ -33,9 +33,7 @@ __export(mcp_server_exports, {
33
33
  createMCPServer: () => createMCPServer
34
34
  });
35
35
  module.exports = __toCommonJS(mcp_server_exports);
36
- var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
37
36
  var import_openai = __toESM(require("openai"));
38
- var import_zod = require("zod");
39
37
 
40
38
  // src/core/guardrails.ts
41
39
  function detectLeakage(text) {
@@ -81,8 +79,8 @@ async function completeOnce({
81
79
 
82
80
  // src/core/answer.ts
83
81
  var noUsage = () => ({ prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 });
84
- async function runAnswerFn(answerFn, { system, messages, mode, sender }) {
85
- const result = await answerFn({ system, messages, mode, sender });
82
+ async function runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }) {
83
+ const result = await answerFn({ system, messages, mode, sender, conversationId });
86
84
  const raw = typeof result === "string" ? result : result?.content;
87
85
  const usage = typeof result === "string" ? void 0 : result?.usage;
88
86
  return {
@@ -97,11 +95,12 @@ async function answerOnce({
97
95
  messages,
98
96
  mode,
99
97
  sender,
98
+ conversationId,
100
99
  temperature,
101
100
  model
102
101
  }) {
103
102
  if (answerFn) {
104
- return runAnswerFn(answerFn, { system, messages, mode, sender });
103
+ return runAnswerFn(answerFn, { system, messages, mode, sender, conversationId });
105
104
  }
106
105
  return completeOnce({ client, system, messages, temperature, model });
107
106
  }
@@ -137,6 +136,62 @@ async function resolveBuckets({
137
136
  return cleaned.filter((bucket) => ceiling.has(bucket));
138
137
  }
139
138
 
139
+ // src/core/logger.ts
140
+ var LEVEL_ORDER = { debug: 0, info: 1, warn: 2, error: 3 };
141
+ function createConsoleLogger(level = "info") {
142
+ const threshold = LEVEL_ORDER[level];
143
+ const write = (at) => (...args) => {
144
+ if (LEVEL_ORDER[at] < threshold) return;
145
+ console.error(`[${at}]`, ...args);
146
+ };
147
+ return {
148
+ debug: write("debug"),
149
+ info: write("info"),
150
+ warn: write("warn"),
151
+ error: write("error")
152
+ };
153
+ }
154
+ function resolveLogger(logger, level) {
155
+ return logger ?? createConsoleLogger(level);
156
+ }
157
+
158
+ // src/core/messages.ts
159
+ function lastUserMessage(messages) {
160
+ return [...messages].reverse().find((m) => m.role === "user");
161
+ }
162
+
163
+ // src/core/pipeline.ts
164
+ var MODE_SETTINGS = {
165
+ public: { topK: 6, label: "Context" },
166
+ private: { topK: 8, label: "Internal Context" }
167
+ };
168
+ async function prepareChat({
169
+ store,
170
+ prompts,
171
+ mode,
172
+ messages,
173
+ personaLayer,
174
+ channelHint,
175
+ buckets
176
+ }) {
177
+ const lastUserMsg = lastUserMessage(messages);
178
+ if (!lastUserMsg) {
179
+ throw new Error("no user message found in conversation");
180
+ }
181
+ const { topK, label } = MODE_SETTINGS[mode];
182
+ const ctx = await store.query(lastUserMsg.content, topK, buckets ?? defaultBuckets(mode));
183
+ const persona = personaLayer?.trim() || (mode === "private" ? prompts.privatePersona : prompts.publicPersona);
184
+ const hint = channelHint?.trim();
185
+ const system = [
186
+ prompts.baseSystemRules,
187
+ persona,
188
+ ...hint ? [hint] : [],
189
+ `${label}:
190
+ ${ctx.join("\n\n")}`
191
+ ].join("\n\n");
192
+ return { system, messages, context: ctx };
193
+ }
194
+
140
195
  // src/core/prompts.ts
141
196
  var import_node_fs = require("node:fs");
142
197
  var import_node_path = require("node:path");
@@ -229,6 +284,7 @@ var VectorStore = class _VectorStore {
229
284
  authToken: options.databaseAuthToken
230
285
  });
231
286
  this.knowledgeDir = options.knowledgeDir || "./config/knowledge";
287
+ this.logger = options.logger ?? createConsoleLogger();
232
288
  }
233
289
  client;
234
290
  /**
@@ -238,9 +294,10 @@ var VectorStore = class _VectorStore {
238
294
  */
239
295
  db;
240
296
  knowledgeDir;
297
+ logger;
241
298
  // On boot: ingest new chunks and embed only missing ones.
242
299
  async build() {
243
- console.log("\u{1F504} Building knowledge base...");
300
+ this.logger.info("\u{1F504} Building knowledge base...");
244
301
  await this.db.execute(`
245
302
  CREATE TABLE IF NOT EXISTS chunks (id TEXT PRIMARY KEY, bucket TEXT NOT NULL, source TEXT NOT NULL, text TEXT NOT NULL);
246
303
  `);
@@ -249,7 +306,7 @@ var VectorStore = class _VectorStore {
249
306
  `);
250
307
  await this.db.execute("CREATE INDEX IF NOT EXISTS idx_chunks_bucket ON chunks(bucket);");
251
308
  const docs = loadKnowledge(this.knowledgeDir);
252
- console.log(`\u{1F4DA} Loaded ${docs.length} knowledge documents`);
309
+ this.logger.info(`\u{1F4DA} Loaded ${docs.length} knowledge documents`);
253
310
  const rows = [];
254
311
  for (const d of docs) {
255
312
  for (const part of chunk(d.text)) {
@@ -257,21 +314,19 @@ var VectorStore = class _VectorStore {
257
314
  rows.push({ id, bucket: d.bucket, source: d.source, text: part });
258
315
  }
259
316
  }
260
- console.log(`\u{1F4E6} Created ${rows.length} chunks from knowledge documents`);
317
+ this.logger.info(`\u{1F4E6} Created ${rows.length} chunks from knowledge documents`);
261
318
  await this.cleanupStaleChunks(rows.map((r) => r.id));
262
- const tx = await this.db.transaction("write");
263
- try {
264
- for (const r of rows) {
265
- await tx.execute({
319
+ const UPSERT_BATCH = 500;
320
+ for (let i = 0; i < rows.length; i += UPSERT_BATCH) {
321
+ const batch = rows.slice(i, i + UPSERT_BATCH);
322
+ await this.db.batch(
323
+ batch.map((r) => ({
266
324
  sql: `INSERT INTO chunks(id,bucket,source,text) VALUES(?,?,?,?)
267
325
  ON CONFLICT(id) DO NOTHING`,
268
326
  args: [r.id, r.bucket, r.source, r.text]
269
- });
270
- }
271
- await tx.commit();
272
- } catch (e) {
273
- await tx.rollback();
274
- throw e;
327
+ })),
328
+ "write"
329
+ );
275
330
  }
276
331
  const ids = rows.map((r) => r.id);
277
332
  const missing = [];
@@ -286,32 +341,25 @@ var VectorStore = class _VectorStore {
286
341
  for (const row of res.rows) missing.push(String(row.id));
287
342
  }
288
343
  if (missing.length === 0) {
289
- console.log("\u2705 No new chunks to embed - knowledge base is up to date");
344
+ this.logger.info("\u2705 No new chunks to embed - knowledge base is up to date");
290
345
  return;
291
346
  }
292
- console.log(`\u{1F52E} Embedding ${missing.length} new/updated chunks with ${EMB_MODEL}...`);
347
+ this.logger.info(`\u{1F52E} Embedding ${missing.length} new/updated chunks with ${EMB_MODEL}...`);
293
348
  const textById = new Map(rows.map((r) => [r.id, r.text]));
294
349
  const BATCH = 96;
295
350
  for (let i = 0; i < missing.length; i += BATCH) {
296
351
  const batchIds = missing.slice(i, i + BATCH);
297
352
  const inputs = batchIds.map((id) => textById.get(id) || "");
298
353
  const emb = await this.client.embeddings.create({ model: EMB_MODEL, input: inputs });
299
- const tx2 = await this.db.transaction("write");
300
- try {
301
- emb.data.forEach((d, idx) => {
302
- const id = batchIds[idx];
303
- tx2.execute({
304
- sql: "INSERT INTO embeddings(id,model,embedding) VALUES(?,?,?)",
305
- args: [id, EMB_MODEL, JSON.stringify(d.embedding)]
306
- });
307
- });
308
- await tx2.commit();
309
- } catch (e) {
310
- await tx2.rollback();
311
- throw e;
312
- }
354
+ await this.db.batch(
355
+ emb.data.map((d, idx) => ({
356
+ sql: "INSERT INTO embeddings(id,model,embedding) VALUES(?,?,?)",
357
+ args: [batchIds[idx], EMB_MODEL, JSON.stringify(d.embedding)]
358
+ })),
359
+ "write"
360
+ );
313
361
  }
314
- console.log(`\u2705 Successfully embedded ${missing.length} new chunks`);
362
+ this.logger.info(`\u2705 Successfully embedded ${missing.length} new chunks`);
315
363
  }
316
364
  // Remove chunks from database that no longer exist in markdown files
317
365
  async cleanupStaleChunks(currentIds) {
@@ -322,7 +370,7 @@ var VectorStore = class _VectorStore {
322
370
  if (staleIds.length === 0) {
323
371
  return;
324
372
  }
325
- console.log(`\u{1F9F9} Cleaning up ${staleIds.length} stale chunks...`);
373
+ this.logger.info(`\u{1F9F9} Cleaning up ${staleIds.length} stale chunks...`);
326
374
  const BATCH_SIZE = 500;
327
375
  for (let i = 0; i < staleIds.length; i += BATCH_SIZE) {
328
376
  const batch = staleIds.slice(i, i + BATCH_SIZE);
@@ -332,7 +380,7 @@ var VectorStore = class _VectorStore {
332
380
  args: batch
333
381
  });
334
382
  }
335
- console.log(`\u2713 Cleaned up ${staleIds.length} stale chunks and their embeddings`);
383
+ this.logger.info(`\u2713 Cleaned up ${staleIds.length} stale chunks and their embeddings`);
336
384
  }
337
385
  static cosine(a, b) {
338
386
  let dot = 0;
@@ -389,13 +437,11 @@ function getOrGenerateConversationId(providedId) {
389
437
  }
390
438
 
391
439
  // src/mcp-server/cost-tracker.ts
392
- function calculateCost(usage) {
440
+ function calculateCost(usage, rates) {
393
441
  const promptTokens = usage.prompt_tokens || 0;
394
442
  const completionTokens = usage.completion_tokens || 0;
395
443
  const totalTokens = promptTokens + completionTokens;
396
- const promptCost = promptTokens / 1e6 * 2.5;
397
- const completionCost = completionTokens / 1e6 * 10;
398
- const estimatedCost = promptCost + completionCost;
444
+ const estimatedCost = rates ? promptTokens / 1e6 * rates.promptPer1M + completionTokens / 1e6 * rates.completionPer1M : null;
399
445
  return {
400
446
  promptTokens,
401
447
  completionTokens,
@@ -408,15 +454,17 @@ function calculateCost(usage) {
408
454
  var MCPLogger = class {
409
455
  enableConsole;
410
456
  callback;
411
- constructor(enableConsole = true, callback) {
457
+ logger;
458
+ constructor(enableConsole = true, callback, logger) {
412
459
  this.enableConsole = enableConsole;
413
460
  this.callback = callback;
461
+ this.logger = logger ?? createConsoleLogger();
414
462
  }
415
463
  /**
416
464
  * Log a chat interaction
417
465
  */
418
466
  async logChatInteraction(toolName, conversationId, conversationMessages, ragContext, response, duration, cost) {
419
- const lastUserMsg = [...conversationMessages].reverse().find((m) => m.role === "user");
467
+ const lastUserMsg = lastUserMessage(conversationMessages);
420
468
  const logEvent = {
421
469
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
422
470
  toolName,
@@ -429,7 +477,7 @@ var MCPLogger = class {
429
477
  cost
430
478
  };
431
479
  if (this.enableConsole) {
432
- console.log(
480
+ this.logger.info(
433
481
  JSON.stringify({
434
482
  event: "mcp_chat",
435
483
  ...logEvent
@@ -440,13 +488,13 @@ var MCPLogger = class {
440
488
  try {
441
489
  await this.callback(logEvent);
442
490
  } catch (err) {
443
- console.error("Logging callback error:", err);
491
+ this.logger.error("Logging callback error:", err);
444
492
  }
445
493
  }
446
494
  }
447
495
  };
448
- function createLogger(enableConsole = true, callback) {
449
- return new MCPLogger(enableConsole, callback);
496
+ function createLogger(enableConsole = true, callback, logger) {
497
+ return new MCPLogger(enableConsole, callback, logger);
450
498
  }
451
499
 
452
500
  // src/mcp-server/rate-limiter.ts
@@ -528,14 +576,57 @@ function createRateLimiter(limit, windowMs = 6e4) {
528
576
  return limit ? new RateLimiter(limit, windowMs) : null;
529
577
  }
530
578
 
579
+ // src/mcp-server/sdk.ts
580
+ function wrapMissingMcpPeerError(packageName, cause) {
581
+ return new Error(
582
+ `The MCP server ('@diegoaltoworks/chatter/mcp') requires the optional peer dependency '${packageName}', which is not installed. Install it with \`bun add ${packageName}\` (or npm/pnpm/yarn) to use this feature.`,
583
+ { cause }
584
+ );
585
+ }
586
+ var cachedSdk;
587
+ function loadMcpSdk() {
588
+ if (!cachedSdk) {
589
+ cachedSdk = import("@modelcontextprotocol/sdk/server/mcp.js").then((mod) => {
590
+ if (!mod.McpServer) throw new Error("module has no McpServer export");
591
+ return mod.McpServer;
592
+ }).catch((error) => {
593
+ cachedSdk = void 0;
594
+ throw wrapMissingMcpPeerError("@modelcontextprotocol/sdk", error);
595
+ });
596
+ }
597
+ return cachedSdk;
598
+ }
599
+ var cachedZod;
600
+ function loadZod() {
601
+ if (!cachedZod) {
602
+ cachedZod = import("zod").then((mod) => {
603
+ if (!mod.z) throw new Error("module has no z export");
604
+ return mod.z;
605
+ }).catch((error) => {
606
+ cachedZod = void 0;
607
+ throw wrapMissingMcpPeerError("zod", error);
608
+ });
609
+ }
610
+ return cachedZod;
611
+ }
612
+
613
+ // src/mcp-server/tool-input.ts
614
+ function resolveConversationMessages(message, messages) {
615
+ if (messages) return messages;
616
+ if (message) return [{ role: "user", content: message }];
617
+ throw new Error("Either 'message' or 'messages' is required");
618
+ }
619
+
531
620
  // src/mcp-server.ts
532
621
  async function createMCPServer(config) {
533
- console.log(`\u{1F680} Starting ${config.bot.name} MCP Server...`);
622
+ const log = resolveLogger(config.logger, config.logLevel);
623
+ log.info(`\u{1F680} Starting ${config.bot.name} MCP Server...`);
624
+ const [McpServer, z] = await Promise.all([loadMcpSdk(), loadZod()]);
534
625
  const knowledgeDir = config.knowledgeDir || "./config/knowledge";
535
626
  const promptsDir = config.promptsDir || "./config/prompts";
536
627
  const transportMode = config.transport || "stdio";
537
628
  const rateLimiter = createRateLimiter(config.toolRateLimit);
538
- const logger = createLogger(config.logging?.console !== false, config.logging?.onChat);
629
+ const logger = createLogger(config.logging?.console !== false, config.logging?.onChat, log);
539
630
  const publicToolConfig = {
540
631
  enabled: config.tools?.public?.enabled !== false,
541
632
  name: config.tools?.public?.name || "chat_public",
@@ -549,29 +640,31 @@ async function createMCPServer(config) {
549
640
  const client = new import_openai.default({
550
641
  apiKey: config.openai.apiKey
551
642
  });
643
+ const model = config.openai.model || DEFAULT_MODEL;
552
644
  const store = new VectorStore(client, {
553
645
  databaseUrl: config.database.url,
554
646
  databaseAuthToken: config.database.authToken,
555
- knowledgeDir
647
+ knowledgeDir,
648
+ logger: log
556
649
  });
557
650
  await store.build();
558
- console.log("\u2705 Knowledge base ready");
651
+ log.info("\u2705 Knowledge base ready");
559
652
  const prompts = new PromptLoader(promptsDir, config.bot);
560
- const server = new import_mcp.McpServer({
653
+ const server = new McpServer({
561
654
  name: config.bot.name,
562
655
  version: "1.0.0"
563
656
  });
564
- console.log("\u2705 MCP server initialized");
657
+ log.info("\u2705 MCP server initialized");
565
658
  if (publicToolConfig.enabled) {
566
659
  const publicShape = {
567
- message: import_zod.z.string().optional().describe("A single message to send (for simple queries)"),
568
- messages: import_zod.z.array(
569
- import_zod.z.object({
570
- role: import_zod.z.enum(["user", "assistant"]),
571
- content: import_zod.z.string()
660
+ message: z.string().optional().describe("A single message to send (for simple queries)"),
661
+ messages: z.array(
662
+ z.object({
663
+ role: z.enum(["user", "assistant"]),
664
+ content: z.string()
572
665
  })
573
666
  ).optional().describe("Conversation history with alternating user/assistant messages"),
574
- conversationId: import_zod.z.string().optional().describe(
667
+ conversationId: z.string().optional().describe(
575
668
  "Optional conversation ID to track sessions. If not provided, a new ID will be generated."
576
669
  )
577
670
  };
@@ -590,34 +683,28 @@ async function createMCPServer(config) {
590
683
  `Rate limit exceeded for ${publicToolConfig.name}. Maximum ${config.toolRateLimit} requests per minute.`
591
684
  );
592
685
  }
593
- let conversationMessages;
594
- if (messages && Array.isArray(messages)) {
595
- conversationMessages = messages;
596
- } else if (message) {
597
- conversationMessages = [{ role: "user", content: String(message) }];
598
- } else {
599
- throw new Error("Either 'message' or 'messages' is required");
600
- }
601
- const lastUserMsg = [...conversationMessages].reverse().find((m) => m.role === "user");
602
- if (!lastUserMsg) {
686
+ const conversationMessages = resolveConversationMessages(message, messages);
687
+ if (!lastUserMessage(conversationMessages)) {
603
688
  throw new Error("No user message found in conversation");
604
689
  }
605
- const buckets = await resolveBuckets({ mode: "public", bucketsFor: config.bucketsFor }) ?? defaultBuckets("public");
606
- const ctx = await store.query(lastUserMsg.content, 6, buckets);
607
- const system = [
608
- prompts.baseSystemRules,
609
- prompts.publicPersona,
610
- `Context:
611
- ${ctx.join("\n\n")}`
612
- ].join("\n\n");
690
+ const buckets = await resolveBuckets({ mode: "public", bucketsFor: config.bucketsFor });
691
+ const { system, context: ctx } = await prepareChat({
692
+ store,
693
+ prompts,
694
+ mode: "public",
695
+ messages: conversationMessages,
696
+ buckets
697
+ });
613
698
  const result = await answerOnce({
614
699
  answerFn: config.answerFn,
615
700
  client,
616
701
  system,
617
702
  messages: conversationMessages,
618
- mode: "public"
703
+ mode: "public",
704
+ conversationId: convId,
705
+ model
619
706
  });
620
- const cost = calculateCost(result.usage);
707
+ const cost = calculateCost(result.usage, config.openai.pricing);
621
708
  const duration = Date.now() - startTime;
622
709
  await logger.logChatInteraction(
623
710
  publicToolConfig.name,
@@ -627,7 +714,7 @@ ${ctx.join("\n\n")}`
627
714
  result.content,
628
715
  duration,
629
716
  cost
630
- ).catch((err) => console.error("Logging error:", err));
717
+ ).catch((err) => log.error("Logging error:", err));
631
718
  return {
632
719
  content: [
633
720
  {
@@ -647,18 +734,18 @@ ${ctx.join("\n\n")}`
647
734
  };
648
735
  }
649
736
  );
650
- console.log(`\u2705 Registered tool: ${publicToolConfig.name}`);
737
+ log.info(`\u2705 Registered tool: ${publicToolConfig.name}`);
651
738
  }
652
739
  if (privateToolConfig.enabled) {
653
740
  const privateShape = {
654
- message: import_zod.z.string().optional().describe("A single message to send (for simple queries)"),
655
- messages: import_zod.z.array(
656
- import_zod.z.object({
657
- role: import_zod.z.enum(["user", "assistant"]),
658
- content: import_zod.z.string()
741
+ message: z.string().optional().describe("A single message to send (for simple queries)"),
742
+ messages: z.array(
743
+ z.object({
744
+ role: z.enum(["user", "assistant"]),
745
+ content: z.string()
659
746
  })
660
747
  ).optional().describe("Conversation history with alternating user/assistant messages"),
661
- conversationId: import_zod.z.string().optional().describe(
748
+ conversationId: z.string().optional().describe(
662
749
  "Optional conversation ID to track sessions. If not provided, a new ID will be generated."
663
750
  )
664
751
  };
@@ -677,34 +764,28 @@ ${ctx.join("\n\n")}`
677
764
  `Rate limit exceeded for ${privateToolConfig.name}. Maximum ${config.toolRateLimit} requests per minute.`
678
765
  );
679
766
  }
680
- let conversationMessages;
681
- if (messages && Array.isArray(messages)) {
682
- conversationMessages = messages;
683
- } else if (message) {
684
- conversationMessages = [{ role: "user", content: String(message) }];
685
- } else {
686
- throw new Error("Either 'message' or 'messages' is required");
687
- }
688
- const lastUserMsg = [...conversationMessages].reverse().find((m) => m.role === "user");
689
- if (!lastUserMsg) {
767
+ const conversationMessages = resolveConversationMessages(message, messages);
768
+ if (!lastUserMessage(conversationMessages)) {
690
769
  throw new Error("No user message found in conversation");
691
770
  }
692
- const buckets = await resolveBuckets({ mode: "private", bucketsFor: config.bucketsFor }) ?? defaultBuckets("private");
693
- const ctx = await store.query(lastUserMsg.content, 8, buckets);
694
- const system = [
695
- prompts.baseSystemRules,
696
- prompts.privatePersona,
697
- `Internal Context:
698
- ${ctx.join("\n\n")}`
699
- ].join("\n\n");
771
+ const buckets = await resolveBuckets({ mode: "private", bucketsFor: config.bucketsFor });
772
+ const { system, context: ctx } = await prepareChat({
773
+ store,
774
+ prompts,
775
+ mode: "private",
776
+ messages: conversationMessages,
777
+ buckets
778
+ });
700
779
  const result = await answerOnce({
701
780
  answerFn: config.answerFn,
702
781
  client,
703
782
  system,
704
783
  messages: conversationMessages,
705
- mode: "private"
784
+ mode: "private",
785
+ conversationId: convId,
786
+ model
706
787
  });
707
- const cost = calculateCost(result.usage);
788
+ const cost = calculateCost(result.usage, config.openai.pricing);
708
789
  const duration = Date.now() - startTime;
709
790
  await logger.logChatInteraction(
710
791
  privateToolConfig.name,
@@ -714,7 +795,7 @@ ${ctx.join("\n\n")}`
714
795
  result.content,
715
796
  duration,
716
797
  cost
717
- ).catch((err) => console.error("Logging error:", err));
798
+ ).catch((err) => log.error("Logging error:", err));
718
799
  return {
719
800
  content: [
720
801
  {
@@ -734,16 +815,16 @@ ${ctx.join("\n\n")}`
734
815
  };
735
816
  }
736
817
  );
737
- console.log(`\u2705 Registered tool: ${privateToolConfig.name}`);
818
+ log.info(`\u2705 Registered tool: ${privateToolConfig.name}`);
738
819
  }
739
820
  const enabledTools = [
740
821
  publicToolConfig.enabled ? publicToolConfig.name : null,
741
822
  privateToolConfig.enabled ? privateToolConfig.name : null
742
823
  ].filter(Boolean);
743
824
  if (enabledTools.length === 0) {
744
- console.warn("\u26A0\uFE0F Warning: No MCP tools enabled");
825
+ log.warn("\u26A0\uFE0F Warning: No MCP tools enabled");
745
826
  }
746
- console.log(
827
+ log.info(
747
828
  `\u2705 ${config.bot.name} MCP Server ready (transport: ${transportMode}, tools: ${enabledTools.join(", ") || "none"})`
748
829
  );
749
830
  return server;