Add thread rename support

This commit is contained in:
Codex
2026-05-21 09:31:47 +00:00
parent ad61f7eeed
commit bc866d1224
7 changed files with 197 additions and 4 deletions

View File

@@ -180,6 +180,8 @@ func (b *Bot) handleCommand(ctx context.Context, message *Message, session store
return true, b.sendThreads(ctx, userID, chatID)
case "resume":
return true, b.resumeThread(ctx, userID, chatID, args)
case "rename":
return true, b.renameThread(ctx, userID, chatID, session, args)
case "fork":
return true, b.forkThread(ctx, userID, chatID, session)
case "archive":
@@ -212,6 +214,7 @@ func (b *Bot) sendHelp(ctx context.Context, chatID int64) error {
"/thread or /threads - list recent threads",
"/resume - choose a recent thread",
"/resume ID - resume a thread",
"/rename TITLE or /rename ID TITLE - rename a thread",
"/fork - fork the active thread",
"/archive [ID] - archive a thread",
"/status - show active settings",
@@ -295,9 +298,16 @@ func (b *Bot) resumeThreadByID(ctx context.Context, userID, chatID int64, id int
}
return err
}
if _, err := b.codex.ResumeThread(ctx, thread.CodexThreadID); err != nil {
resumed, err := b.codex.ResumeThread(ctx, thread.CodexThreadID)
if err != nil {
return b.sendError(ctx, chatID, "Could not resume Codex thread", err)
}
if resumed.Name != "" && normalizeThreadTitle(resumed.Name) != thread.Title {
thread.Title = normalizeThreadTitle(resumed.Name)
if err := b.store.RenameThread(ctx, userID, thread.ID, thread.Title); err != nil {
return err
}
}
if err := b.store.SetActiveThread(ctx, userID, thread.ID); err != nil {
return err
}
@@ -310,6 +320,50 @@ func (b *Bot) resumeThreadByID(ctx context.Context, userID, chatID int64, id int
return err
}
func (b *Bot) renameThread(ctx context.Context, userID, chatID int64, session store.Session, args []string) error {
if len(args) == 0 {
_, err := b.tg.SendMessage(ctx, chatID, "Use /rename TITLE for the active thread, or /rename THREAD_ID TITLE.", SendMessageOptions{})
return err
}
var thread store.Thread
titleArgs := args
var err error
if len(args) > 1 {
if id, parseErr := strconv.ParseInt(args[0], 10, 64); parseErr == nil {
thread, err = b.store.GetThreadByID(ctx, userID, id)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
_, sendErr := b.tg.SendMessage(ctx, chatID, "Thread not found.", SendMessageOptions{})
return sendErr
}
return err
}
titleArgs = args[1:]
}
}
if thread.ID == 0 {
thread, err = b.activeThread(ctx, userID, session)
}
if err != nil {
return b.sendNoActiveThread(ctx, chatID, err)
}
title := normalizeThreadTitle(strings.Join(titleArgs, " "))
if title == "" {
_, err := b.tg.SendMessage(ctx, chatID, "Thread title cannot be empty.", SendMessageOptions{})
return err
}
if err := b.codex.SetThreadName(ctx, thread.CodexThreadID, title); err != nil {
return b.sendError(ctx, chatID, "Could not rename Codex thread", err)
}
if err := b.store.RenameThread(ctx, userID, thread.ID, title); err != nil {
return err
}
_, err = b.tg.SendMessage(ctx, chatID, fmt.Sprintf("Renamed thread ID %d: %s", thread.ID, title), SendMessageOptions{})
return err
}
func (b *Bot) forkThread(ctx context.Context, userID, chatID int64, session store.Session) error {
thread, err := b.activeThread(ctx, userID, session)
if err != nil {
@@ -319,7 +373,11 @@ func (b *Bot) forkThread(ctx context.Context, userID, chatID int64, session stor
if err != nil {
return b.sendError(ctx, chatID, "Could not fork Codex thread", err)
}
local, err := b.store.CreateThread(ctx, userID, forked.ID, thread.WorkspaceID, "fork of #"+strconv.FormatInt(thread.ID, 10))
title := forked.Name
if title == "" {
title = "fork of ID " + strconv.FormatInt(thread.ID, 10)
}
local, err := b.store.CreateThread(ctx, userID, forked.ID, thread.WorkspaceID, title)
if err != nil {
return err
}
@@ -654,7 +712,10 @@ func (b *Bot) createNewThread(ctx context.Context, userID, chatID int64, session
if err != nil {
return store.Thread{}, store.Workspace{}, b.sendError(ctx, chatID, "Could not start Codex thread", err)
}
title := codexThread.Preview
title := codexThread.Name
if title == "" {
title = codexThread.Preview
}
if title == "" {
title = workspace.Label
}
@@ -1167,6 +1228,17 @@ func (b *Bot) handleCodexNotification(ctx context.Context, event codexapp.Event)
}
return b.completeTurnOutput(ctx, params.ThreadID)
}
case "thread/name/updated":
var params struct {
ThreadID string `json:"threadId"`
ThreadName *string `json:"threadName"`
}
if err := json.Unmarshal(event.Params, &params); err != nil {
return err
}
if params.ThreadID != "" && params.ThreadName != nil {
return b.store.RenameThreadByCodexID(ctx, params.ThreadID, normalizeThreadTitle(*params.ThreadName))
}
case "serverRequest/resolved":
var params struct {
ThreadID string `json:"threadId"`
@@ -1476,6 +1548,15 @@ func resumeThreadMarkup(threads []store.Thread, page int, hasNext bool) *InlineK
return &InlineKeyboardMarkup{InlineKeyboard: keyboard}
}
func normalizeThreadTitle(title string) string {
title = strings.Join(strings.Fields(title), " ")
runes := []rune(title)
if len(runes) > 80 {
title = string(runes[:80])
}
return title
}
func threadDisplayTitle(thread store.Thread) string {
title := strings.Join(strings.Fields(thread.Title), " ")
if title == "" {