Prevent stale approval buttons

This commit is contained in:
Codex
2026-05-24 03:23:58 +00:00
parent fd80780581
commit e85d0eb928
4 changed files with 91 additions and 9 deletions

View File

@@ -329,7 +329,7 @@ func (b *Bot) sendResumeChoices(ctx context.Context, userID, chatID int64, page
text := resumeThreadListText(threads, page)
markup := resumeThreadMarkup(threads, page, hasNext)
if messageID != 0 {
_, err := b.tg.EditMessageText(ctx, chatID, messageID, EscapeHTML(text), EditMessageTextOptions{ParseMode: "HTML", ReplyMarkup: markup})
_, err := b.tg.EditMessageText(ctx, chatID, messageID, EscapeHTML(text), EditMessageTextOptions{ParseMode: "HTML", ReplyMarkup: editReplyMarkup(markup)})
return err
}
_, err = b.tg.SendMessage(ctx, chatID, EscapeHTML(text), SendMessageOptions{ParseMode: "HTML", ReplyMarkup: markup})
@@ -366,7 +366,7 @@ func (b *Bot) resumeThreadByID(ctx context.Context, userID, chatID int64, id int
}
text := fmt.Sprintf("Active thread ID %d: %s", thread.ID, threadDisplayTitle(thread))
if messageID != 0 {
_, err = b.tg.EditMessageText(ctx, chatID, messageID, EscapeHTML(text), EditMessageTextOptions{ParseMode: "HTML"})
_, err = b.tg.EditMessageText(ctx, chatID, messageID, EscapeHTML(text), EditMessageTextOptions{ParseMode: "HTML", ReplyMarkup: clearInlineKeyboardMarkup()})
return err
}
_, err = b.tg.SendMessage(ctx, chatID, EscapeHTML(text), SendMessageOptions{ParseMode: "HTML"})
@@ -1149,7 +1149,7 @@ func (b *Bot) handleApprovalCallback(ctx context.Context, callback *CallbackQuer
return err
}
updated := b.resolveApprovalMessageHTML(approval, decision)
_, err = b.tg.EditMessageText(ctx, callback.Message.Chat.ID, callback.Message.MessageID, updated, EditMessageTextOptions{ParseMode: "HTML"})
_, err = b.tg.EditMessageText(ctx, callback.Message.Chat.ID, callback.Message.MessageID, updated, EditMessageTextOptions{ParseMode: "HTML", ReplyMarkup: clearInlineKeyboardMarkup()})
return ignoreTelegramMessageNotModified(err)
}
@@ -1710,6 +1710,9 @@ func (b *Bot) handleCodexServerRequest(ctx context.Context, event codexapp.Event
if err != nil {
return err
}
if approval.Status != "pending" {
return nil
}
text := renderApprovalHTML(kind, event.Params, "")
markup := approvalMarkup(approval.ID)
if msg, ok, err := b.attachApprovalToToolMessage(ctx, params.ThreadID, params.ItemID, text, markup); err != nil {
@@ -2023,7 +2026,7 @@ func (b *Bot) upsertToolMessage(ctx context.Context, threadID, itemID, htmlText
msgID := tool.messageID
markup := tool.approvalMarkup
b.mu.Unlock()
_, err := b.tg.EditMessageText(ctx, msgChatID, msgID, combined, EditMessageTextOptions{ParseMode: "HTML", ReplyMarkup: markup})
_, err := b.tg.EditMessageText(ctx, msgChatID, msgID, combined, EditMessageTextOptions{ParseMode: "HTML", ReplyMarkup: editReplyMarkup(markup)})
if err := ignoreTelegramMessageNotModified(err); err != nil {
return err
}
@@ -2078,7 +2081,7 @@ func (b *Bot) attachApprovalToToolMessage(ctx context.Context, threadID, itemID,
msg := Message{MessageID: tool.messageID, Chat: Chat{ID: tool.chatID}}
b.mu.Unlock()
_, err := b.tg.EditMessageText(ctx, msg.Chat.ID, msg.MessageID, combined, EditMessageTextOptions{ParseMode: "HTML", ReplyMarkup: markup})
_, err := b.tg.EditMessageText(ctx, msg.Chat.ID, msg.MessageID, combined, EditMessageTextOptions{ParseMode: "HTML", ReplyMarkup: editReplyMarkup(markup)})
if err := ignoreTelegramMessageNotModified(err); err != nil {
b.clearToolApproval(threadID, itemID)
b.logger.Printf("edit tool approval message %s/%s: %v", threadID, itemID, err)
@@ -2653,6 +2656,17 @@ func (b *Bot) rememberSettingsMessage(ctx context.Context, userID int64, chatID
}
}
func clearInlineKeyboardMarkup() *InlineKeyboardMarkup {
return &InlineKeyboardMarkup{InlineKeyboard: [][]InlineKeyboardButton{}}
}
func editReplyMarkup(markup *InlineKeyboardMarkup) *InlineKeyboardMarkup {
if markup != nil {
return markup
}
return clearInlineKeyboardMarkup()
}
func approvalMarkup(id int64) *InlineKeyboardMarkup {
return &InlineKeyboardMarkup{InlineKeyboard: [][]InlineKeyboardButton{
{

View File

@@ -84,6 +84,25 @@ func TestApprovalResponseForPermissions(t *testing.T) {
}
}
func TestEditReplyMarkupClearsInlineKeyboard(t *testing.T) {
markup := editReplyMarkup(nil)
if markup == nil {
t.Fatal("nil edit markup would leave Telegram buttons unchanged")
}
raw, err := json.Marshal(markup)
if err != nil {
t.Fatal(err)
}
if string(raw) != `{"inline_keyboard":[]}` {
t.Fatalf("clear markup JSON = %s", raw)
}
existing := approvalMarkup(7)
if editReplyMarkup(existing) != existing {
t.Fatal("non-nil markup should be preserved")
}
}
func TestParseCommand(t *testing.T) {
name, args, ok := parseCommand("/resume@my_bot 123")
if !ok || name != "resume" || len(args) != 1 || args[0] != "123" {