feat: integrate model router, session persistence, and hook engine

- NativeAgent now loads/saves messages to SessionStore
- Daemon creates ModelRouter with fallback chain support
- Telegram bot handles confirmation callbacks from HookEngine
- Session data stored in ~/.local/share/flynn/sessions.db

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
William Valentin
2026-02-05 00:05:42 -08:00
parent 26bd6ce65d
commit 6e6c263e14
3 changed files with 143 additions and 12 deletions
+36 -2
View File
@@ -1,11 +1,14 @@
import { Bot, Context } from 'grammy';
import { Bot } from 'grammy';
import type { NativeAgent } from '../../backends/index.js';
import type { TelegramConfig } from '../../config/index.js';
import type { HookEngine } from '../../hooks/index.js';
import { isAllowedChat, createMessageHandler, createResetHandler } from './handlers.js';
import { parseConfirmationCallback } from './confirmations.js';
export interface TelegramBotConfig {
telegram: TelegramConfig;
agent: NativeAgent;
hookEngine?: HookEngine;
}
export function createTelegramBot(config: TelegramBotConfig): Bot {
@@ -13,6 +16,7 @@ export function createTelegramBot(config: TelegramBotConfig): Bot {
const handleMessage = createMessageHandler(config.agent);
const handleReset = createResetHandler(config.agent);
const allowedChatIds = config.telegram.allowed_chat_ids;
const hookEngine = config.hookEngine;
// Middleware to check chat ID
bot.use(async (ctx, next) => {
@@ -24,6 +28,34 @@ export function createTelegramBot(config: TelegramBotConfig): Bot {
await next();
});
// Handle confirmation callbacks
bot.on('callback_query:data', async (ctx) => {
const data = ctx.callbackQuery.data;
const parsed = parseConfirmationCallback(data);
if (!parsed || !hookEngine) {
await ctx.answerCallbackQuery({ text: 'Invalid action' });
return;
}
const resolved = hookEngine.resolveConfirmation(parsed.id, {
approved: parsed.approved,
reason: parsed.approved ? undefined : 'Denied by user',
});
if (resolved) {
await ctx.answerCallbackQuery({
text: parsed.approved ? '✅ Approved' : '❌ Denied',
});
await ctx.editMessageText(
ctx.callbackQuery.message?.text + `\n\n${parsed.approved ? '✅ Approved' : '❌ Denied'}`,
{ parse_mode: 'Markdown' }
);
} else {
await ctx.answerCallbackQuery({ text: 'Confirmation expired or not found' });
}
});
// Command handlers
bot.command('start', async (ctx) => {
await ctx.reply('Flynn is ready. Send me a message!');
@@ -35,7 +67,9 @@ export function createTelegramBot(config: TelegramBotConfig): Bot {
});
bot.command('status', async (ctx) => {
await ctx.reply('Flynn is running.');
const pending = hookEngine?.getPendingConfirmations() ?? [];
const statusMsg = `Flynn is running.\nPending confirmations: ${pending.length}`;
await ctx.reply(statusMsg);
});
// Message handler