source dump of claude code
at main 100 lines 3.3 kB view raw
1import type { ContentBlockParam } from '@anthropic-ai/sdk/resources' 2import { randomUUID } from 'crypto' 3import { setPromptId } from 'src/bootstrap/state.js' 4import type { 5 AttachmentMessage, 6 SystemMessage, 7 UserMessage, 8} from 'src/types/message.js' 9import { logEvent } from '../../services/analytics/index.js' 10import type { PermissionMode } from '../../types/permissions.js' 11import { createUserMessage } from '../messages.js' 12import { logOTelEvent, redactIfDisabled } from '../telemetry/events.js' 13import { startInteractionSpan } from '../telemetry/sessionTracing.js' 14import { 15 matchesKeepGoingKeyword, 16 matchesNegativeKeyword, 17} from '../userPromptKeywords.js' 18 19export function processTextPrompt( 20 input: string | Array<ContentBlockParam>, 21 imageContentBlocks: ContentBlockParam[], 22 imagePasteIds: number[], 23 attachmentMessages: AttachmentMessage[], 24 uuid?: string, 25 permissionMode?: PermissionMode, 26 isMeta?: boolean, 27): { 28 messages: (UserMessage | AttachmentMessage | SystemMessage)[] 29 shouldQuery: boolean 30} { 31 const promptId = randomUUID() 32 setPromptId(promptId) 33 34 const userPromptText = 35 typeof input === 'string' 36 ? input 37 : input.find(block => block.type === 'text')?.text || '' 38 startInteractionSpan(userPromptText) 39 40 // Emit user_prompt OTEL event for both string (CLI) and array (SDK/VS Code) 41 // input shapes. Previously gated on `typeof input === 'string'`, so VS Code 42 // sessions never emitted user_prompt (anthropics/claude-code#33301). 43 // For array input, use the LAST text block: createUserContent pushes the 44 // user's message last (after any <ide_selection>/attachment context blocks), 45 // so .findLast gets the actual prompt. userPromptText (first block) is kept 46 // unchanged for startInteractionSpan to preserve existing span attributes. 47 const otelPromptText = 48 typeof input === 'string' 49 ? input 50 : input.findLast(block => block.type === 'text')?.text || '' 51 if (otelPromptText) { 52 void logOTelEvent('user_prompt', { 53 prompt_length: String(otelPromptText.length), 54 prompt: redactIfDisabled(otelPromptText), 55 'prompt.id': promptId, 56 }) 57 } 58 59 const isNegative = matchesNegativeKeyword(userPromptText) 60 const isKeepGoing = matchesKeepGoingKeyword(userPromptText) 61 logEvent('tengu_input_prompt', { 62 is_negative: isNegative, 63 is_keep_going: isKeepGoing, 64 }) 65 66 // If we have pasted images, create a message with image content 67 if (imageContentBlocks.length > 0) { 68 // Build content: text first, then images below 69 const textContent = 70 typeof input === 'string' 71 ? input.trim() 72 ? [{ type: 'text' as const, text: input }] 73 : [] 74 : input 75 const userMessage = createUserMessage({ 76 content: [...textContent, ...imageContentBlocks], 77 uuid: uuid, 78 imagePasteIds: imagePasteIds.length > 0 ? imagePasteIds : undefined, 79 permissionMode, 80 isMeta: isMeta || undefined, 81 }) 82 83 return { 84 messages: [userMessage, ...attachmentMessages], 85 shouldQuery: true, 86 } 87 } 88 89 const userMessage = createUserMessage({ 90 content: input, 91 uuid, 92 permissionMode, 93 isMeta: isMeta || undefined, 94 }) 95 96 return { 97 messages: [userMessage, ...attachmentMessages], 98 shouldQuery: true, 99 } 100}