feat: persistent conversation history across sessions and restarts
- ConversationStore: per-chat JSON files in data/, survives restarts - 6000 token budget per chat context (fits ~20-30 exchanges) - Auto-trims old messages, always includes most recent - Wired into message handler: loads history before AI call, saves after - /reset command to clear chat history per chat - Cross-session, cross-model, cross-chat isolation
This commit is contained in:
@@ -11,7 +11,7 @@ import { isDuplicate, markProcessed } from './deduplication.js';
|
||||
import { queueRequest, clearQueue, isProcessing } from './request-queue.js';
|
||||
import { sendFormatted, splitMessage, escapeMarkdown, sendStreamingMessage, StreamConsumer, markdownToHtml } from './message-sender.js';
|
||||
import { withSelfCorrection } from './self-correction.js';
|
||||
import { getMemory } from './memory.js';
|
||||
import { getMemory, getConversation } from './memory.js';
|
||||
|
||||
function buildSessionKey(chatId, threadId) {
|
||||
return threadId ? `${chatId}:${threadId}` : String(chatId);
|
||||
@@ -157,8 +157,12 @@ export async function initBot(config, api, tools, skills, agents) {
|
||||
const memory = getMemory();
|
||||
await memory.init();
|
||||
|
||||
// ── Conversation history (cross-session, cross-model) ──
|
||||
const conversation = getConversation();
|
||||
await conversation.init();
|
||||
|
||||
// ── Service registry ──
|
||||
const svc = { config, api, tools: tools || [], skills: skills || [], agents: agents || [], rtk, memory,
|
||||
const svc = { config, api, tools: tools || [], skills: skills || [], agents: agents || [], rtk, memory, conversation,
|
||||
toolMap: new Map((tools || []).map(t => [t.name, t])),
|
||||
};
|
||||
|
||||
@@ -625,6 +629,12 @@ export async function initBot(config, api, tools, skills, agents) {
|
||||
await ctx.reply(ok ? '🗑 Memory deleted.' : '❌ Memory not found.');
|
||||
});
|
||||
|
||||
bot.command('reset', async (ctx) => {
|
||||
const chatKey = conversation._key(ctx.chat.id, ctx.message?.message_thread_id);
|
||||
await conversation.clear(chatKey);
|
||||
await ctx.reply('🧹 Chat history cleared. Fresh start — I won\'t remember our previous conversation in this chat.');
|
||||
});
|
||||
|
||||
bot.command('cron', async (ctx) => {
|
||||
await ctx.reply('⏰ *Cron:* CronScheduler at `src/utils/cronScheduler.ts` — 1s interval, task locking, auto-recovery.');
|
||||
});
|
||||
@@ -676,19 +686,31 @@ export async function initBot(config, api, tools, skills, agents) {
|
||||
await queueRequest(key, text, async () => {
|
||||
await ctx.api.sendChatAction(ctx.chat.id, 'typing');
|
||||
|
||||
// ── Load conversation history for this chat ──
|
||||
const chatKey = conversation._key(ctx.chat.id, ctx.message?.message_thread_id);
|
||||
const history = await conversation.getContext(chatKey);
|
||||
|
||||
// Create stream consumer for real-time edit-in-place
|
||||
const consumer = new StreamConsumer(ctx, { editInterval: 1000 });
|
||||
const runPromise = consumer.run();
|
||||
|
||||
// Build messages: system + history + current
|
||||
const messages = [
|
||||
{ role: 'system', content: buildSystemPrompt(svc) },
|
||||
...history,
|
||||
{ role: 'user', content: text },
|
||||
];
|
||||
|
||||
// Wrap chatWithAI with self-correction + streaming
|
||||
const chatWithCorrection = withSelfCorrection(async (msgs) => {
|
||||
return await chatWithAI(msgs, { onDelta: (token) => consumer.onDelta(token) });
|
||||
});
|
||||
|
||||
const result = await chatWithCorrection([
|
||||
{ role: 'system', content: buildSystemPrompt(svc) },
|
||||
{ role: 'user', content: text },
|
||||
]);
|
||||
const result = await chatWithCorrection(messages);
|
||||
|
||||
// ── Save this exchange to conversation history ──
|
||||
await conversation.add(chatKey, 'user', text);
|
||||
if (result) await conversation.add(chatKey, 'assistant', result);
|
||||
|
||||
// Signal completion and wait for final edit
|
||||
consumer.finish();
|
||||
|
||||
Reference in New Issue
Block a user