44 */
55
66import {
7+ type BaseSessionService ,
78 createSamplingHandler ,
9+ type EnhancedRunner ,
810 extractTextFromContent ,
911 type LlmRequest ,
12+ type MemoryService ,
1013} from "@iqai/adk" ;
1114import { createClawdAgent } from "../agents/agent.js" ;
1215import { getTelegramAgent } from "../agents/telegram-agent/agent.js" ;
@@ -19,7 +22,6 @@ import {
1922 initScheduler ,
2023 stopScheduler ,
2124} from "./SchedulerService.js" ;
22- import { sessionManager } from "./SessionManager.js" ;
2325
2426const log = createLogger ( "Telegram" ) ;
2527
@@ -227,51 +229,122 @@ adk-claw pairing approve telegram ${code}
227229Code expires in 1 hour.` ;
228230}
229231
230- const commands : Record < string , CommandHandler > = {
231- "/start" : async ( chatId , userId ) => {
232- const config = getRawConfig ( ) ;
232+ /**
233+ * Format duration between a unix timestamp (seconds) and now
234+ */
235+ function formatDuration ( startTimestamp : number ) : string {
236+ const diffMs = Date . now ( ) - startTimestamp * 1000 ;
237+ const minutes = Math . floor ( diffMs / 60000 ) ;
238+ const hours = Math . floor ( minutes / 60 ) ;
239+
240+ if ( hours > 0 ) {
241+ return `${ hours } h ${ minutes % 60 } m` ;
242+ }
243+ return `${ minutes } m` ;
244+ }
245+
246+ /**
247+ * Create command handlers bound to ADK session lifecycle.
248+ * Uses sessionService.createSession / deleteSession and runner.setSession
249+ * to properly create/destroy ADK sessions instead of local state.
250+ */
251+ function createCommands ( deps : {
252+ runner : EnhancedRunner ;
253+ sessionService : BaseSessionService ;
254+ memoryService ?: MemoryService ;
255+ } ) : Record < string , CommandHandler > {
256+ const { runner, sessionService, memoryService } = deps ;
257+
258+ return {
259+ "/start" : async ( _chatId , userId ) => {
260+ const config = getRawConfig ( ) ;
233261
234- // Check if user is already allowed
235- if ( isAllowed ( "telegram" , userId ) ) {
236- return `👋 Welcome back!
262+ // Check if user is already allowed
263+ if ( isAllowed ( "telegram" , userId ) ) {
264+ return `👋 Welcome back!
237265
238266I'm ${ config . agent . name } , your personal AI assistant.
239267
240268Commands:
241269/new - Save session & start fresh
242270/reset - Clear session without saving
243271/help - Show available commands` ;
244- }
272+ }
245273
246- // User needs pairing
247- return getPairingResponse ( userId , undefined , chatId ) ;
248- } ,
274+ // User needs pairing
275+ return getPairingResponse ( userId , undefined , _chatId ) ;
276+ } ,
249277
250- "/new" : async ( chatId , _userId ) => {
251- const summary = await sessionManager . saveAndReset ( chatId ) ;
252- return `✅ Session saved to memory.
278+ "/new" : async ( _chatId , _userId ) => {
279+ const currentSession = runner . getSession ( ) ;
280+
281+ // Save current session to memory if it has events
282+ if ( memoryService && currentSession . events . length > 0 ) {
283+ const freshSession = await sessionService . getSession (
284+ currentSession . appName ,
285+ currentSession . userId ,
286+ currentSession . id ,
287+ ) ;
288+ if ( freshSession ) {
289+ await memoryService . addSessionToMemory ( freshSession ) ;
290+ }
291+ }
292+
293+ // Derive summary from ADK session events
294+ const eventCount = currentSession . events . length ;
295+ let summary = "No conversation to save." ;
296+ if ( eventCount > 0 ) {
297+ const firstTimestamp = currentSession . events [ 0 ] . timestamp ;
298+ const duration = formatDuration ( firstTimestamp ) ;
299+ summary = `${ eventCount } events over ${ duration } ` ;
300+ }
301+
302+ // Create a new ADK session and swap it into the runner
303+ const newSession = await sessionService . createSession (
304+ currentSession . appName ,
305+ currentSession . userId ,
306+ ) ;
307+ runner . setSession ( newSession ) ;
308+
309+ return `✅ Session saved to memory.
253310
254311📝 Summary: ${ summary }
255312
256313🆕 New session started!` ;
257- } ,
314+ } ,
258315
259- "/reset" : async ( chatId , _userId ) => {
260- sessionManager . reset ( chatId ) ;
261- return "🔄 Session cleared (not saved). Fresh start!" ;
262- } ,
316+ "/reset" : async ( _chatId , _userId ) => {
317+ const currentSession = runner . getSession ( ) ;
263318
264- "/help" : async ( _chatId , _userId ) => {
265- return `📚 Available commands:
319+ // Delete the old session without saving to memory
320+ await sessionService . deleteSession (
321+ currentSession . appName ,
322+ currentSession . userId ,
323+ currentSession . id ,
324+ ) ;
325+
326+ // Create a fresh ADK session
327+ const newSession = await sessionService . createSession (
328+ currentSession . appName ,
329+ currentSession . userId ,
330+ ) ;
331+ runner . setSession ( newSession ) ;
332+
333+ return "🔄 Session cleared (not saved). Fresh start!" ;
334+ } ,
335+
336+ "/help" : async ( _chatId , _userId ) => {
337+ return `📚 Available commands:
266338
267339/start - Start the bot and pair
268340/new - Save session & start fresh
269341/reset - Clear session without saving
270342/help - Show this help message
271343
272344Just send a message to chat with me!` ;
273- } ,
274- } ;
345+ } ,
346+ } ;
347+ }
275348
276349/**
277350 * Start the Telegram bot with MCP sampling
@@ -291,10 +364,8 @@ export async function startTelegramBot(): Promise<void> {
291364 channel : "telegram" ,
292365 } ) ;
293366
294- // Wire memory service into session manager for /new command
295- if ( memoryService ) {
296- sessionManager . setDeps ( memoryService , sessionService , session ) ;
297- }
367+ // Create command handlers bound to ADK session lifecycle
368+ const commands = createCommands ( { runner, sessionService, memoryService } ) ;
298369
299370 // Create sampling handler with command detection
300371 const samplingHandler = createSamplingHandler (
@@ -327,17 +398,9 @@ export async function startTelegramBot(): Promise<void> {
327398 registerUserCommands ( config . telegramBotToken , chatId ) ;
328399 }
329400
330- // Track in session
331- sessionManager . getOrCreate ( chatId , userId ) ;
332- sessionManager . addMessage ( chatId , "user" , messageText ) ;
333-
334- // Get response from agent
401+ // Get response from agent (ADK tracks events in session automatically)
335402 try {
336403 const response = await runner . ask ( messageText ) ;
337-
338- // Track response
339- sessionManager . addMessage ( chatId , "assistant" , response ) ;
340-
341404 return response ;
342405 } catch ( error ) {
343406 log . error (
0 commit comments