Skip to content

feat(threads): auto-generate first-turn thread titles#1375

Merged
juliusmarminge merged 27 commits intopingdotgg:mainfrom
maria-rcks:feat/title-summaries-2
Mar 29, 2026
Merged

feat(threads): auto-generate first-turn thread titles#1375
juliusmarminge merged 27 commits intopingdotgg:mainfrom
maria-rcks:feat/title-summaries-2

Conversation

@maria-rcks
Copy link
Copy Markdown
Collaborator

@maria-rcks maria-rcks commented Mar 24, 2026

Closes #990

What Changed

  • use the existing text generation model setting for first-turn thread title generation
  • auto-generate a thread title from the first user prompt on the server
  • keep title generation off the critical path so the turn starts immediately
  • rename the settings copy from "Git writing model" to "Text generation model"
  • reuse the same text generation model for automatic worktree branch naming

Why

Thread titles were still just truncated prompt text.

This makes new threads easier to scan while keeping the model selection simpler and reusing the same text generation path we already have for commit, PR, and branch text.

UI Changes

Settings now says Text generation model

image

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

Medium Risk
Adds new server-side first-turn behavior and new TextGeneration.generateThreadTitle flows across orchestration, contracts, and providers; risk is mainly around unintended title overwrites and provider CLI/output edge cases.

Overview
Auto-generates thread titles on the first user turn. ProviderCommandReactor now forks background work on first-turn start to (a) generate a sidebar-safe thread title via TextGeneration.generateThreadTitle and update thread.meta only when the current title is a replaceable placeholder (default or matches a client-provided seed), and (b) reuse the same configured text-generation model when auto-renaming temporary worktree branches.

Adds end-to-end support for thread title generation: new buildThreadTitlePrompt, sanitizeThreadTitle (single-line, quote/whitespace cleanup, 50-char ellipsis, fallback to "New thread"), provider implementations in both Codex and Claude layers, routing support, and expanded tests.

Updates the command/event contract so clients send a titleSeed in thread.turn.start, which is propagated into thread.turn-start-requested for consistent placeholder detection. Also replaces the web-only truncateTitle with a shared packages/shared truncate utility and updates the web client to send titleSeed on turn start.

Written by Cursor Bugbot for commit 4e07295. This will update automatically on new commits. Configure here.

Note

Auto-generate thread titles on the first user turn

  • On the first user message in a thread, ProviderCommandReactor now forks a title generation effect alongside branch name generation, calling TextGeneration.generateThreadTitle and updating thread.meta if the current title is the default placeholder or matches the client-provided titleSeed.
  • Both ClaudeTextGeneration and CodexTextGeneration implement generateThreadTitle using a new buildThreadTitlePrompt helper in Prompts.ts and a sanitizeThreadTitle util that trims, strips quotes, truncates to 50 chars, and falls back to "New thread".
  • The web client passes a titleSeed (derived from the draft message) in thread.turn.start commands so the server can avoid overwriting a user-set title that matches the seed.
  • A shared truncate utility in packages/shared/src/String.ts replaces the former per-app truncateTitle.
  • Behavioral Change: ThreadTurnStartRequestedPayload gains an optional titleSeed field and loses assistantDeliveryMode; consumers decoding this event type will need to handle the schema change.

Macroscope summarized 4e07295.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 24, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 3f906ecf-37ff-4129-89d3-67513c543b68

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Mar 24, 2026
@maria-rcks maria-rcks force-pushed the feat/title-summaries-2 branch from b256e14 to 5335f66 Compare March 24, 2026 20:32
@maria-rcks maria-rcks force-pushed the feat/title-summaries-2 branch from 5335f66 to 6e727cd Compare March 25, 2026 12:59
@maria-rcks maria-rcks force-pushed the feat/title-summaries-2 branch from af6a180 to aefb74c Compare March 26, 2026 03:04
@maria-rcks maria-rcks force-pushed the feat/title-summaries-2 branch from aefb74c to 4ad369f Compare March 26, 2026 22:49
)(function* () {
return yield* new TextGenerationError({
operation: "generateThreadTitle",
detail: "Thread title generation is only supported through Codex.",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Comment on lines +406 to +425
const promptSections = [
"You write concise thread titles for coding conversations.",
"Return a JSON object with key: title.",
"Rules:",
"- Title should summarize the user's request, not restate it verbatim.",
"- Keep it short and specific (3-8 words).",
"- Avoid quotes, filler, prefixes, and trailing punctuation.",
"- If images are attached, use them as primary context for visual/UI issues.",
"",
"User message:",
limitSection(input.message, 8_000),
];
if (attachmentLines.length > 0) {
promptSections.push(
"",
"Attachment metadata:",
limitSection(attachmentLines.join("\n"), 4_000),
);
}
const prompt = promptSections.join("\n");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prompts.ts?

@maria-rcks maria-rcks force-pushed the feat/title-summaries-2 branch from ff72a3c to 451f55b Compare March 28, 2026 21:37
@maria-rcks maria-rcks force-pushed the feat/title-summaries-2 branch from 80461bc to cb2929e Compare March 28, 2026 22:28
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

const {
title = "T3 Code",
description = "T3 Code — The best way to code with AI.",
description = "T3 Code — A great way to code with agents.",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🥲

@maria-rcks
Copy link
Copy Markdown
Collaborator Author

sorry codex went rouge


<Layout>
<h1 class="tagline">T3 Code is the best way to code with AI.</h1>
<h1 class="tagline">T3 Code is a great way to code with agents.</h1>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🥲

also very unrelated to the PR

Comment on lines +2999 to +3000
textGenerationModel: selectedTextGenerationModel,
assistantDeliveryMode: settings.enableAssistantStreaming ? "streaming" : "buffered",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these settings are server authorative now, you don't need to send them from client. the text generaiton service reads the server settings to find the right model

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: name module String.ts and export just truncate. nothing title related here...

Comment on lines +224 to +227
Layer.succeed(TextGeneration, {
generateBranchName,
generateThreadTitle,
} as unknown as TextGenerationShape),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Layer.mock(TextGeneration, {}) if you don't wanna provide a full service implementation

Comment on lines +79 to +113
function buildReplaceableThreadTitles(input: {
readonly messageText: string;
readonly attachments?: ReadonlyArray<ChatAttachment>;
readonly titleSeed?: string;
}): ReadonlySet<string> {
const titles = new Set<string>([DEFAULT_THREAD_TITLE]);
const trimmedTitleSeed = input.titleSeed?.trim();

if (trimmedTitleSeed) {
titles.add(trimmedTitleSeed);
return titles;
}

const trimmedMessage = input.messageText.trim();

if (trimmedMessage.length > 0) {
titles.add(truncateTitle(trimmedMessage));
return titles;
}

const firstImageAttachment = input.attachments?.find((attachment) => attachment.type === "image");
if (firstImageAttachment) {
titles.add(truncateTitle(`Image: ${firstImageAttachment.name}`));
}

return titles;
}

function isReplaceableThreadTitle(
currentTitle: string,
input: {
readonly messageText: string;
readonly attachments?: ReadonlyArray<ChatAttachment>;
readonly titleSeed?: string;
},
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't quite follow what this is meant to do

@juliusmarminge juliusmarminge merged commit 5513845 into pingdotgg:main Mar 29, 2026
11 checks passed
@maria-rcks maria-rcks deleted the feat/title-summaries-2 branch March 29, 2026 00:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: support ai generated thread names

3 participants