@@ -161,13 +161,13 @@ func telegramCmd(args []string) error {
161161 // block the main update processing loop. The TelegramApprover blocks waiting
162162 // for inline keyboard callbacks, which arrive via the main loop — only async
163163 // dispatch prevents deadlock.
164- handler .OnTextMessage = func (chatID int64 , text string ) (string , error ) {
165- go handleChatMessage (chatID , text , bot , handler , sessionManager ,
164+ handler .OnTextMessage = func (chatID int64 , messageID int , text string ) (string , error ) {
165+ go handleChatMessage (chatID , messageID , text , bot , handler , sessionManager ,
166166 resolved , systemMessage , handlerLog )
167167 return "" , nil
168168 }
169169
170- handler .OnCommand = func (chatID int64 , cmdName string , argsStr string ) (string , error ) {
170+ handler .OnCommand = func (chatID int64 , messageID int , cmdName string , argsStr string ) (string , error ) {
171171 cmd := telegram .FindCommand (cmdName )
172172 if cmd == nil {
173173 return fmt .Sprintf ("Unknown command: /%s" , cmdName ), nil
@@ -300,7 +300,7 @@ func telegramCmd(args []string) error {
300300 "Use your write_file tool to save the plan." ,
301301 description , slug ,
302302 )
303- go handleChatMessage (chatID , prompt , bot , handler , sessionManager ,
303+ go handleChatMessage (chatID , messageID , prompt , bot , handler , sessionManager ,
304304 resolved , systemMessage , handlerLog )
305305 return fmt .Sprintf ("📝 *Planning* `%s`…\n \n _Generating plan for: %s_" , slug , description ), nil
306306 }
@@ -400,14 +400,14 @@ func telegramCmd(args []string) error {
400400 return "" , nil // approval callbacks are routed by the approver
401401 }
402402
403- handler .OnVoiceMessage = func (chatID int64 , fileID string ) (string , error ) {
404- go handleChatMessage (chatID , "[voice message: " + fileID + "]" ,
403+ handler .OnVoiceMessage = func (chatID int64 , messageID int , fileID string ) (string , error ) {
404+ go handleChatMessage (chatID , messageID , "[voice message: " + fileID + "]" ,
405405 bot , handler , sessionManager , resolved , systemMessage , handlerLog )
406406 return "" , nil
407407 }
408408
409- handler .OnPhotoMessage = func (chatID int64 , fileIDs []string ) (string , error ) {
410- go handleChatMessage (chatID , "[photo message: " + strings .Join (fileIDs , "," )+ "]" ,
409+ handler .OnPhotoMessage = func (chatID int64 , messageID int , fileIDs []string ) (string , error ) {
410+ go handleChatMessage (chatID , messageID , "[photo message: " + strings .Join (fileIDs , "," )+ "]" ,
411411 bot , handler , sessionManager , resolved , systemMessage , handlerLog )
412412 return "" , nil
413413 }
@@ -541,6 +541,7 @@ func spawnChild() error {
541541// back the response. Each chat gets its own TelegramApprover instance.
542542func handleChatMessage (
543543 chatID int64 ,
544+ messageID int ,
544545 text string ,
545546 bot * telegram.Bot ,
546547 handler * telegram.Handler ,
@@ -563,7 +564,7 @@ func handleChatMessage(
563564 // Get or create the session for this chat.
564565 cs , err := sessionManager .GetOrCreate (chatID )
565566 if err != nil {
566- reportError (bot , chatID , "Failed to create session: " + err .Error ())
567+ reportError (bot , chatID , messageID , "Failed to create session: " + err .Error ())
567568 return
568569 }
569570
@@ -586,7 +587,7 @@ func handleChatMessage(
586587 // exhausted — avoids burning an API call just to be rejected.
587588 if resolved .Telegram .DailyTokenBudget > 0 {
588589 if err := bot .CheckDailyBudget (1 ); err != nil {
589- reportError (bot , chatID , fmt .Sprintf (
590+ reportError (bot , chatID , messageID , fmt .Sprintf (
590591 "Daily token budget exhausted: %v. " +
591592 "The budget resets at midnight UTC. " +
592593 "Set daily_token_budget to 0 in config for unlimited usage." ,
@@ -660,7 +661,7 @@ func handleChatMessage(
660661 },
661662 }
662663 if _ , err := bot .SendMessage (chatID , "❓ " + question ,
663- & telegram.SendOpts {ReplyMarkup : replyMarkup , ParseMode : "Markdown" }); err != nil {
664+ & telegram.SendOpts {ReplyMarkup : replyMarkup , ParseMode : "Markdown" , ReplyToMessageID : messageID }); err != nil {
664665 return "" , fmt .Errorf ("clarify: send message: %w" , err )
665666 }
666667
@@ -746,14 +747,14 @@ func handleChatMessage(
746747 switch event .Type {
747748 case "loaded" :
748749 names := strings .Join (event .Skills , ", " )
749- go bot .SendMessage (chatID , "📚 Loaded skill: " + names , nil )
750+ go bot .SendMessage (chatID , "📚 Loaded skill: " + names , & telegram. SendOpts { ReplyToMessageID : messageID } )
750751 case "autoloaded" :
751752 names := strings .Join (event .Skills , ", " )
752- go bot .SendMessage (chatID , "📚 Auto-loaded skills: " + names , nil )
753+ go bot .SendMessage (chatID , "📚 Auto-loaded skills: " + names , & telegram. SendOpts { ReplyToMessageID : messageID } )
753754 case "saved" :
754- go bot .SendMessage (chatID , fmt .Sprintf ("✓ Saved skill %q" , event .SkillName ), nil )
755+ go bot .SendMessage (chatID , fmt .Sprintf ("✓ Saved skill %q" , event .SkillName ), & telegram. SendOpts { ReplyToMessageID : messageID } )
755756 case "deleted" :
756- go bot .SendMessage (chatID , fmt .Sprintf ("✗ Deleted skill %q" , event .SkillName ), nil )
757+ go bot .SendMessage (chatID , fmt .Sprintf ("✗ Deleted skill %q" , event .SkillName ), & telegram. SendOpts { ReplyToMessageID : messageID } )
757758 case "suggested" :
758759 replyMarkup := & telegram.InlineKeyboardMarkup {
759760 InlineKeyboard : [][]telegram.InlineKeyboardButton {
@@ -779,14 +780,14 @@ func handleChatMessage(
779780 msg += fmt .Sprintf ("\n \n ```\n %s\n ```" , preview )
780781 }
781782 go bot .SendMessage (chatID , msg ,
782- & telegram.SendOpts {ReplyMarkup : replyMarkup , ParseMode : "Markdown" })
783+ & telegram.SendOpts {ReplyMarkup : replyMarkup , ParseMode : "Markdown" , ReplyToMessageID : messageID })
783784 }
784785 },
785786 }
786787
787788 agent , err := odek .New (agentCfg )
788789 if err != nil {
789- reportError (bot , chatID , "Failed to create agent: " + err .Error ())
790+ reportError (bot , chatID , messageID , "Failed to create agent: " + err .Error ())
790791 return
791792 }
792793 defer agent .Close ()
@@ -803,7 +804,7 @@ func handleChatMessage(
803804 // Run the agent with the full message history (multi-turn).
804805 response , updatedMessages , err := agent .RunWithMessages (agentCtx , cs .Messages )
805806 if err != nil {
806- reportError (bot , chatID , "Agent error: " + err .Error ())
807+ reportError (bot , chatID , messageID , "Agent error: " + err .Error ())
807808 return
808809 }
809810
@@ -819,7 +820,7 @@ func handleChatMessage(
819820 "Further agent runs may be blocked until the daily budget resets. " +
820821 "Use `/stats` to check current usage." ,
821822 err ,
822- ), & telegram.SendOpts {ParseMode : telegram .ParseModeMarkdownV2 })
823+ ), & telegram.SendOpts {ParseMode : telegram .ParseModeMarkdownV2 , ReplyToMessageID : messageID })
823824 }
824825 }
825826
@@ -832,7 +833,7 @@ func handleChatMessage(
832833
833834 // Send the response, then append compact stats as a separate message.
834835 if response != "" {
835- handler .SendResponse (chatID , response )
836+ handler .SendResponse (chatID , response , messageID )
836837
837838 // Send run stats as a separate message directly via Bot.SendMessage
838839 // (bypassing SendResponse/FormatResponse) so MarkdownV2 backtick code
@@ -844,10 +845,13 @@ func handleChatMessage(
844845
845846 statsLine := formatTelegramStats (runInfo , toolList )
846847 if _ , err := bot .SendMessage (chatID , statsLine , & telegram.SendOpts {
847- ParseMode : telegram .ParseModeMarkdownV2 ,
848+ ParseMode : telegram .ParseModeMarkdownV2 ,
849+ ReplyToMessageID : messageID ,
848850 }); err != nil {
849851 // Fallback: send as plain text so the info isn't lost
850- if _ , err2 := bot .SendMessage (chatID , statsLine , nil ); err2 != nil {
852+ if _ , err2 := bot .SendMessage (chatID , statsLine , & telegram.SendOpts {
853+ ReplyToMessageID : messageID ,
854+ }); err2 != nil {
851855 fmt .Fprintf (os .Stderr , "odek telegram: stats send fallback failed: %v (orig: %v)\n " , err2 , err )
852856 }
853857 }
@@ -991,9 +995,9 @@ func formatStopSummary(info loop.IterationInfo) string {
991995}
992996
993997// reportError sends an error message to the given chat and logs to stderr.
994- func reportError (bot * telegram.Bot , chatID int64 , msg string ) {
998+ func reportError (bot * telegram.Bot , chatID int64 , messageID int , msg string ) {
995999 fmt .Fprintf (os .Stderr , "odek telegram: %s\n " , msg )
996- if _ , err := bot .SendMessage (chatID , "❌ " + msg , nil ); err != nil {
1000+ if _ , err := bot .SendMessage (chatID , "❌ " + msg , & telegram. SendOpts { ReplyToMessageID : messageID } ); err != nil {
9971001 fmt .Fprintf (os .Stderr , "odek telegram: send error message: %v\n " , err )
9981002 }
9991003}
0 commit comments