Unified · Split
| @@ -150,28 +150,12 @@ export interface BranchNamePromptInput { · 149 unmodified lines | ||
| 150 | 150 | interface PromptFromMessageInput { |
| 151 | 151 | instruction: string; |
| 152 | 152 | responseShape: string; |
| 153 | − guidance?: ReadonlyArray<string> | undefined; | |
| 154 | − rulesLabel?: string | undefined; | |
| 155 | 153 | rules: ReadonlyArray<string>; |
| 156 | − afterRules?: ReadonlyArray<string> | undefined; | |
| 157 | 154 | message: string; |
| 158 | − messageLabel?: string | undefined; | |
| 159 | − preserveMessageEnd?: boolean | undefined; | |
| 160 | 155 | attachments?: ReadonlyArray<ChatAttachment> | undefined; |
| 161 | 156 | additionalInstructions?: string | undefined; |
| 162 | 157 | } |
| 163 | 158 | |
| 164 | −function preserveMessageEnd(message: string): string { | |
| 165 | − const alreadyTruncated = message.startsWith(EARLIER_CONTENT_TRUNCATION_MARKER); | |
| 166 | − const contents = alreadyTruncated | |
| 167 | − ? message.slice(EARLIER_CONTENT_TRUNCATION_MARKER.length) | |
| 168 | − : message; | |
| 169 | − if (!alreadyTruncated && contents.length <= 8_000) { | |
| 170 | − return contents; | |
| 171 | − } | |
| 172 | − return `${EARLIER_CONTENT_TRUNCATION_MARKER}${contents.slice(-8_000)}`; | |
| 173 | −} | |
| 174 | − | |
| 175 | 159 | function buildPromptFromMessage(input: PromptFromMessageInput): string { |
| 176 | 160 | const attachmentLines = (input.attachments ?? []).map( |
| 177 | 161 | (attachment) => `- ${attachment.name} (${attachment.mimeType}, ${attachment.sizeBytes} bytes)`, |
| @@ -180,15 +164,11 @@ function buildPromptFromMessage(input: PromptFromMessageInput): string { · 2 unmodified lines | ||
| 180 | 164 | const promptSections = [ |
| 181 | 165 | input.instruction, |
| 182 | 166 | input.responseShape, |
| 183 | − ...(input.guidance ?? []), | |
| 184 | − input.rulesLabel ?? "Rules:", | |
| 167 | + "Rules:", | |
| 185 | 168 | ...input.rules.map((rule) => `- ${rule}`), |
| 186 | − ...(input.afterRules ?? []), | |
| 187 | 169 | "", |
| 188 | − `${input.messageLabel ?? "User message"}:`, | |
| 189 | − input.preserveMessageEnd | |
| 190 | − ? preserveMessageEnd(input.message) | |
| 191 | − : limitSection(input.message, 8_000), | |
| 170 | + "User message:", | |
| 171 | + limitSection(input.message, 8_000), | |
| 192 | 172 | ...policyInstruction(input.additionalInstructions), |
| 193 | 173 | ]; |
| 194 | 174 | if (attachmentLines.length > 0) { |
| @@ -234,88 +214,102 @@ export interface ThreadTitlePromptInput { · 39 unmodified lines | ||
| 234 | 214 | policy?: TextGenerationPolicy | undefined; |
| 235 | 215 | } |
| 236 | 216 | |
| 217 | +// Keep shared editorial rules in these two prompts in sync. Regeneration | |
| 218 | +// intentionally adds guidance for thread history and the previous title. | |
| 219 | +const INITIAL_THREAD_TITLE_PROMPT = `Generate a title that will help the user recognize this T3 Code thread weeks later. | |
| 220 | +Return JSON with exactly one key: title. | |
| 221 | + | |
| 222 | +Before answering, silently reduce the request to: | |
| 223 | +- Subject: What system, feature, or problem is this really about? | |
| 224 | +- Outcome: What does the user ultimately want to understand or change? | |
| 225 | +- Incidental instructions: What only describes how the agent should do the work? | |
| 226 | + | |
| 227 | +Title the subject and outcome. Discard incidental instructions. | |
| 228 | + | |
| 229 | +Editorial rules: | |
| 230 | +- 3-8 words, fewer than 40 characters. | |
| 231 | +- Use a compact noun phrase or clear action phrase. | |
| 232 | +- Capture the umbrella goal when the request lists several symptoms or steps. | |
| 233 | +- Name the product change, not the mock, plan, report, branch, or PR used to produce it. | |
| 234 | +- Models, subagents, tools, output formats, and monitoring instructions do not belong in the title unless they are themselves the topic. | |
| 235 | +- For reviews, name what is being reviewed and the relevant concern. Avoid generic titles such as "Review PR 123" when linked or attached context reveals the subject. | |
| 236 | +- For research, name the question domain rather than the requested research process. | |
| 237 | +- Do not claim the work is complete. | |
| 238 | +- Do not copy and truncate the user's message. | |
| 239 | +- Avoid project names already visible in the UI, quotes, labels, filler, and trailing punctuation. | |
| 240 | +- Use attached images as primary context for UI issues. | |
| 241 | +- When a URL or attachment is the only source of the subject, use available tools to inspect it. If it cannot be resolved, remain accurate rather than guessing.`; | |
| 242 | + | |
| 243 | +function regenerateThreadTitlePrompt(previousTitle: string): string { | |
| 244 | + return `Regenerate the title for an existing T3 Code thread so the user can recognize it weeks later. | |
| 245 | +The previous title was ${JSON.stringify(previousTitle)}. | |
| 246 | +Return JSON with exactly one key: title. | |
| 247 | + | |
| 248 | +Determine the title in this order: | |
| 249 | +1. Read the USER messages first. Identify the latest explicit durable goal. The original subject remains the subject until the user clearly changes what the thread is about. | |
| 250 | +2. Use ASSISTANT messages to resolve vague links, unnamed code, and discovered product nouns. Do not promote one assistant finding into the thread subject unless the user adopts it as a new goal. | |
| 251 | +3. Compare that subject with the previous title. Preserve accurate scope words, especially when earlier content is truncated. Replace the previous title when it is generic, artifact-based, a completion update, or contradicted by the thread. | |
| 252 | +4. Title the durable subject and desired outcome, not the current workflow state. | |
| 253 | + | |
| 254 | +Editorial rules: | |
| 255 | +- 3-8 words, fewer than 40 characters. | |
| 256 | +- Use a compact noun phrase or clear action phrase. | |
| 257 | +- Preserve the umbrella subject when later messages focus on one finding, provider, platform, or implementation detail. | |
| 258 | +- A thread progressing through research, planning, implementation, review, CI, merge, and monitoring has usually not changed subjects. | |
| 259 | +- Ignore deliverables and operations such as mocks, plans, HTML, branches, PRs, tests, CI, commits, merging, and monitoring unless they are the actual topic. | |
| 260 | +- Models, subagents, tools, output formats, and monitoring instructions do not belong in the title unless they are themselves the topic. | |
| 261 | +- Treat final operational follow-ups and assistant completion summaries as weak evidence of subject. | |
| 262 | +- For reviews, name the reviewed feature or system and its durable concern, not one finding from the review. | |
| 263 | +- For research, name the question domain rather than the research process. | |
| 264 | +- Do not claim the work is complete. | |
| 265 | +- Do not copy and truncate a thread message. | |
| 266 | +- Avoid project names already visible in the UI, PR numbers, quotes, labels, filler, and trailing punctuation. | |
| 267 | +- Use attached images as primary context for UI issues. | |
| 268 | +- When a URL or attachment is the only source of the subject, use available tools to inspect it. If it cannot be resolved, remain accurate rather than guessing. | |
| 269 | +- Return a meaningfully improved title, not a cosmetic paraphrase of the previous title. | |
| 270 | + | |
| 271 | +Examples of the distinction: | |
| 272 | +- A subagent-monitoring review that finds a Codex roster bug remains "Review Subagent Monitoring Risks," not "Codex Roster Bug Review." | |
| 273 | +- A vague failing-test request later identified as a lazy thread-feed mismatch becomes "Fix Lazy Thread Feed Test," not "Prevent Mobile Feed Regressions." | |
| 274 | +- A QR-sharing overhaul that ends with CI and merge work remains about QR sharing, not the PR lifecycle.`; | |
| 275 | +} | |
| 276 | + | |
| 277 | +function preserveMessageEnd(message: string): string { | |
| 278 | + const alreadyTruncated = message.startsWith(EARLIER_CONTENT_TRUNCATION_MARKER); | |
| 279 | + const contents = alreadyTruncated | |
| 280 | + ? message.slice(EARLIER_CONTENT_TRUNCATION_MARKER.length) | |
| 281 | + : message; | |
| 282 | + if (!alreadyTruncated && contents.length <= 8_000) { | |
| 283 | + return contents; | |
| 284 | + } | |
| 285 | + return `${EARLIER_CONTENT_TRUNCATION_MARKER}${contents.slice(-8_000)}`; | |
| 286 | +} | |
| 287 | + | |
| 288 | +function threadTitlePromptSuffix(input: ThreadTitlePromptInput): string { | |
| 289 | + const additionalInstructions = policyInstruction(input.policy?.threadTitleInstructions); | |
| 290 | + const attachmentLines = (input.attachments ?? []).map( | |
| 291 | + (attachment) => `- ${attachment.name} (${attachment.mimeType}, ${attachment.sizeBytes} bytes)`, | |
| 292 | + ); | |
| 293 | + | |
| 294 | + let suffix = ""; | |
| 295 | + if (additionalInstructions.length > 0) { | |
| 296 | + suffix = `\n${additionalInstructions.join("\n")}`; | |
| 297 | + } | |
| 298 | + if (attachmentLines.length > 0) { | |
| 299 | + suffix += `\n\nAttachment metadata:\n${limitSection(attachmentLines.join("\n"), 4_000)}`; | |
| 300 | + } | |
| 301 | + return suffix; | |
| 302 | +} | |
| 303 | + | |
| 237 | 304 | export function buildThreadTitlePrompt(input: ThreadTitlePromptInput) { |
| 238 | − const isRegeneration = input.previousTitle !== undefined; | |
| 239 | − const prompt = buildPromptFromMessage({ | |
| 240 | − instruction: isRegeneration | |
| 241 | − ? [ | |
| 242 | − "Regenerate the title for an existing T3 Code thread so the user can recognize it weeks later.", | |
| 243 | − `The previous title was ${JSON.stringify(input.previousTitle)}.`, | |
| 244 | − ].join("\n") | |
| 245 | − : "Generate a title that will help the user recognize this T3 Code thread weeks later.", | |
| 246 | − responseShape: "Return JSON with exactly one key: title.", | |
| 247 | − guidance: isRegeneration | |
| 248 | − ? [ | |
| 249 | − "", | |
| 250 | − "Determine the title in this order:", | |
| 251 | − "1. Read the USER messages first. Identify the latest explicit durable goal. The original subject remains the subject until the user clearly changes what the thread is about.", | |
| 252 | − "2. Use ASSISTANT messages to resolve vague links, unnamed code, and discovered product nouns. Do not promote one assistant finding into the thread subject unless the user adopts it as a new goal.", | |
| 253 | − "3. Compare that subject with the previous title. Preserve accurate scope words, especially when earlier content is truncated. Replace the previous title when it is generic, artifact-based, a completion update, or contradicted by the thread.", | |
| 254 | − "4. Title the durable subject and desired outcome, not the current workflow state.", | |
| 255 | − "", | |
| 256 | − ] | |
| 257 | − : [ | |
| 258 | − "", | |
| 259 | − "Before answering, silently reduce the request to:", | |
| 260 | − "- Subject: What system, feature, or problem is this really about?", | |
| 261 | − "- Outcome: What does the user ultimately want to understand or change?", | |
| 262 | − "- Incidental instructions: What only describes how the agent should do the work?", | |
| 263 | − "", | |
| 264 | − "Title the subject and outcome. Discard incidental instructions.", | |
| 265 | − "", | |
| 266 | − ], | |
| 267 | − rulesLabel: "Editorial rules:", | |
| 268 | − rules: isRegeneration | |
| 269 | − ? [ | |
| 270 | − "3-8 words, fewer than 40 characters.", | |
| 271 | − "Use a compact noun phrase or clear action phrase.", | |
| 272 | − "Preserve the umbrella subject when later messages focus on one finding, provider, platform, or implementation detail.", | |
| 273 | − "A thread progressing through research, planning, implementation, review, CI, merge, and monitoring has usually not changed subjects.", | |
| 274 | − "Ignore deliverables and operations such as mocks, plans, HTML, branches, PRs, tests, CI, commits, merging, and monitoring unless they are the actual topic.", | |
| 275 | − "Models, subagents, tools, output formats, and monitoring instructions do not belong in the title unless they are themselves the topic.", | |
| 276 | − "Treat final operational follow-ups and assistant completion summaries as weak evidence of subject.", | |
| 277 | − "For reviews, name the reviewed feature or system and its durable concern, not one finding from the review.", | |
| 278 | − "For research, name the question domain rather than the research process.", | |
| 279 | − "Do not claim the work is complete.", | |
| 280 | − "Do not copy and truncate a thread message.", | |
| 281 | − "Avoid project names already visible in the UI, PR numbers, quotes, labels, filler, and trailing punctuation.", | |
| 282 | − "Use attached images as primary context for UI issues.", | |
| 283 | − "When a URL or attachment is the only source of the subject, use available tools to inspect it. If it cannot be resolved, remain accurate rather than guessing.", | |
| 284 | − "Return a meaningfully improved title, not a cosmetic paraphrase of the previous title.", | |
| 285 | − ] | |
| 286 | − : [ | |
| 287 | − "3-8 words, fewer than 40 characters.", | |
| 288 | − "Use a compact noun phrase or clear action phrase.", | |
| 289 | − "Capture the umbrella goal when the request lists several symptoms or steps.", | |
| 290 | − "Name the product change, not the mock, plan, report, branch, or PR used to produce it.", | |
| 291 | − "Models, subagents, tools, output formats, and monitoring instructions do not belong in the title unless they are themselves the topic.", | |
| 292 | − 'For reviews, name what is being reviewed and the relevant concern. Avoid generic titles such as "Review PR 123" when linked or attached context reveals the subject.', | |
| 293 | − "For research, name the question domain rather than the requested research process.", | |
| 294 | − "Do not claim the work is complete.", | |
| 295 | − "Do not copy and truncate the user's message.", | |
| 296 | − "Avoid project names already visible in the UI, quotes, labels, filler, and trailing punctuation.", | |
| 297 | − "Use attached images as primary context for UI issues.", | |
| 298 | − "When a URL or attachment is the only source of the subject, use available tools to inspect it. If it cannot be resolved, remain accurate rather than guessing.", | |
| 299 | − ], | |
| 300 | − afterRules: isRegeneration | |
| 301 | − ? [ | |
| 302 | − "", | |
| 303 | − "Examples of the distinction:", | |
| 304 | − '- A subagent-monitoring review that finds a Codex roster bug remains "Review Subagent Monitoring Risks," not "Codex Roster Bug Review."', | |
| 305 | − '- A vague failing-test request later identified as a lazy thread-feed mismatch becomes "Fix Lazy Thread Feed Test," not "Prevent Mobile Feed Regressions."', | |
| 306 | − "- A QR-sharing overhaul that ends with CI and merge work remains about QR sharing, not the PR lifecycle.", | |
| 307 | − ] | |
| 308 | − : undefined, | |
| 309 | − message: input.message, | |
| 310 | − ...(isRegeneration | |
| 311 | − ? { | |
| 312 | − messageLabel: "Thread contents", | |
| 313 | − preserveMessageEnd: true, | |
| 314 | − } | |
| 315 | − : {}), | |
| 316 | − attachments: input.attachments, | |
| 317 | − additionalInstructions: input.policy?.threadTitleInstructions, | |
| 318 | − }); | |
| 305 | + let prompt: string; | |
| 306 | + if (input.previousTitle === undefined) { | |
| 307 | + const message = limitSection(input.message, 8_000); | |
| 308 | + prompt = `${INITIAL_THREAD_TITLE_PROMPT}\n\nUser message:\n${message}${threadTitlePromptSuffix(input)}`; | |
| 309 | + } else { | |
| 310 | + const message = preserveMessageEnd(input.message); | |
| 311 | + prompt = `${regenerateThreadTitlePrompt(input.previousTitle)}\n\nThread contents:\n${message}${threadTitlePromptSuffix(input)}`; | |
| 312 | + } | |
| 319 | 313 | const outputSchema = Schema.Struct({ |
| 320 | 314 | title: Schema.String, |
| 321 | 315 | }); |